1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
一、有限状态机(FSM)简介
有限状态机FSM(Finite State Machine),通常指任意一个时刻在一种状态之中,不同状态的转移是通过动作来触发的,不同状态下,不同动作将触发不同的状态转移,当然也可以不发生转移。 二、按键的状态机 1. 按键的状态 常用的按键只有2个状态,按下(press)和抬起(release) 2. 按键的动作 常用的按键只有2个动作:按下和抬起 3. 按键的状态转换图 当按键被按下时候,它的状态为按下,当按键松开时,它的状态为松开: 2. 按键事件与定时器应用 通常按键事件有短按和长按,本文定义了五种按键事件: [tr]事件(action)描述[/tr]
三、状态图与事件 如上所示,可综合绘制状态装换图与事件产生的条件: 四、程序设计 为简化程序,本文使用电容触摸模块,固不讨论按键抖动,程序也无滤波部分。另外,当电容被触摸时候,输出高电平,当电容未被触摸时,输出低电平。 1. 枚举变量:按键状态与事件 //按键状态 enum btn_sta{ RELEASE = 0, PRESSED = 1 }; //按键事件 enum { ACT_NO = 0, ACT_PRESS, ACT_SHORT_CLICKED, ACT_LONG_CLICKED, ACT_LONG_PRESSING, }_btn_evt; 首先将按键状态与按键事件通过枚举变量的形式表达,提高程序的可读性。 2. 定义状态 static enum btn_sta _cur_sta = RELEASE, _last_sta = RELEASE; 在状态机中,状态的转移需要当前状态,所以定义_cur_sta来存储,同时,事件的产生需要和上一状态对比,所以需要_last_sta来存储,在程序中,当一次读取按键结束后,当前的状态值,就成为了上次状态的值。 3. 事件的检测 关键变量定义完成以后,就需要具体的程序逻辑实现,通常状态机可以用swich case和if语句来实现: _cur_sta = (enum btn_sta)GET_BTN_STA(); //获取当前按键的动作,读取IO的值 if(_last_sta == RELEASE) //上次状态释放 { switch (_cur_sta) { case RELEASE: //当前状态为释放 _btn_evt = ACT_NO; //一直释放状态:无事件 break; case PRESSED: //当前状态为按下 _btn_evt = ACT_PRESS; //释放到按下:按下动作 time_last = HAL_GetTick(); //记录时刻 break; } } else if(_last_sta == PRESSED) //上次状态为抬起 { switch (_cur_sta) { case RELEASE: //当前状态为释放 if(HAL_GetTick() - time_last > 1000) { _btn_evt = ACT_LONG_CLICKED; //间隔<1s,短按(单击) } else { _btn_evt = ACT_SHORT_CLICKED; //间隔>1s,长按(长单击) } break; case PRESSED: //当前事件为按下 if(HAL_GetTick() - time_last > 1000) { _btn_evt = ACT_LONG_PRESSING; //事件间隔>1s,长按中 } break; } } _last_sta = _cur_sta; //本次状态更新为上一次状态, //为下次扫描做准备 4. 事件处理(禁止/启用重复触发) 当获取一次事件以后,需要对各个事件进行处理,首先需要定一个各个处理函数: void btn_evt_proc_short_click(void) { printf("%srn",__FUNCTION__); } void btn_evt_proc_long_click(void) { printf("%srn",__FUNCTION__); } void btn_evt_proc_long_pressing(void) { printf("%srn",__FUNCTION__); } void btn_evt_proc_press(void) { printf("%srn",__FUNCTION__); } 接着使用switch语句可以很简单的处理各个事件: switch(_btn_evt) { case ACT_NO: break; case ACT_SHORT_CLICKED: btn_evt_proc_short_click(); break; case ACT_LONG_CLICKED: btn_evt_proc_long_click(); break; case ACT_PRESS: btn_evt_proc_press(); break; case ACT_LONG_PRESSING: btn_evt_proc_long_pressing(); break; } return; 由于程序会不断的扫描按键,考虑一种情况:当用户按下按键时候,程序就会不断的检测到按下事件,用户若长时间不松开,程序还将一直检测到ACT_LONG_PRESSING(长按中)事件,那么程序将不断的调用btn_evt_proc_long_pressing();,若你需要在这个过程中,只调用一次函数,那么可以设计程序,禁止重复触发,注意到以下程序片段需要添加到按键事件处理之前: #define BAN_DECT_REPET 1 #if BAN_DECT_REPET uint8_t static last_evt = 0; if(last_evt == _btn_evt) { last_evt = _btn_evt; //若本次事件和上次相同 return; //程序返回 } last_evt = _btn_evt; #endif 可以看到,当 BAN_DECT_REPET 为1时候,程序将会被程序,若2次事件一致,程序将返回(提前返回),此时,程序处理函数将不会被触发。 五、程序源码 程序完整源码(File:button.c): /****************************************************************************************** * @File: button.c * @Data:2020年7月2日 * @by :YonasLuo * @ver :1.0 ******************************************************************************************/ #include "button.h" #include "stdio.h" #include "main.h" #include "gpio.h" /****************************************************************************************** * @buttonCode ******************************************************************************************/ #define GET_BTN_STA() (HAL_GPIO_ReadPin(btn_GPIO_Port, btn_Pin)) #define BAN_DECT_REPET (1) //1:repet dectect 0: ban repet dectect /****************************************************************************************** * @API ******************************************************************************************/ void btn_evt_proc_short_click(void) { printf("%srn",__FUNCTION__); } void btn_evt_proc_long_click(void) { printf("%srn",__FUNCTION__); } void btn_evt_proc_long_pressing(void) { printf("%srn",__FUNCTION__); } void btn_evt_proc_press(void) { printf("%srn",__FUNCTION__); } /****************************************************************************************** * @buttonCode ******************************************************************************************/ enum btn_sta{ RELEASE = 0, PRESSED = 1 }; enum { ACT_NO = 0, ACT_PRESS, ACT_SHORT_CLICKED, ACT_LONG_CLICKED, ACT_LONG_PRESSING, }_btn_evt; static enum btn_sta _cur_sta = RELEASE, _last_sta = RELEASE; static uint16_t time_last = 0; void btn_proc_poll(void) { _cur_sta = (enum btn_sta)GET_BTN_STA(); if(_last_sta == RELEASE) { switch (_cur_sta) { case RELEASE: _btn_evt = ACT_NO; break; case PRESSED: _btn_evt = ACT_PRESS; time_last = HAL_GetTick(); break; } } else if(_last_sta == PRESSED) { switch (_cur_sta) { case RELEASE: if(HAL_GetTick() - time_last > 1000) { _btn_evt = ACT_LONG_CLICKED; } else { _btn_evt = ACT_SHORT_CLICKED; } break; case PRESSED: if(HAL_GetTick() - time_last > 1000) { _btn_evt = ACT_LONG_PRESSING; } break; } } _last_sta = _cur_sta; #if BAN_DECT_REPET uint8_t static last_evt = 0; if(last_evt == _btn_evt) { last_evt = _btn_evt; return; } last_evt = _btn_evt; #endif switch(_btn_evt) { case ACT_NO: break; case ACT_SHORT_CLICKED: btn_evt_proc_short_click(); break; case ACT_LONG_CLICKED: btn_evt_proc_long_click(); break; case ACT_PRESS: btn_evt_proc_press(); break; case ACT_LONG_PRESSING: btn_evt_proc_long_pressing(); break; } return; } /***************************** END OF FILE *****************************/ 六、测试 ... void main(void) { .... extern void btn_proc_poll(void ); while (1) { btn_proc_poll(); } .... } 测试函数时,在main()中不断调用处理函数btn_proc_poll()即可 七、改进 这个程序最明显的问题是移植不够简便:
|
|
|
|
只有小组成员才能发言,加入小组>>
3310 浏览 9 评论
2991 浏览 16 评论
3492 浏览 1 评论
9055 浏览 16 评论
4086 浏览 18 评论
1174浏览 3评论
603浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
596浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2333浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1894浏览 2评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-21 21:19 , Processed in 1.176023 second(s), Total 77, Slave 58 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号