当前位置:网站首页>如何深入理解“有限状态机”的设计思想?
如何深入理解“有限状态机”的设计思想?
2022-07-05 14:00:00 【大方老师讲单片机】
如何深入理解“有限状态机”的设计思想?
程序设计和数字电路设计中都经常会用到“有限状态机”的设计思想。
在嵌入式软件开发中,状态机编程是一个十分重要的编程思想,它也是嵌入式开发中一个常用的编程框架。掌握了状态机编程思想,可以更加逻辑清晰的实现复杂的业务逻辑功能。
1状态机思想
状态机,或称有限状态机FSM(Finite State Machine),是一种重要的编程思想。
状态机有3要素:状态、事件与响应
·状态:系统处在什么状态?
·事件:发生了什么事?
·响应:此状态下发生了这样的事,系统要如何处理?
状态机编程前,首先要根据需要实现的功能,整理出一个对应的状态转换图(状态机图),然后就可以根据这个状态转换图,套用状态机编程模板,实现对应是状态机代码了。
状态机编程主要有 3种方法:switch-case法、表格驱动法、函数指针法,本篇先介绍最简单也最易理解的switch-case法。
2状态机实例
下面以按键消抖功能,来介绍switch-case法的状态机编程思路。
2.1按钮消抖状态转换图
状态机机编程前,首先要明确的对应功能的状态机需要几个状态,本例的按键功能,只检测最基础的按下与松开状态(暂不实现长按、双击等状态),并增加对应的按钮去抖功能,因此,需要用到4个状态:
·稳定松开状态
·按下抖动状态
·稳定按下状态
·松开抖动状态
对应的状态转换图如下:
由于按键通常处于松开状态,这里让状态机的初始化状态为松开状态,然后在这4个状态中来回切换。
图中的VT代表按键检测到电平,VT=0即检测到低电平,可能是按键按下,由初始的“稳定松开”状态转为“按下抖动”状态
当持续检测到低电平(VT=0)一段时间后,认为消抖完成,由“按下抖动”状态转为“稳定按下”状态
在“按下抖动”状态时,在指定的一段时间内,再次检测到高电平(VT=1),说明确实是按钮抖动(比如按键被快速拨动了一下又弹起,或强烈震动导致的按键抖动),则由“按下抖动”状态转为“稳定松开”状态
2.2编程实现
2.2.1状态定义
对应上面的按钮状态图,可以知道需要用到4个状态:
·稳定松开状态(KS_RELEASE)
·按下抖动状态(KS_PRESS_SHAKE)
·稳定按下状态(KS_PRESS)
·松开抖动状态(KS_RELEASE_SHAKE)
这里使用枚举来定义这4个状态。为了在调试时,能够把对应状态名称以字符串的形式打印出来,这里使用宏定义的一个小技巧:
#符号+自定义的枚举名称
即可自动转变为字符串形式,再将这些字符串放到const char* key_status_name[]数组中,便可通过数组的形式访问这些状态的字符串名称形式。
此外,为了不重复书写枚举名称与对应的枚举字符串(#+枚举名称),进一步使用宏定义的方式,只定义一次状态,然后通过下面两条宏定义,实现对枚举项和枚举项对应的字符串的分别获取:
#define ENUM_ITEM(ITEM) ITEM,#define ENUM_STRING(ITEM) #ITEM,
具体是宏定义、枚举定义与枚举名称数组声明如下:
#define ENUM_ITEM(ITEM) ITEM,#define ENUM_STRING(ITEM) #ITEM,#define KEY_STATUS_ENUM(STATUS) \ STATUS(KS_RELEASE) /*稳定松开状态*/ \ STATUS(KS_PRESS_SHAKE) /*按下抖动状态*/ \ STATUS(KS_PRESS) /*稳定按下状态*/ \ STATUS(KS_RELEASE_SHAKE) /*松开抖动状态*/ \ STATUS(KS_NUM) /*状态总数(无效状态)*/ \ typedefenum{
KEY_STATUS_ENUM(ENUM_ITEM)}KEY_STATUS;constchar*key_status_name[]={
KEY_STATUS_ENUM(ENUM_STRING)};
宏定义不便理解的,可以将宏定义分别带入,转为最终的结果,理解替代后的具体形式,比如下面的宏定义带入替换示意:
/*KEY_STATUS_ENUM(STATUS) --> STATUS(KS_RELEASE) ... STATUS(KS_NUM)KEY_STATUS_ENUM(ENUM_ITEM)--> ENUM_ITEM(KS_RELEASE) ... ENUM_ITEM(KS_NUM)--> KS_RELEASE, ... KS_NUM,KEY_STATUS_ENUM(ENUM_STRING)--> ENUM_STRING(KS_RELEASE) ... ENUM_STRING(KS_NUM)--> #KS_RELEASE, ... #KS_NUM,*/
2.2.2状态机实现
下面是状态机的具体实现:
·状态机函数key_status_check在一个循环中,被每隔10ms调用一次
·定义一个g_keyStatus表示状态机所处的状态
·在每个循环中,switch根据当前的状态,执行对应状态所需要执行的逻辑
·定义一个g_DebounceCnt用于消抖时间计算,当持续进入消抖状态,每次循环(10ms)中将此值加1,持续一定次数(5次,即50ms),认为是稳定的按下或松开,消抖完成,跳转到稳定方向或稳定松开状态
·在每个状态的执行逻辑中,当检测到某些条件满足时,跳转到其它的状态
·通过状态的不断跳转,实现状态机的运行
·此外,为方便观察状态机中状态的变化,定义了一个g_lastKeyStatus表示前一状态,当状态发生变化时,可以将状态名称打印出来
KEY_STATUSg_keyStatus=KS_RELEASE;//当前按键的状态KEY_STATUSg_lastKeyStatus=KS_NUM;//上一状态intg_DebounceCnt=0;//消抖时间计数voidkey_status_check(){
switch(g_keyStatus)
{
//按键释放(初始状态)caseKS_RELEASE:
{
//检测到低电平,先进行消抖if(KEY0==0)
{
g_keyStatus=KS_PRESS_SHAKE;
g_DebounceCnt=0;
}
}
break;
//按下抖动caseKS_PRESS_SHAKE:
{
g_DebounceCnt++;
//确实是抖动if(KEY0==1)
{
g_keyStatus=KS_RELEASE;
}
//消抖完成elseif(g_DebounceCnt==5)
{
g_keyStatus=KS_PRESS;
printf("=====> key press\r\n");
}
}
break;
//稳定按下caseKS_PRESS:
{
//检测到高电平,先进行消抖if(KEY0==1)
{
g_keyStatus=KS_RELEASE_SHAKE;
g_DebounceCnt=0;
}
}
break;
//松开抖动caseKS_RELEASE_SHAKE:
{
g_DebounceCnt++;
//确实是抖动if(KEY0==0)
{
g_keyStatus=KS_PRESS;
}
//消抖完成elseif(g_DebounceCnt==5)
{
g_keyStatus=KS_RELEASE;
printf("=====> key release\r\n");
}
}
break;
default:break;
}
if(g_keyStatus!=g_lastKeyStatus)
{
g_lastKeyStatus=g_keyStatus;
printf("new key status:%d(%s)\r\n",g_keyStatus,key_status_name[g_keyStatus]);
}}intmain(void){
delay_init();//延时函数初始化KEY_Init();
uart_init(115200);
printf("hello\r\n");
while(1)
{
key_status_check();
delay_ms(10);
}}
注:本例程需要使用一个按键,需要初始化对应的GPIO,这里不再贴代码。
2.3使用测试
将完整的代码编译后烧录到板子中,连接串口,按下与松开按键,观察串口输出信息。
我的测试输出信息如下:
前两次拨动按键模拟按钮抖动的情况,可以看到串口打印出两次从松开到按下抖动的状态切换。
然后是按下按键,再松开按键,可以看到状态的变化:松开 ->按下抖动 ->按下 ->松开抖动 ->松开
3总结
本篇介绍了嵌入式软件开发中常用的状态机编程实现,并通过按键消抖实例,以常用的switch-case形式,实现了对应的状态机编程代码实现,并通过测试,串口打印对应状态,分析状态机的状态跳转过程。
边栏推荐
- 那些考研后才知道的事
- :: ffff:192.168.31.101 what address is it?
- 2022建筑焊工(建筑特殊工种)特种作业证考试题库及在线模拟考试
- 2022 construction welder (special type of construction work) special operation certificate examination question bank and online simulation examination
- Requset + BS4 crawling shell listings
- Self built shooting range 2022
- Log4j utilization correlation
- What is information security? What is included? What is the difference with network security?
- About the problem and solution of 403 error in wampserver
- 03_Solr之dataimport
猜你喜欢
金融壹账通香港上市:市值63亿港元 叶望春称守正笃实,久久为功
IP packet header analysis and static routing
我为什么支持 BAT 拆掉「AI 研究院」
Attack and defense world crypto WP
In addition to the root directory, other routes of laravel + xampp are 404 solutions
【华南理工大学】考研初试复试资料分享
upload (1-6)
Assembly language - Beginner's introduction
Laravel dompdf exports PDF, and the problem of Chinese garbled code is solved
TiCDC 6.0原理之Sorter演进
随机推荐
如何把大的‘tar‘存档文件分割成特定大小的多个文件
What is the ranking of GF futures? Is it safe and reliable to open an account for GF futures online?
Why do I support bat to dismantle "AI research institute"
登录界面代码
Solution to the prompt of could not close zip file during phpword use
Recommendation number | what are interesting people looking at?
How to deal with the Yellow Icon during the installation of wampserver
【云资源】云资源安全管理用什么软件好?为什么?
Hongmeng fourth training
Blue Bridge Cup study 2022.7.5 (morning)
牛客网:拦截导弹
Attack and defense world crypto WP
Self built shooting range 2022
Linux下mysql数据库安装教程
PHP basic syntax
Some ideas about Apache mesos
[buuctf.reverse] 152-154
Leetcode array question brushing notes
poi设置列的数据格式(有效)
Pancake Bulldog robot V2 (code optimized)