03按键

一、IO口输入内容

在学习按键之前先学习一下如何往单片机的IO口输入内容。

其实输入的本质就是往单片机的一个端口在外部给一个电平,然后单片机中的程序去读取那个端口的电平即可完成一次输入。

51单片机的输入电平是非常简单的,不需要像stm32一样,需要调节端口的模式才能读取端口的电平,51单片机只需要读取端口的电平就可以了,非常的简单。

比如说现在我给我的单片机的P2组中第2个引脚一个电平,那么接收的代码如下:

int main(){
    int a = P2_2;
}

这样变量a中的值就是我给P2组中第二个引脚的电平值了。

二、什么是按键

按键在生活中很常见,一些嵌入式的设备都是通过按键来就行控制的(除了一些触摸显示屏),按键是使用是比较常用的。

在51单片机中通常使用的按键就这两种

第一种:独立按键,这种按键每一个是对应每一个引脚的,相当于一个按键控制一个引脚

img

img

第二种:矩阵按键,这种按键是一个矩阵,能控制的东西比较多,使用的引脚也比较多,如果每一个按键控制一个引脚,那么需要16个引脚,也就是需要2个引脚组,这样的方法很耗费引脚,那么这里使用到一种方法了。

img

就是将这些按键的一个引脚全部接在一起,另一个引脚按照行的方式了解在一起,就变成这种样子了

img

这样只需要一组IO口就可以得到16个按钮输入的内容。

三、按键分析

1.独立按键

从原理图可以知道

img

这些独立按键的一段引脚全部接到GND上的,而另一个接P3组中的0,1,2,3引脚。当点击任意按键,比如说K2按键,那么就会给P30这个端口一个低电平。

如果这些按键的一端全部接高电平,那么当点击任意按键,比如说K2按键,那么就会给P30一个高电平。

但是如果你是自己设置按键的话,让按键的一端全部接低电平,因为在使用单片机的时候,我们会给所有的IO组一个高电平,这个过程叫做充能,所以如果你接高电平的时候在判断的时候会产生误判。

现在简单写一个按键输入判断输入电平的一个程序

void main(){
    char a = P3_0;
    if (a == 0){
        ;//执行的内容
    }
}

如果用户按下了K2这个按键,那么P3_0的引脚应该会接收到一个低电平,然后再判断执行其它的内容。

2.矩阵按键

首先先看原理图

img

这个按键是一行连接到一个端口,列连接到一个端口,就比如第一行的左引脚全部都连接到P1_7这个引脚,第一列的右引脚都连接到P1_3这个引脚,这个是需要注意的。

而控制这个按键需要更复杂的方式,因为这个按键的一端并没有连接到电平,所以我们需要先让一端为低电平,另一端来接收用户按下按键时输入的电平,然后再发过来让之前接收的一端变成输出的,然后输出的变成输入,然后将两次按下的电平进行相加,这样就可以通过相加和判断按下的是哪一个按键了。这种方法叫做按键扫描。

不用担心上面的方法导致接收不了,因为内部运行的速度非常快,当我们做按键扫描的时候,可能第一个键还没有弹回到原来的地方就已经扫描完毕了。

那么代码可以这样写:

void main(){
    char row, col; //定义两个变量来接收端口输入
    P1 = 0x0F;
    col = P1;
    P1 = 0xF0;
    row = P1;
}

然后两值一相加判断一下结果为多少就可以知道按下的按键是哪一个了,每个按键所对应相加的值如下:

char btnNumber[] = {0x77, 0x7B, 0x7D, 0x7E, 0xB7, 0xBB, 0xBD, 0xBE, 0xD7, 0xDB, 0xDD, 0xDE, 0xE7, 0xEB, 0xED, 0xEE};

上面的按键是按照行来排列的,0x77, 0x7B, 0x7D, 0x7E是第一行中的第一个按键一直到第四个。剩下的以此类推。

在使用的时候可以添加一个for循环来判断:

void main(){
    char btnNumber[] = {0x77, 0x7B, 0x7D, 0x7E, 0xB7, 0xBB, 0xBD, 0xBE, 0xD7, 0xDB, 0xDD, 0xDE, 0xE7, 0xEB, 0xED, 0xEE};
    char row, col, i; //定义两个变量来接收端口输入
    P1 = 0x0F;
    col = P1;
    P1 = 0xF0;
    row = P1;
    for (i = 0; i < 16; i++){
        if (col + row == btnNumber[i]){
            ;
        }
    }
}

上面的方法是判断是不是按下了矩形按键中的一个内容,下面的方法是判断是不是按下了一个具体的按键:

