初学单片机时,讲到了一个按键“消抖”概念,视屏教程中只是说到要确定按键是不是真正按下,所以需要加一个延时来判断。
附上延时消抖程序代码:
代码1
void keypros()
{
if(k1==0) //检测按键K1是否按下
{
delay(1000); //消除抖动 一般大约10ms
if(k1==0) //再次判断按键是否按下
{
//此处添加相应操作
}
while(!k1); //检测按键是否松开
}
}
前不久,又了解到另外两个按键消抖的代码
状态机消抖:
代码2
#define key_input P3
#define key_state_0 0 //判断是否按下
#define key_state_1 1 //判断是否为抖动
#define key_state_2 2 //判断是否弹起
unsigned char t0count;
char key_value;
char read_key(void)
{
static char key_state = 0;
char key_press, key_return = 0;
key_press = key_input&key_mask;
switch (key_state)
{
case key_state_0:
if (key_press!=key_mask) key_state = key_state_1;
break;
case key_state_1:
if (key_press!=key_input&key_mask)
{
if(key_press==0x0e) key_return = 1; //S7
if(key_press==0x0d) key_return = 2; //S6
if(key_press==0x0b) key_return = 3; //S5
if(key_press==0x07) key_return = 4; //S4
key_state = key_state_2;
}
else
key_state = key_state_0;
break;
case key_state_2:
if (key_press==0x0f) key_state = key_state_0;
break;
}
return key_return;
}
void tm0_isr() interrupt 1
{
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
t0count++;
if(t0count>=2)
{
t0count=0;
key_value=read_key();
}
}
...
if(key_value==...)
{
//对于操作
}
三行代码消抖:
代码2
#define KEYPORT P3
typedef unsigned char u8;
u8 Trg;
u8 Cont;
u8 t0count;
void Read_key()
{
u8 readdata=KEYPORT^0xff; //核心代码1
Trg=readdata&(readdata^Cont); //核心代码2
Cont=readdata; //核心代码3
}
void tm0_isr() interrupt 1
{
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
t0count++;
if(t0count>=10)
{
t0count=0;
Read_key();
}
}
...
if(Trg==...) //短按
{
//对应操作
}
if(Cont==..) //长按
{
//对应操作
}
三种方法为什么都可以“消抖”?
这一问题引起了我的思考。
于是我查询了什么是按键抖动:按键抖动通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。
如下图:
由于单片机的运行速度非常快,按下一次按键,可能在A点检测到一次低电平,在B点检测到一次高电平,在C点又检测到一次低电平。同时抖动是随机,不可测的。那么按下一次按键,抖动可能对会让单片机误以为按下多次按键。
延时消抖
最简单的消抖原理,就是当检测到按键状态变化后,先等待一个 10ms 左右的延时时间,让抖动消失后再进行一次按键状态检测,如果与刚才检测到的状态相同,就可以确认按键已经稳定的动作了。
状态机消抖
与延时消抖大同小异,间隔10ms检测一次按键电位变化,状态0时检测到有按键电位变化就进入状态1,状态1再来确认是否还有变化,有则是按下按键。但是由于是利用了定时器,所以在间隔等待10ms时单片机仍可以进行其他进程。
三行代码消抖
真的佩服。
说起来与状态机消抖原理差不多也是利用定时器间隔10ms检测一次按键。但是构思非常巧妙。
当没有按键按下时,Trg与Cont均为0x00。
当单片机执行到此程序且按键按下时(假设P3^0按下),Trg与Cont均为0x01。
当单片机再次执行到此程序且按键按下时(假设P3^0按下),Trg为0x00,Cont为0x01。
而当按键释放,再次检测时,Trg与Cont又均为0x00。(恢复到没有按键按下状态)