当前位置: 首页 >  资讯  >  正文

状态机——单片机的万能语言(附代码) 天天热头条
2023-05-06 15:21:33 面包芯语

在此之前,曾经分享过两种状态机的实现方法,有些朋友说有点难度,我想再补充一些基础实现方法以及思路,一步一步走,链接放在这里了:


(资料图片)

用C语言实现状态机(实用篇)

函数指针方法实现简单状态机(附代码)

本文将从最基础入门的方法,帮助大家了解状态机,从我常用的两种状态机编写方式为大家慢慢展开。

一、switch/case的方法来实现

要点:

用switch/case的结构配合一个状态变量,通过修改状态变量的值来切换状态。

代码如下:

1//代码参考网络 2 3//!定义状态名称与状态值之间的关系 4#defineFSM_START0x00 5#defineFSM_STATE_A0x01 6#defineFSM_STATE_B0x02 7… 8#defineFSM_RESET0xFF 910boolfsm_example_A(<形参列表>){11staticuint8_ts_chFSMState=FSM_START;//!<定义状态变量12…13switch(s_chFSMState){14caseFSM_START:15//!这里添加状态机初始化代码16…17s_chFSMState=FSM_STATE_A;//!<进入下一状态18break;19caseFSM_STATE_A:20//!这里添加状态机A进入下一状态的检测代码21if(<某某条件>){22//!这里做一些进入下一状态时要做的准备工作23s_chFSMState=FSM_STATE_B;//!<进入下一状态24}25break;26caseFSM_STATE_B:27//!这里添加状态机A进入下一状态的检测代码28if(<某某条件>){29//!这里做一些进入下一状态时要做的准备工作30s_chFSMState=FSM_STATE_A;//!<进入下一状态31}elseif(<某某条件>){32}elseif(<某某条件>){33…34}else{35}36break;37…38caseFSM_STOP:39caseFSM_RESET:40default:41//!这里添加状态机复位相关的代码42…43chFSMState=FSM_START;//!<状态机复位44//!返回false表示状态机已经不需要继续运行了45returnfalse;46}4748//!返回true表示状态机正在运行49returntrue;50}

小结:

从代码可知,这种状态机就是一路走到黑,没有让多个状态同时处于激活状态,也就是说在同一时刻,只能处于一种状态之下。

无疑,实际中有很多这样的应用,比如简单的灯的开关,当然也有很多情况是多种状态并存的,比如天气的状态就可以分为晴天、阴天、风雨雷电等等,可以同时处于多个状态。

二、通用的if/else来了

要点:

用if else…else if结构的组合来描述状态流程图。

范例:

1//代码参考网络 2//!首先将布尔量的状态标志压缩在一个字节里面以节省内存开支 3typedefunion{ 4uint8_tValue; 5uint8_tByte; 6struct{ 7unsignedBIT0:1; 8unsignedBIT1:1; 9unsignedBIT2:1;10unsignedBIT3:1;11unsignedBIT4:1;12unsignedBIT5:1;13unsignedBIT6:1;14unsignedBIT7:1;15}Bits;16}byte_t;1718#defineFSM_ACTION_FLAGs_tbState.Bits19#defineFSM_STOP_ALL_ACTIONS()do{s_tbState.Value=0;}while(0)20#defineFSM_START(0==s_tbState.Value)21#defineFSM_STATE_AFSM_ACTION_FLAG.BIT022#defineFSM_STATE_BFSM_ACTION_FLAG.BIT123…24#defineFSM_STATE_HFSM_ACTION_FLAG.BIT72526boolfsm_example_B(<形参列表>){27staticbyte_ts_tbState={0};//!<定义状态变量2829if(FSM_START){//!<起始状态30//!这里放置状态机初始化的代码31…32FSM_STATE_A=true;//!<进入状态B,start装台自动结束33}3435if(FSM_STATE_A){//!<一个典型的简单状态36//!这里放置状态A的代码或者37…38//!这里放置某些条件以开启别的状态39if(<某些条件>){40//!这里做一些“进入”下一个状态之前的准备工作41FSM_STATE_B=true;//!<开启下一个状态42FSM_STATE_A=false;//!<结束当前状态43}44}4546if(FSM_STATE_B){//!<一个典型的监视状态47…48//!这里检测某些条件49if(<某些条件>){50//!这里做一些“开启”某个状态的准备工作51FSM_STATE_C=true;//!<开启某一个状态而不结束当前状态52FSM_STATE_D=true;//!<你当然可以一次触发多个状态53…54}elseif(<某些条件>){55//!满足某些条件以后关闭当前状态56FSM_STATE_B=false;57}58}59…60if(FSM_STATE_F){//!<一个典型的子状态机调用61if(!fsm_example_a(<实参列表>)){//!<等待子状态机返回false62//!子状态机运行完成,进入下一状态63…64FSM_STATE_F=false;//!<结束当前状态65FSM_STATE_x=true;//!<进入下一状态x代表某个字母66}67}6869if(FSM_STATE_H){//!<一个典型的中止状态70//!<某些状态机的操作,比如释放某些资源71…72FSM_STOP_ALL_ACTIONS();//!<复位状态机73returnfalse;//!<返回false表示状态机结束74}7576returntrue;//!<返回true表示状态机保持运行77}

小结:

从范例可知,这种状态机虽然看起来比较费脑子,但是在应用当中非常灵活,通过布尔变量的开启和关闭,你可以自由的控制某些状态的开启。

并且同一时刻可能有多个状态是激活的,这种结构几乎可以翻译任何流程图。

三、所有的函数都可以看作是状态机

要点:

所有的函数都可以看作是状态机,如果函数有返回值,且这个返回值能表征至少两种以上不同的状态,那么这些返回值就可以被用作指示当前状态机的运行情况。

在我们实际编程中,我们也需要有这样的思维,比如函数之间的引用,参数传递,这些都可以当作一个状态,那么我们编码的过程中,就能够根据状态运行进行相应的模块化。

范例:

我们经常会用到的枚举类型,来写测试用例,以判断程序具体执行到函数体的哪一块了

1enum 2{ 3test1=0, 4test2, 5test3, 6test4, 7... 8} 910//举个简单的例子,根据返回值判断函数运行到哪里,来判断逻辑走向11inttestDemo()12{13if(FSM_STATE_A){14if(<某些条件>){15returntest1;16}else{17returntest2;18}19}else{20returntest3;21}22returntest4;23}

小结:

状态机可以说是一个万能的计算机语言表述方式,应用很广泛,是裸机条件下多任务的廉价实现方案。

四、状态机总结

在带有操作系统的情况下也是如此,我们了解了状态机的本质,能够运用得当的话,对我们的模块化编程,代码的整理是很有帮助的。

热门推荐