void main(){
    char btnNumber[] = {0x77, 0x7B, 0x7D, 0x7E, 0xB7, 0xBB, 0xBD, 0xBE, 0xD7, 0xDB, 0xDD, 0xDE, 0xE7, 0xEB, 0xED, 0xEE};
    char row, col, i; //定义两个变量来接收端口输入
    P1 = 0x0F;
    col = P1;
    P1 = 0xF0;
    row = P1;
    if (col + row == btnNumber[1]){
        ;
    }
    else if (col + row == btnNumber[2]){
        ;
    }
    else if (col + row == btnNumber[0]){
        ;
    }
}

3.抖动

抖动这个问题是在使用按键的时候经常会出现,因为按键中有一个金属弹片,当按下按键时会挤压金属弹片来导通电流,而松手时金属弹片会弹回去。

按下和松手都会让金属弹片有抖动,这个抖动的电流是不稳定的,会影响程序对按键的判断

img

如上图就可以知道按键的抖动,那我们如何消除这个按键的抖动呢?

第一种方法直接从硬件方面下手,使用另一种没用抖动或者抖动频率小的硬件。

第二种方法是使用程序来超过这个抖动,也就是用一个延迟函数来将这个按下的抖动时间消除。

这里我们选择第二种方法,毕竟咱使用的已经是封装好的板子,如果自己搭建的话可以使用第一种方法,代码如下:

void delay(unsigned int time){
    unsigned char i, j;
    while(time--){
        i = 15;
        j = 90;
        do
        {
            while (--j);
        } while (--i);
    }
}
void main(){
    delay(10);
    char a = P3_0;
    if (a == 0){
        ;//执行的内容
    }
}

延迟10毫秒后得到的就是一个稳定的电平了。这个抖动时间一般都是5毫秒到10毫秒的样子,可能有些按键的抖动时间更多,那么就得查看手册了决定了。

四、按键案例

1.按下按键点亮一个LED灯

#include <at89x51.h>
void delay(unsigned int time){
    unsigned char i, j;
    while(time--){
        i = 15;
        j = 90;
        do
        {
            while (--j);
        } while (--i);
    }
}
void main(){
    char row;
    while(1){
        delay(15);
        row = P3_1;
        while (row == 0){
            P2_2 = 0;
        }
    }
}

但按下了K1这个按键后,就让P22口控制的LED灯点亮。

这里还可以扩展,就是当按下后LED亮,再按下后LED灭

#include <at89x51.h>
void delay(unsigned int time){
    unsigned char i, j;
    while(time--){
        i = 15;
        j = 90;
        do
        {
            while (--j);
        } while (--i);
    }
}
void main(){
    char row, x = 0;
    while(1){
        row = P3_1;
        delay(15);
        if (row == 0){
            P2_2 = ~P2_2;
            delay(15);
        }
    }
}

这里是将P22口进行取反操作然后再将这个取反后的值赋值给P22端口。

2.按键模拟二进制

这个其实也是比较简单,就是按下一个按键相当于+1,然后使用LED灯进行显示值。

#include <at89x51.h>
void delay(unsigned int time){
    unsigned char i, j;
    while(time--){
        i = 15;
        j = 90;
        do
        {
            while (--j);
        } while (--i);
    }
}
void main(){
    char row, x = 0;
    while(1){
        row = P3_1;
        delay(15);
        if (row == 0){
            x++;
            delay(15);
        }
        P2 = ~x;
    }
}

可能会有同学提出一个问题,就是当这个x一直叠加到0xFF后需不需要进行判断来为x清0?

其实不需要的,因为我的x是char类型只有8个字节,也就是从0x00一直到0xFF,如果到达了0xFF后再进行只增一那么就会得到0x100,但是这个1已经超过了范围了,所以就会被截断变成0x00。

3.矩形按键控制LED灯的亮灭

这里使用矩形按键来进行控制

#include <at89x51.h>
char btnNumber[] = {0x77, 0x7B, 0x7D, 0x7E, 0xB7, 0xBB, 0xBD, 0xBE, 0xD7, 0xDB, 0xDD, 0xDE, 0xE7, 0xEB, 0xED, 0xEE};
void delay(unsigned int time){
    unsigned char i, j;
    while(time--){
        i = 15;
        j = 90;
        do
        {
            while (--j);
        } while (--i);
    }
}
void main(){
    char row, col, x = 0;
    while(1){
        P1 = 0x0F;
        col = P1;
        P1 = 0xF0;
        row = P1;
        delay(10);
        if (col + row == btnNumber[0]){
            P2_0 = ~P2_0;
            delay(10);
        }
        else if (col + row == btnNumber[1]){
            P2_1 = ~P2_1;
            delay(10);
        }
    }
}

总结

按键的使用地方还是比较多的,一般是用于用户对单片机的控制。

这里要注意,在使用按键的时候如果你是使用的是模拟器,那么就可以不用考虑消抖,如果你是使用硬件环境,那么就需要考虑一下消抖问题。

posted @ 2023-02-03 23:02  Lavender·edgar  阅读(146)  评论(0编辑  收藏  举报