当前位置:网站首页>STM32按键状态机2——状态简化与增加长按功能
STM32按键状态机2——状态简化与增加长按功能
2022-07-07 00:04:00 【华为云】
上篇的状态图如下:
由于只检测按下与松开,并具备按键消抖功能,因此用到了如上的4个状态,按下抖动和松开抖动是两个独立的状态,并且这两个抖动的状态,也是可以在多次循环中连续运行的,这个状态机的循环周期设置的为10ms,当在抖动状态连续检测到某一电平5次后,即认为消抖完成,进入下一个稳定状态。
对于同一个功能,状态图不是一成不变的,对于按键消抖,还可以将两个抖动状态共用一个抖动状态来表示。
1 消抖状态简化
1.1 状态图
将按下抖动与松开抖动共用一个抖动状态来表示,同时需要将状态机的循环周期设置为50ms,这样,抖动状态只需经过一次,通过电平高低即可判定是否真的为按键抖动。简化后的状态图如下:
为了能在抖动状态时,区分前一状态是松开还是按下,进而判断此次是抖动还是按键真的动作,需要增加一个状态来记录前一状态
KEY_STATUS g_keyStatus = KS_RELEASE; //当前循环结束的(状态机的)状态KEY_STATUS g_nowKeyStatus = KS_RELEASE; //当前状态(每次循环后与g_keyStatus保持一致)KEY_STATUS g_lastKeyStatus = KS_RELEASE; //上次状态(用于记录前一状态以区分状态的来源)
注意:此处的g_lastKeyStatus用于记录前一状态,上篇文章中也有这个变量,但作用不同,上篇文章中此变量的作用与此处的g_nowKeyStatus作用相同。
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) { g_keyStatus = KS_RELEASE; if (KS_PRESS == g_lastKeyStatus) { printf("=====> key release\r\n"); } } else { g_keyStatus = KS_PRESS; if (KS_RELEASE == g_lastKeyStatus) { printf("=====> key press\r\n"); } } } break; //稳定短按 case KS_PRESS: { //检测到高电平,先进行消抖 if (KEY0 == 1) { g_keyStatus = KS_SHAKE; } } 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]); }}
注意g_lastKeyStatus变量的作用。
1.3 测试
2 增加长按功能
在检测按下与松开的基础上,再增加长按功能,在状态图中需要增加一个长按状态。然后,对照着状态图修改代码即可。
同样,根据是否需要区分两种抖动状态以及状态机循环周期的不同,可以有两种状态图。
2.1 未简化的状态图
先来看一下循环周期10ms,区分按下抖动与松开抖动这种情况增加长按功能后的状态图:
状态图理清逻辑后,根据状态图,修改对应的代码即可,这里不再贴代码,完整代码可去我的代码仓库查看
2.2 简化的状态图
下面再来看简化消抖状态的具体长按功能的状态机图:
对比可以发现,简化的状态图,状态可以少一个,不过抖动的状态,会有更多的输入和输出,因为目前每隔状态都有经过这个状态。
如果对于抖动检测的要求不高,也可以只保留按下抖动的逻辑,松开抖动的分支去掉,直接跳到松开状态,可以再次简化状态逻辑。
2.3 代码
根据状态图图,编写对应的状态机逻辑代码,如下:
void key_status_check(){ switch(g_keyStatus) { //按键释放(初始状态) case KS_RELEASE: { //检测到低电平,先进行消抖 if (KEY0 == 0) { g_keyStatus = KS_SHAKE; } } break; //抖动 case KS_SHAKE: { if (KEY0 == 1) { g_keyStatus = KS_RELEASE; if (KS_SHORT_PRESS == g_lastKeyStatus || KS_LONG_PRESS == g_lastKeyStatus) { printf("=====> key release\r\n"); } } else { if (KS_RELEASE == g_lastKeyStatus) { g_PressTimeCnt = 0; g_keyStatus = KS_SHORT_PRESS; printf("=====> key short press\r\n"); } else if (KS_SHORT_PRESS == g_lastKeyStatus) { g_keyStatus = KS_SHORT_PRESS; } else { } } } break; //稳定短按 case KS_SHORT_PRESS: { //检测到高电平,先进行消抖 if (KEY0 == 1) { g_keyStatus = KS_SHAKE; } g_PressTimeCnt++; if (g_PressTimeCnt == 20) //1000ms { g_keyStatus = KS_LONG_PRESS; printf("=====> key long press\r\n"); } } break; //稳定长按 case KS_LONG_PRESS: { //检测到高电平,先进行消抖 if (KEY0 == 1) { g_keyStatus = KS_SHAKE; } g_PressTimeCnt++; if (g_PressTimeCnt % 20 == 0) //每隔1000ms打印一次 { printf("=====> key long press:%d\r\n", g_PressTimeCnt/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]); }}
注意,在抖动状态,当检测为高电平(按键松开),不管前一状态是短按还是长按,下一状态都是松开状态。
2.4 测试
3 总结
本篇继续介绍状态机的使用,在上篇的基础上,通过简化按键去抖逻辑,并增加按键长按功能,进一步介绍状态图的修改与状态机代码的实现,并通过实际测试,演示状态机的运行效果。
边栏推荐
猜你喜欢
随机推荐
Educational Codeforces Round 22 B. The Golden Age
Dj-zbs2 leakage relay
拼多多新店如何获取免费流量,需要从哪些环节去优化,才能有效提升店内免费流量
ssm框架的简单案例
什么是依赖注入(DI)
爬虫练习题(三)
Web Authentication API兼容版本信息
Design, configuration and points for attention of network specified source multicast (SSM) simulation using OPNET
Lombok plug-in
软件测试面试技巧
Mapbox Chinese map address
Common skills and understanding of SQL optimization
Sidecar mode
分布式事务介绍
[paper reading] semi supervised left atrium segmentation with mutual consistency training
消息队列:如何确保消息不会丢失
Preliminary practice of niuke.com (9)
How to get free traffic in pinduoduo new store and what links need to be optimized in order to effectively improve the free traffic in the store
三级菜单数据实现,实现嵌套三级菜单数据
Jhok-zbl1 leakage relay