按键的多种状态检测及消抖处理方法
2013-07-09陈晓丽
陈晓丽
(珠海格力电器股份有限公司 广东珠海 519070)
1 引言
在单片机控制系统中,按键是最常用的输入设备,是人-机接口中非常重要的组成部分,家用电器或其它消费性电子产品中就经常使用按键来实现功能控制或选择、配置信息参数等。随着产品功能越来越丰富,按键的作用越来越多样化,同一个按键被赋予了多种功能(即一键多功能),比如:短按一个按键是功能模式切换,而长按这一个按键又可以是关机的功能,这使得按键的检测和处理更加复杂,也更加重要。本文讲述了一种按键的多种状态判断方法和相应的按键消抖处理方法。
2 按键检测
按键都是以开关状态来输入数据的。根据硬件电路上对按键检测的不同来区分,可以将按键检测电路分为两种类型,一类是I/O扫描式按键电路,另一类是AD采样式按键电路。这两种电路在按键检测原理上存在区别,I/O扫描式按键检测是根据I/O的高低电平来判断是否有键按下,而AD采样式按键检测是根据I/O的AD采样值范围来判断是否有键按下。虽然两类按键的检测方法不同,但是按键的处理方法是一样的,包括状态判断处理和消抖处理。
3 按键的多种状态判断
3.1 按键的状态
为了实现一个按键有多种功能,可以根据操作按键的不同状态来进行处理,这就需要判断出按键的各种状态。按键的状态有多种类型,包括短按、长按、连按、短按释放、长按释放等。
3.2 按键的多种状态判断方法
这里介绍一种程序上判断按键多种状态的方法。
程序代码如下:
#define NO_KEY 0xFF //按键没有按下
#define TASK_KEY_LONG 0x20//按键长按
#define TASK_KEY_HOLD 0x40//按键连按
#define TASK_KEY_SHORT_UP 0x80//按键短按后释放
#define TASK_KEY_LONG_UP 0x60//按键长按后释放
#define KEY_SCAN_TIMES 3 //短按键的次数
#define KEY_LONG_TIMES 150 //长按键的次数
#define KEY_HOLD_TIMES 35 //连按键的次数
void KeyScan(void)
{
static unsigned char keyCounter; //用于区分短按、长按、连按等
static unsigned char keyValue; //按键稳定状态值
unsigned char keyTmp; //当前按键状态
unsigned char key_return = NO_KEY;
static bool long_key; //按键长按标志
keyTmp = KeyDetect(); //获取当前的按键
状态,keyTmp的每一位对应一个按键状态,0表
示按键按下
if (keyTmp == NO_KEY) //释放按键或者
按键没有按下
{
if (long_key == 1)
{
key_return = keyValue | TASK_KEY_
LONG_UP; //长按后释放按键
}
else if ((keyCounter < KEY_LONG_
TIMES ) && (keyCounter > KEY_SCAN_
TIMES))
{
key_return = keyValue | TASK_KEY_
SHORT_UP; //短按后释放按键
}
keyValue = NO_KEY;
long_key = 0;
keyCounter = 0;
}
else if ((keyTmp != keyValue))
{
keyValue = keyTmp;
keyCounter = 0;
long_key = 0;
}
else
{
keyCounter++;
if (keyCounter == KEY_SCAN_TIMES)
{
key_return = keyValue; //短按
}
else if (keyCounter == KEY_LONG_
TIMES )
{
if (long_key == FALSE)
{
long_key = TRUE;
key_return = keyValue | TASK_KEY_LONG; //长按
}
}
else if (keyCounter == (KEY_LONG_TIMES + KEY_HOLD_TIMES) )
{
key_return = keyValue | TASK_KEY_HOLD; //连按
keyCounter = KEY_LONG_TIMES;
}
}
if ( key_return != NO_KEY)
{
PutMsg(key_return); //产生有按键的消息
}
}
函数KeyScan()的原理:在定时器的5ms中断处理中调用这个函数,通过按键扫描函数KeyDetect()获取按键状态keyTmp,它的每一位对应一个按键状态,0表示按键按下,1表示按键没有按下。当keyTmp为0xFF时,表示没有任何按键按下;keyCounter用于累计按键按下的时间,短按时间为3×5ms=15ms,也就是说当检测按键按下的时间超过15ms,就会发出一个短按的消息。同理,长按时间为150×5ms=750ms,连按消息是在长按后35×5=175ms后发出的,如果按键一直按着,就会每过175ms发出一个连按消息了;而短按释放的消息就要求按键短按和按键释放两个条件都先后满足,长按释放的消息也如此。当有按键按下时,这个函数会产生一个按键消息key_return,它是按键类型keyValue同按键状态(包括短按、长按、连按、短按释放、长按释放)进行或运算的结果。这样处理的优点:不管有多少个按键,每个按键都可以产生出这5类状态的消息,按键数量有增加或删减时,都不用更改代码,方便维护。
4 按键消抖处理
4.1 按键抖动
常用的按键为机械弹性按键,也称轻触开关。机械按键由于触点的弹性及电压突跳等原因,在触点闭合和断开的瞬间会出现抖动。一个按键在闭合时不会马上稳定地接通,同样,在断开时也不会一下子断开,在闭合和断开的瞬间均伴随有一连串的抖动,如图1所示。
抖动时间的长短由按键的机械特性来决定,一般为5~10ms。这种抖动对人来说是感觉不到的,但对单片机来说,则是完全可以感知的,因为单片机的处理速度在微秒级。按键稳定闭合时间的长短则由操作人员的按键动作决定的,一般为几百个毫秒至数秒。如果不对按键消除抖动则会引起程序处理的误操作。为了避免这种抖动现象,需进行按键消抖处理。
4.2 按键消抖处理方法
按键消抖有“硬件消抖”和“软件消抖”两种方式。简单的硬件消抖是在按键接入单片机的引脚位置并入一个小电容,利用电容的充放电原理来实现消除因抖动所产生的毛刺。简单的软件消抖就是用加固定软件延时的方法来去除抖动,具体方法就是在有按键按下时,利用软件延时一段时间后再次检测按键是否按下,实际上是避开了按键按下时的抖动时间。
这种硬件上并电容或软件上延时的处理方式比较简单,而且确实也起到了一定的消抖作用。但是在实际的生产使用过程中,按键千差万别,不同厂家的物料也参差不齐,各种按键操作时产生的抖动时间不会是一致的。这样就会出现一些问题了,电容过小或延时过短,可能会有按键误动作的问题;而电容过大或延时过长,会有按键操作后反应迟钝的问题。下面介绍一种新的消抖处理方法,抖动时间的长或短,对按键的操作效果没有任何影响。
程序代码如下:
#define KEY_SHAKE_TIMES 8 //按键防抖的扫描次数
static unsigned char Count; //用于累计按键状态稳定的次数
static unsigned char keyStatus; //前次按键状态
if(keyTmp != keyStatus) //当前按键状态是否与前次按键状态相同
{
keyStatus = keyTmp;
Count = 0;
return;
}
else
{
if (Count < 8)
{
Count++;
return;
}
}
只要在前面介绍的函数KeyScan()中获取keyTmp值后增加这段代码就可以了。它的原理:每次扫描中都将当前按键状态keyTmp与前次按键状态keyStatus进行比较,如果比较的两次状态不同,表示按键处于抖动中,会将用于累计按键状态稳定次数的变量Count清零,只有当连续8次比较的状态都相同时,才认为状态稳定了,即没有抖动了。这包括了按键按下时的抖动处理和按键释放的抖动处理。其中Count设为8次,每5ms调用一次,这就是40ms,它的设定,与抖动时间长短无关,只要大于抖动周期就可以了。这种处理方法的优点在于:它与抖动过程的时间长短无关,而且处理代码简单,不用区分按下抖动或释放抖动。
5 总结
本文介绍的这种单片机按键状态检测和消抖处理方法,与一般的按键检测方法相比,能检测更多类型的的按键状态,按键数量有增加或删减时,也不用更改代码,适用范围广,且代码简单,方便维护。采用的按键消抖方法也有很大的改进,与抖动过程的时间长短无关,不用区分按下抖动或释放抖动。
[1]谭浩强 C程序设计 清华大学出版社
[2]刘天时,刘赏,付春 一种单片机键盘电路设计与消抖处理 计算机与网络2012年 第10期
[3]李运兵 微控制器中按键处理技巧及应用 计算机应用系统 2010年第2期