单片机按键消抖措施

 新闻资讯     |      2021-03-02 11:55

凡是按键所用的开关都是机器弹性开关,当机器触点断开、闭适时,由于机器触点的弹性浸染,一个按键开关在闭适时不会顿时就不变的接通,在断开时也不会一下子彻底断开,而是在闭合和断开的瞬间陪伴了陆续串的发抖,如图 8-10 所示。

图 8-10  按键发抖状态图


图 8-10  按键发抖状态图


按键不变闭适时间是非是由操纵人员抉择的,凡是城市在 100ms 以上,决心快速按的话能到达 40-50ms 阁下,很难再低了。发抖时间是由按键的机器特性抉择的,一般城市在 10ms以内,为了确保措施对按键的一次闭合可能一次断开只响应一次,必需举办按键的消抖处理惩罚。当检测到按键状态变革时,不是当即去响应行动,而是先期待闭合或断开不变后再举办处理惩罚。按键消抖可分为硬件消抖和软件消抖。

硬件消抖就是在按键上并联一个电容,如图 8-11 所示,操作电容的充放电特性来对发抖进程中发生的电压毛刺举办滑腻处理惩罚,从而实现消抖。但实际应用中,这种方法的结果往往不是很好,并且还增加了本钱和电路巨大度,所以实际中利用的并不多。

图 8-11  硬件电容消抖


图 8-11  硬件电容消抖


在绝大大都环境下,我们是用软件即措施来实现消抖的。最简朴的消抖道理,就是当检测到按键状态变革后,先期待一个 10ms 阁下的延时时间,让发抖消失后再举办一次按键状态检测,假如与适才检测到的状态沟通,就可以确认按键已经不变的行动了。将上一个的措施稍加窜改,获得新的带消抖成果的措施如下。 #include <reg52.h> sbit ADDR0 = P1^0; sbit ADDR1 = P1^1; sbit ADDR2 = P1^2; sbit ADDR3 = P1^3; sbit ENLED = P1^4; sbit KEY1 = P2^4; sbit KEY2 = P2^5; sbit KEY3 = P2^6; sbit KEY4 = P2^7; unsigned char code LedChar[] = { //数码管显示字符转换表 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E }; void delay(); void main(){ bit keybuf = 1; //按键值暂存,姑且生存按键的扫描值 bit backup = 1; //按键值备份,生存前一次的扫描值 unsigned char cnt = 0; //按键计数,记录按键按下的次数 ENLED = 0; //选择数码管 DS1 举办显示 ADDR3 = 1; ADDR2 = 0; ADDR1 = 0; ADDR0 = 0; P2 = 0xF7; //P2.3 置 0,即 KeyOut1 输出低电平 P0 = LedChar[cnt]; //显示按键次数初值 while (1){ keybuf = KEY4; //把当前扫描值暂存 if (keybuf != backup){ //当前值与前次值不相等说明此时按键有行动 delay(); //延时约莫 10ms if (keybuf == KEY4){ //判定扫描值有没有产生改变,即按键发抖 if (backup == 0){ //假如前次值为 0,则说明当前是弹起行动 cnt++; //按键次数+1 //只用 1 个数码管显示,所以加到 10 就清零从头开始 if (cnt >= 10){ cnt = 0; } P0 = LedChar[cnt]; //计数值显示到数码管上 } backup = keybuf; //更新备份为当前值,以备举办下次较量 } } } } /* 软件延时函数,延时约 10ms */ void delay(){ unsigned int i = 1000; while (i--); } 各人把这个措施下载到板子上再举办试验试试,按一下按键而数字加了多次的问题是不是就这样办理了?把问题办理掉的感受是不是很爽呢?

这个措施用了一个简朴的算法实现了按键的消抖。作为这种很简朴的演示措施,我们可以这样来写,可是实际做项目开拓的时候,措施量往往很大,各类状态值也许多, while(1)这个主轮回要不断的扫描各类状态值是否有产生变革,实时的举办任务调治,假如措施中间加了这种 delay 延时操纵后,很大概某一事件产生了,可是我们措施还在举办 delay 延时操纵中,当这个事件产生完了,措施还在 delay 操纵中,当我们 delay 完事再去查抄的时候,已经晚了,冰球突破,已经检测不到谁人事件了。为了制止这种环境的产生,我们要只管缩短 while(1)轮回一次所用的时间,而需要举办长时间延时的操纵,必需想其它的步伐来处理惩罚。

