一、按键介绍
轻触按键:相当于是一种机械开关,按下时开关接通,松开时开关断开,实现原理是通过轻触按键内部的金属弹片受力弹动来实现接通和断开
轻触按键的内部结构介绍
我们可以很清楚地看到下图的按键内部是由两个触点和一个金属弹片组成的,两个触点与按键左端相连,中间的金属弹片与按键右端相连。当我们按下按键的时候按键内部的金属弹片就会被压扁,与两左端两触点接触,这样就实现了开关的接通。当我们松手的时候,金属弹片恢复原状,不再与两触点接触,这样开关就断开了。
按键的抖动
机械开关触点的断开、闭合时由于弹性作用,不会立马稳定的断开与接通,而是会产生一个轻微的抖动
消除抖动
按键消抖有两种方式,一种是硬件消抖,另一种是软件消抖。为了使电路更加简单同时也节约成本,通常采用软件消抖。
根据按键抖动的波形图,我们只需要在按键按下的时候给程序一个延迟,延迟掉这段抖动的时间即可。
二、独立按键
独立按键在51单片机中的位置
独立按键原理图
根据独立按键原理图可知
P3_4口控制key17, P3_5口控制key18......以此类推。
三、矩阵按键
在某一个系统设计中,如果需要使用很多的按键时,做成独立按键会大量占用 IO 口,因此我们引入了矩阵按键的设计。
矩阵按键原理图
根据矩阵按键原理图可知两个io口一起控制一个矩阵按键,并且八个io口就控制了16个按键,大大节约了io口资源。
例如:P3_4和P3_1口控制key5
当key5按下时,P3^4和P3^1口会因为接地而被拉低成低电平
四、抬手检测
为了防止多次检测按键或区分是按下触发程序还是抬手触发程序,我们需设计一个检测抬手的程序。
我们设计一个while循环,当按键没松手时就会一直执行这个空循环
如按键io口为P3^7
例:while(P3^7==0);
但是这个检测方式有延迟
按键控制流水灯和跑马灯
视频效果
独立按键控制流水灯、跑马灯
代码
#include <REGX52.H>
unsigned char n,m,i=2;
sbit k1=P3^4;
sbit k2=P3^5;
void Delay1ms(unsigned char xms) //@12.000MHz 延时函数
{
unsigned char i, j;
i = 12;
j = 169;
while(xms--)
{
do
{
while (--j);
} while (--i);
}
}
void water_lamp(),race_lamp(); //声明water_lamp和race_lamp函数
void main()
{
n=0,m=0;
while(1)
{
if(i%2==0)
{
water_lamp();
}
else
{
race_lamp();
}
if(k1==0)
{
Delay1ms(5);
while(k1==0);
i++;
}
if(k2==0)
{
Delay1ms(5);
while(k2==0);
if(++n>13) n=0;
if(++m>13) m=0;
}
}
}
void water_lamp()
{
if(n==0) P1=0XFE;
if(n==1) P1=0XFC;
if(n==2) P1=0XF8;
if(n==3) P1=0XF0;
if(n==4) P1=0XE0;
if(n==5) P1=0XC0;
if(n==6) P1=0X80;
if(n==7) P1=0X7F;
if(n==8) P1=0X3F;
if(n==9) P1=0X1F;
if(n==10) P1=0X0F;
if(n==11) P1=0X07;
if(n==12) P1=0X03;
if(n==13) P1=0X01;
}
void race_lamp()
{
if(n==0) P1=0XFE;
if(n==1) P1=0XFD;
if(n==2) P1=0XFB;
if(n==3) P1=0XF7;
if(n==4) P1=0XEF;
if(n==5) P1=0XDF;
if(n==6) P1=0XBF;
if(n==7) P1=0X7F;
if(n==8) P1=0XBF;
if(n==9) P1=0XDF;
if(n==10) P1=0XEF;
if(n==11) P1=0XF7;
if(n==12) P1=0XFB;
if(n==13) P1=0XFD;
}
五、状态机思想的抬手检测
从上面的视频中,我们可以很明显看到按键有时候的反应不是那么灵敏,这就是利用while循环检测松手的bug,这个时候单片机的cpu一直卡在while循环里面,导致程序卡顿,这个时候我们可以利用另一种写法—状态机写法。
我们可以设置两种状态,0是按键按下状态,1是按键抬起状态。首先我们定义一个按键标志位key_flag,如果key_flag=0就是有按键按下的状态,key_flag=1就是按键抬起状态。同时当有不同按键按下的时候,则返回不同的值
代码
unsigned char key_flag;
if((P3_1==0 || P3_2==0 || P3_3==0) && key_flag==0)
{
if(P3_1==0) return 1; //如果按键1按下则返回1
if(P3_2==0) return2; //如果按键2按下则返回2
if(P3_3==0) return3; //如果按键3按下则返回3
key_flag=1; //检测位置1,防止多次检测
}
if((P3_1==1 && P3_2==1 && P3_3==1) && key_flag==1)
{
key_flag=0; //重新置标志位为0,方便进入下次按键检测
}
最后在主函数中接收返回值,判断到底是哪个按键按下。