当前位置:网站首页>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 总结
本篇继续介绍状态机的使用,在上篇的基础上,通过简化按键去抖逻辑,并增加按键长按功能,进一步介绍状态图的修改与状态机代码的实现,并通过实际测试,演示状态机的运行效果。
边栏推荐
- JVM the truth you need to know
- MySQL-CentOS7通过YUM安装MySQL
- 毕业之后才知道的——知网查重原理以及降重举例
- 论文阅读【Semantic Tag Augmented XlanV Model for Video Captioning】
- The 2022 China low / no code Market Research and model selection evaluation report was released
- Paper reading [MM21 pre training for video understanding challenge:video captioning with pre training techniqu]
- 纪念下,我从CSDN搬家到博客园啦!
- Flink SQL 实现读写redis,并动态生成Hset key
- async / await
- 关于服装ERP,你知道多少?
猜你喜欢
基于NCF的多模块协同实例
JD commodity details page API interface, JD commodity sales API interface, JD commodity list API interface, JD app details API interface, JD details API interface, JD SKU information interface
Unity让摄像机一直跟随在玩家后上方
随机生成session_id
Paper reading [semantic tag enlarged xlnv model for video captioning]
Introduction to distributed transactions
DOM node object + time node comprehensive case
《ClickHouse原理解析与应用实践》读书笔记(6)
JVM (19) -- bytecode and class loading (4) -- talk about class loader again
4. 对象映射 - Mapping.Mapster
随机推荐
Modes of optical fiber - single mode and multimode
Flinksql 读写pgsql
Getting started with DES encryption
Reading the paper [sensor enlarged egocentric video captioning with dynamic modal attention]
4. Object mapping Mapster
4. 对象映射 - Mapping.Mapster
What is dependency injection (DI)
Paper reading [MM21 pre training for video understanding challenge:video captioning with pre training techniqu]
JVM the truth you need to know
Design, configuration and points for attention of network arbitrary source multicast (ASM) simulation using OPNET
Message queue: how to deal with message backlog?
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
京东商品详情页API接口、京东商品销量API接口、京东商品列表API接口、京东APP详情API接口、京东详情API接口,京东SKU信息接口
SQLSTATE[HY000][1130] Host ‘host. docker. internal‘ is not allowed to connect to this MySQL server
JVM (19) -- bytecode and class loading (4) -- talk about class loader again
English grammar_ Noun possessive
Educational Codeforces Round 22 B. The Golden Age
AI face editor makes Lena smile
Lombok plug-in
Design, configuration and points for attention of network unicast (one server, multiple clients) simulation using OPNET