那么消抖操纵所需要的延时该怎么处理惩罚呢?其实除了这种简朴的延时,我们尚有更优异的要领来处理惩罚按键发抖问题。举个例子:我们启用一个按时间断,每 2ms 进一次间断,扫描一次按键状态而且存储起来,持续扫描 8 次后,看看这持续 8 次的按键状态是否是一致的。8 次按键的时间或许是 16ms,这 16ms 内假如按键状态一直保持一致,那就可以确定此刻按键处于不变的阶段,而非处于发抖的阶段,如图 8-12。


图 8-12  按键持续扫描判定


如果左边时间是起始 0 时刻,每颠末 2ms 左移一次,每移动一次,判定当前持续的 8 次按键状态是不是全 1 可能全 0,假如是全 1 则鉴定为弹起,假如是全 0 则鉴定为按下,假如0 和 1 交织,就认为是发抖,不做任何鉴定。想一下,这样是不是比简朴的延时越发靠得住?

操作这种要领,就可以制止通过延时消抖占用单片机执行时间,而是转化成了一种按键状态鉴定而非按键进程鉴定,我们只对当前按键的持续 16ms 的 8 次状态举办判定,而不再体贴它在这 16ms 内都做了什么工作,那么下面就凭据这种思路用措施实现出来,同样只以K4 为例。 #include <reg52.h> sbit ADDR0 = P1^0; sbit ADDR1 = P1^1; sbit ADDR2 = P1^2; sbit ADDR3 = P1^3; sbit ENLED = P1^4; sbit KEY1 = P2^4; sbit KEY2 = P2^5; sbit KEY3 = P2^6; sbit KEY4 = P2^7; unsigned char code LedChar[] = { //数码管显示字符转换表 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E }; bit KeySta = 1; //当前按键状态 void main(){ bit backup = 1; //按键值备份,生存前一次的扫描值 unsigned char cnt = 0; //按键计数,记录按键按下的次数 EA = 1; //使能总间断 ENLED = 0; //选择数码管 DS1 举办显示 ADDR3 = 1; ADDR2 = 0; ADDR1 = 0; ADDR0 = 0; TMOD = 0x01; //配置 T0 为模式 1 TH0 = 0xF8; //为 T0 赋初值 0xF8CD,按时 2ms TL0 = 0xCD; ET0 = 1; //使能 T0 间断 TR0 = 1; //启动 T0 P2 = 0xF7; //P2.3 置 0,即 KeyOut1 输出低电平 P0 = LedChar[cnt]; //显示按键次数初值 while (1){ if (KeySta != backup){ //当前值与前次值不相等说明此时按键有行动 if (backup == 0){ //假如前次值为 0,则说明当前是弹起行动 cnt++; //按键次数+1 if (cnt >= 10){ //只用 1 个数码管显示,所以加到 10 就清零从头开始 cnt = 0; } P0 = LedChar[cnt]; //计数值显示到数码管上 } //更新备份为当前值,以备举办下次较量 backup = KeySta; } } } /* T0 间断处事函数,用于按键状态的扫描并消抖 */ void InterruptTimer0() interrupt 1{ //扫描缓冲区,生存一段时间内的扫描值 static unsigned char keybuf = 0xFF; TH0 = 0xF8; //从头加载初值 TL0 = 0xCD; //缓冲区左移一位,并将当前扫描值移入最低位 keybuf = (keybuf<<1) | KEY4; //持续 8 次扫描值都为 0,即 16ms 内都只检测到按下状态时,可认为按键已按下 if (keybuf == 0x00){ KeySta = 0; //持续 8 次扫描值都为 1,即 16ms 内都只检测到弹起状态时,可认为按键已弹起 }else if (keybuf == 0xFF){ KeySta = 1; } else{ //其它环境则说明按键状态尚未不变,则差池 KeySta 变量值举办更新 } } 这个算法是我们在实际工程中常常利用按键所总结的一个较量好的要领,先容给各人,此后都可以用这种要领消抖了。虽然,按键消抖也尚有其它的要领,措施实现更是多种多样,各人也可以再多思量下其它的算法,拓展下思路。