上篇文章,介绍了将按键检测增加长按功能,并将按下抖动与松开抖动共用一个抖动状态来表示,其状态图如下:
仔细研究这个状态图,其它还存在一些问题:
短按状态,只要按下去,不需要等按键再释放,就会触发短按事件。对于需要按下再松开作为一次短按的应用来说,此状态图也不满足需求
长按状态,必须先经过短按状态,即长按按键,会先触发一个短按,再触发一个长按。如果实际应用中需要分别使用短按和长按,则此状态图不满足要求
本篇,就来解决上述两个问题,并再增加一个按键双击检测,实现一个功能更全面的按键检测。
1 增加双击检测 增加一个双击检测,需要增加两个状态:
等待再次按下
确认第2次按下
同时,之前的“短按状态”和“长按状态”分别改为“确认按下”和“确认长按”。
1.1 状态图修改 修改后的状态图如下,有以下几点需要注意:
“确认按下”不是短按触发的条件,需要等松开后,经消抖进入到“等待再次按下”一段时间后(200ms),没有再次被按下,才触发短按事件,这样就解决了本篇开头提到的第1个问题
“确认按下”不是短按触发的条件,另一个用途是,当此状态继续保持按下状态一段时间后(1s),则会单独触发长按事件,同时进入到“确认长按”状态,这样就解决了本篇开头提到的第2个问题
对于双击事件的检测,首先按下按键进入“确认按下”状态,然后在1s内松开进入“等待再次按下”状态,接着在200ms内再次按下进入“确认第2次按下”状态,然后在1s内松开,即可触发双击事件,并同时进入“稳定松开”状态
注意,在“确认第2次按下”状态下,如果在1s内没有松开,也会进入到“确认长按”状态
1.2 程序编写 根据状态图,修改对应的状态机逻辑,修改后的代码如下:
void key_status_check(){ switch(g_keystatus) { //按键释放(初始状态) case ks_release: { //检测到低电平,先进行消抖 if (key0 == 0) { g_keystatus = ks_shake; } } break; //抖动 case ks_shake: { if (key0 == 1) { //从松开状态来的抖动 if (ks_release == g_lastkeystatus) { g_keystatus = ks_release; } //从等待再次按下状态来的抖动 else if (ks_wait_press_again == g_lastkeystatus) { g_keystatus = ks_wait_press_again; } //从确认按下状态来 else if (ks_affirm_short_press == g_lastkeystatus) { g_waitpressagaincnt = 0; g_keystatus = ks_wait_press_again; } //从确认再次按下状态来 else if (ks_affirm_press_again == g_lastkeystatus) { printf(=====> key double press\r\n); g_keystatus = ks_release; } //从确认长按状态来 else if (ks_affirm_long_press == g_lastkeystatus) { g_keystatus = ks_release; } else { printf(err!\r\n); } } else { //从确认按下状态来的抖动 if (ks_affirm_short_press == g_lastkeystatus) { g_keystatus = ks_affirm_short_press; } //从第2次按下状态来的抖动 else if (ks_affirm_press_again == g_lastkeystatus) { g_keystatus = ks_affirm_press_again; } //从确认长按状态来的抖动 else if (ks_affirm_long_press == g_lastkeystatus) { g_keystatus = ks_affirm_long_press; } //从松开状态而来 else if (ks_release == g_lastkeystatus) { g_presstimecnt = 0; g_keystatus = ks_affirm_short_press; //printf(=====> key short press\r\n); } //从等待再次看下(的松开)状态而来 else if (ks_wait_press_again == g_lastkeystatus) { g_press2timecnt = 0; g_keystatus = ks_affirm_press_again; } else { printf(err!\r\n); } } } break; //确认按下 case ks_affirm_short_press: { //检测到高电平,先进行消抖 if (key0 == 1) { g_keystatus = ks_shake; } else { if (g_longpresstimecnt % 20 == 0) //每隔1000ms打印一次 { printf(=====> key long press:%d\r\n, g_longpresstimecnt/20); keyevent = ke_long_press; } g_longpresstimecnt++; } } break; //等待再次按下 case ks_wait_press_again: { //检测到低电平,先进行消抖 if (key0 == 0) { g_keystatus = ks_shake; } g_waitpressagaincnt++; if (g_waitpressagaincnt == 4) //200ms没有再次按下 { printf(=====> key single press\r\n); g_keystatus = ks_release; } } break; //确认第2次按下 case ks_affirm_press_again: { //检测到高电平,先进行消抖 if (key0 == 1) { g_keystatus = ks_shake; } g_press2timecnt++; if (g_press2timecnt == 20) //1000ms { g_longpresstimecnt = 0; g_keystatus = ks_affirm_long_press; } } break; //确认长按 case ks_affirm_long_press: { //检测到高电平,先进行消抖 if (key0 == 1) { g_keystatus = ks_shake; } g_longpresstimecnt++; if (g_longpresstimecnt % 20 == 0) //每隔1000ms打印一次 { printf(=====> key long press:%d\r\n, g_longpresstimecnt/20); } } break; default:break; } if (g_keystatus != g_nowkeystatus) { g_lastkeystatus = g_nowkeystatus; g_nowkeystatus = g_keystatus; //printf(new key status:%d(%s)\r\n, g_keystatus, key_status_name[g_keystatus]); }} 最后注释掉的一句是调试打印,调试时可打开,方便观察状态变化
1.3 测试 短按、长按、双击的测试结果如下:
还有从确认第2次按下状态到达的长按状态:
2 功能优化 上面的代码实现,是在主函数中,每50ms延时执行一次状态机循环(主函数代码如下),仅用做演示按键状态机的运行机制。
int main(void){ delay_init(); key_init(); uart_init(115200); printf(hello\r\n); while(1) { key_status_check(); delay_ms(50); }} 实际开发中,按键检测程序,应该作为一个独立的模块运行,当检测到某一按键状态触发时,通知应用程序来使用。
对于stm32裸机开发来说,可以将按键状态机放到一个定时器中断服务函数中运行,当检测到某一按键状态触发后,通知应用程序:
//主函数int main(void){ delay_init(); key_init(); uart_init(115200); tim3_int_init(500-1,7200-1); //调用定时器使得50ms产生一个中断 printf(hello\r\n); while(1) { }}//定时器3中断服务程序void tim3_irqhandler(void) //tim3中断{ if (tim_getitstatus(tim3, tim_it_update) != reset) //检查tim3更新中断发生与否 { tim_clearitpendingbit(tim3, tim_it_update ); //清除timx更新中断标志 key_event keyevent = key_status_check(); switch (keyevent) { case ke_short_press: printf(检测到单击\r\n); break; case ke_double_press: printf(检测到双击\r\n); break; case ke_long_press: printf(检测到长按\r\n); break; default:break; } }}
3 总结 本篇在前两篇按键状态机的基础上,继续介绍增加按键的双击功能,并解决之前状态存在的两个问题,通过实测验证,演示短按、长按、双击的使用效果。最后对代码结构进行优化,使其更符合实际开发应用。
温湿度的控制有助于提高工业造纸生产的效率
iPhone7屏幕拆解图文教程
美国军方探索人工智能的新用途
富满电子拟募集资金超过3.5亿元人民币投资智能化生产建设项目以及补充流动资金
华为占据5G手机半壁江山,国产智能手机一哥
STM32按键状态机3——增加双击与功能优化
嵌入式主板品牌有哪些_6大嵌入式主板品牌盘点
努比亚Z11miniS你绝不知道的4个小功能!
基于openEuler的全栈自主AI机密计算解决方案
nubiaX6拆解过程高清图集
关于GD32F207ZE的5x5 RGBW矩阵灯DMX512控制板的介绍和分析
RT6204应用原理图及设计指南
中兴成第二家携手英特尔的国产厂商 发力智能终端
解决自动驾驶的三个核心问题
谷歌表示已经从Play商店中删除了30种应用
为什么逆变器发出的电和电表走的不一样_案例分析详解
高增益的信号调理电路的薄膜和厚膜传感器-High-Gain
iPhone8什么时候上市:iphone8量产难发布要推迟!最新概念图似魅族?
卫星通信v2 第四章 传输技术(1)
“汇新杯”新兴科技+互联网创新大赛报名倒计时,现在只差一个你!