不知道大家有没有这样一种感觉,就是感觉自己玩单片机还可以,各个功能模块也都会驱动,但是如果让你完整地写一套代码,却无逻辑与框架可言,上来就是开始写!东抄抄西抄抄,说明编程还处于比较低的水平。那么,如何才能提高自己的编程水平呢?

学会一种好的编程框架或者一种编程思想,可能会受用终生!比如模块化编程、框架式编程、状态机编程等,都是一种好的框架。

状态机(state machine)有5个要素,分别是状态(state)、迁移(transition)、事件(event)、动作(action)、条件(guard)。

什么是状态机

状态机是一个这样的东东:状态机(state machine)有 5 个要素,分别是状态(state)、迁移(transition)、事件(event)、动作(action)、条件(guard)。

        状态:一个系统在某一时刻所存在的稳定的工作情况,系统在整个工作周期中可能有多个状态。例如一部电动机共有正转、反转、停转这 3 种状态。一个状态机需要在状态集合中选取一个状态作为初始状态。

        迁移:系统从一个状态转移到另一个状态的过程称作迁移,迁移不是自动发生的,需要外界对系统施加影响。停转的电动机自己不会转起来,让它转起来必须上电。

        事件:某一时刻发生的对系统有意义的事情,状态机之所以发生状态迁移,就是因为出现了事件。对电动机来讲,加正电压、加负电压、断电就是事件

       动作:在状态机的迁移过程中,状态机会做出一些其它的行为,这些行为就是动作,动作是状态机对事件的响应。给停转的电动机加正电压,电动机由停转状态迁移到正转状态,同时会启动电机,这个启动过程可以看做是动作,也就是对上电事件的响应。

         条件:状态机对事件并不是有求必应的,有了事件,状态机还要满足一定的条件才能发生状态迁移。还是以停转状态的电动机为例,虽然合闸上电了,但是如果供电线路有问题的话,电动机还是不能转起来。

只谈概念太空洞了,上一个小例子:一单片机、一按键、两LED 灯(记为L1和L2)。

规则描述:

1、L1L2状态转换顺序OFF/OFF--->ON/OFF--->ON/ON--->OFF/ON--->OFF/OFF

2、通过按键控制L1L2的状态,每次状态转换需连续按键5

3、L1L2的初始状态OFF/OFF

下面这段程序是根据功能要求写成的代码。

程序清单List1:

void main(void)
{sys_init();led_off(LED1);led_off(LED2);g_stFSM.u8LedStat = LS_OFFOFF;g_stFSM.u8KeyCnt = 0;while(1){if(test_key()==TRUE){fsm_active();}else{; /*idle code*/}}
}
void fsm_active(void)
{if(g_stFSM.u8KeyCnt > 3) /*击键是否满 5 次*/{switch(g_stFSM.u8LedStat){case LS_OFFOFF:led_on(LED1); /*输出动作*/g_stFSM.u8KeyCnt = 0;g_stFSM.u8LedStat = LS_ONOFF; /*状态迁移*/break;case LS_ONOFF:led_on(LED2); /*输出动作*/g_stFSM.u8KeyCnt = 0;g_stFSM.u8LedStat = LS_ONON; /*状态迁移*/break;case LS_ONON:led_off(LED1); /*输出动作*/g_stFSM.u8KeyCnt = 0;g_stFSM.u8LedStat = LS_OFFON; /*状态迁移*/break;case LS_OFFON:led_off(LED2); /*输出动作*/g_stFSM.u8KeyCnt = 0;g_stFSM.u8LedStat = LS_OFFOFF; /*状态迁移*/break;default: /*非法状态*/led_off(LED1);led_off(LED2);g_stFSM.u8KeyCnt = 0;g_stFSM.u8LedStat = LS_OFFOFF; /*恢复初始状态*/break;}}else{g_stFSM.u8KeyCnt++; /*状态不迁移,仅记录击键次数*/}
}

实际上在状态机编程中,正确的顺序应该是先有状态转换图,后有程序,程序应该是根据设计好的状态图写出来的。不过考虑到有些童鞋会觉得代码要比转换图来得亲切,我就先把程序放在前头了。

这张状态转换图是用UML(统一建模语言)的语法元素画出来的。

圆角矩形代表状态机的各个状态,里面标注着状态的名称。带箭头的直线或弧线代表状态迁移,起于初态,止于次态。图中的文字内容是对迁移的说明,格式是:事件[条件]/动作列表(后两项可选)。

“事件[条件]/动作列表”要说明的意思是:如果在某个状态下发生了“事件”,并且状态机满足“[条件]”,那么就要执行此次状态转移,同时要产生一系列“动作”,以响应事件。在这个例子里,我用“KEY”表示击键事件。

图中有一个黑色实心圆点,表示状态机在工作之前所处的一种不可知的状态,在运行之前状态机必须强制地由这个状态迁移到初始状态,这个迁移可以有动作列表(如图1所示),但不需要事件触发。

图中还有一个包含黑色实心圆点的圆圈,表示状态机生命周期的结束,这个例子中的状态机生生不息,所以没有状态指向该圆圈。

关于这个状态转换图就不多说了,相信大家结合着上面的代码能很容易看明白。现在我们再聊一聊程序清单List1。

先看一下fsm_active()这个函数,g_stFSM.u8KeyCnt = 0;这个语句在switch—case里共出现了 5 次,前 4 次是作为各个状态迁移的动作出现的。从代码简化提高效率的角度来看,我们完全可以把这 5 次合并为 1 次放在 switch—case 语句之前,两者的效果是完全一样的,代码里之所以这样啰嗦,是为了清晰地表明每次状态迁移中所有的动作细节,这种方式和图2的状态转换图所要表达的意图是完全一致的。

再看一下g_stFSM这个状态机结构体变量,它有两个成员:u8LedStat和 u8KeyCnt。用这个结构体来做状态机好像有点儿啰嗦,我们能不能只用一个像 u8LedStat 这样的整型变量来做状态机呢?

当然可以!我们把图 2中的这 4 个状态各自拆分成 5 个小状态,这样用 20 个状态同样能实现这个状态机,而且只需要一个 unsigned char 型的变量就足够了,每次击键都会引发状态迁移, 每迁移 5 次就能改变一次 LED 灯的状态,从外面看两种方法的效果完全一样。

假设我把功能要求改一下,把连续击键5次改变L1L2的状态改为连续击键100次才能改变L1L2的状态。这样的话第二种方法需要4X100=400个状态!而且函数fsm_active()中的switch—case语句里要有400个case,这样的程序还有法儿写么?

同样的功能改动,如果用g_stFSM这个结构体来实现状态机的话,函数fsm_active()只需要将if(g_stFSM.u8KeyCnt>3)改为if(g_stFSM.u8KeyCnt > 98)就可以了!

   g_stFSM结构体的两个成员中,u8LedStat可以看作是质变因子,相当于主变量;u8KeyCnt可以看作是量变因子,相当于辅助变量。量变因子的逐步积累会引发质变因子的变化。像g_stFSM这样的状态机被称作Extended State Machine

状态机编程的优点

(1)提高CPU使用效率

话说我只要见到满篇都是delay_ms()的程序就会蛋疼,动辄十几个ms几十个ms的软件延时是对CPU资源的巨大浪费,宝贵的CPU机时都浪费在了NOP指令上。那种为了等待一个管脚电平跳变或者一个串口数据而岿然不动的程序也让我非常纠结,如果事件一直不发生,你要等到世界末日么?

把程序状态机化,这种情况就会明显改观,程序只需要用全局变量记录下工作状态,就可以转头去干别的工作了。当然,忙完那些活儿之后,要再看看工作状态有没有变化。只要目标事件(定时未到、电平没跳变、串口数据没收完)还没发生,工作状态就不会改变,程序就一直重复着“查询—干别的—查询—干别的”这样的循环,这样CPU就闲不下来了。

在程序清单 List3 中,if{}else{}语句里else下的内容(代码中没有添加,只是加了一条/*idle code*/的注释示意)就是上文所说的“别的工作” 。这种处理方法的实质,就是在程序等待事件的过程中间隔性地插入一些有意义的工作,好让CPU不是一直无谓地等待。

(2) 逻辑完备性

不知道大家有没有用C语言写过计算器的小程序,我很早以前写过,写出来一测试,那个惨不忍睹啊!当我规规矩矩的输入算式的时候,程序可以得到正确的计算结果,但要是故意输入数字和运算符号的随意组合,程序总是得出莫名其妙的结果。

后来我试着思维模拟一下程序的工作过程,正确的算式思路清晰,流程顺畅,可要碰上了不规矩的式子,走着走着我就晕菜了,那么多的标志位,那么多的变量,变来变去,最后直接分析不下去了。

很久之后我认识了状态机,才恍然明白,当时的程序是有逻辑漏洞的。如果把这个计算器程序当做是一个反应式系统,那么一个数字或者运算符就可以看做一个事件,一个算式就是一组事件组合。

对于一个逻辑完备的反应式系统,不管什么样的事件组合,系统都能正确处理事件,而且系统自身的工作状态也一直处在可知可控的状态中。反过来,如果一个系统的逻辑功能不完备,在某些特定事件组合的驱动下,系统就会进入一个不可知不可控的状态,与设计者的意图相悖。

状态机就能解决逻辑完备性的问题。

状态机是一种以系统状态为中心,以事件为变量的设计方法,它专注于各个状态的特点以及状态之间相互转换的关系。状态的转换恰恰是事件引起的,那么在研究某个具体状态的时候,我们自然而然地会考虑任何一个事件对这个状态有什么样的影响。这样,每一个状态中发生的每一个事件都会在我们的考虑之中,也就不会留下逻辑漏洞。

(3)程序结构清晰

用状态机写出来的程序的结构是非常清晰的。

程序员最痛苦的事儿莫过于读别人写的代码。如果代码不是很规范,而且手里还没有流程图,读代码会让人晕了又晕,只有顺着程序一遍又一遍的看,很多遍之后才能隐约地明白程序大体的工作过程。有流程图会好一点,但是如果程序比较大,流程图也不会画得多详细,很多细节上的过程还是要从代码中理解。

相比之下,用状态机写的程序要好很多,拿一张标准的UML状态转换图,再配上一些简明的文字说明,程序中的各个要素一览无余。程序中有哪些状态,会发生哪些事件,状态机如何响应,响应之后跳转到哪个状态,这些都十分明朗,甚至许多动作细节都能从状态转换图中找到。可以毫不夸张的说,有了UML状态转换图,程序流程图写都不用写。

嵌入式状态机编程简介相关推荐

  1. 使用Keil语言的嵌入式C编程教程(上)

    使用Keil语言的嵌入式C编程教程(上) Embedded C Programming Tutorial with Keil Language Embedded System 嵌入式系统是指以单片机为 ...

  2. 1.嵌入式系统的简介

    1.嵌入式系统的简介 1.1 课程规划与学习方法 嵌入式系统无疑是当前最热门最有发展前途的IT应用领域之一,但同时也是最难以掌 握的学科之一,很多踌躇满志的同学最终没能成为一名合格的嵌入式工程师,很大 ...

  3. C语言状态机编程思想

    关注.星标公众号,直达精彩内容 文章来源:头条-嵌入式在左C语言在右 链接:https://www.toutiao.com/i6843028812112855564/ 有限状态机概念 有限状态机是一种 ...

  4. [转载]Java嵌入式开发之一-简介使用Java编写Palm OS程序的解决方案

    Java嵌入式开发之一-简介使用Java编写Palm OS程序的解决方案 现在,使用Java语言为 Palm OS编写程序的领域还没有完全统一,并且也有许多程度上的差异,目前,市面上有好几种不同的可用 ...

  5. c语言按键状态机,C语言状态机编程思想

    原标题:C语言状态机编程思想 有限状态机概念 有限状态机是一种概念思想,把复杂的控制逻辑分解成有限个稳定状态,组成闭环系统,通过事件触发,让状态机按设定的顺序处理事务.单片机C语言的状态机编程,是利用 ...

  6. linux编程是否含7,第7章Linux编程简介.ppt

    第7章Linux编程简介 ARM 应用系统设计第7章 Linux编程简介 佘黎煌 东北大学信息科学与工程学院 电子信息工程研究所 嵌入式Linux概况 现在 Linux 广泛用于各类计算应用,不仅包括 ...

  7. PIC单片机与PIC单片机C语言编程简介

    对于计算机学院与电子学院相关的同学来说,单片机一定不是一个陌生的概念.在大学的学习生涯中,经常用于教学的是MCS-51系列单片机.其实,除了MCS-51单片机外,还有一类单片机--PIC单片机. PI ...

  8. ARM Cortex-M3M4 处理器上的嵌入式系统编程

    ARM Cortex-M3/M4 处理器上的嵌入式系统编程 在基于 ARM Cortex M 处理器的微控制器上使用 C 编程和汇编进行动手编码 课程英文名:Embedded Systems Prog ...

  9. 使用Keil语言的嵌入式C编程教程(下)

    使用Keil语言的嵌入式C编程教程(下) 用8051单片机进行定时器/计数器的计算与编程 延迟是应用软件开发中的重要因素之一.然而,在实现定时延迟的过程中,正常的延迟并不能给出克服这一问题的宝贵结果. ...

最新文章

  1. extjs grid renderer用法
  2. 04-String课后动手动脑
  3. 主题简介 ASP .NET
  4. C/C++学习之路: 模板和异常
  5. Android 在布局容器中动态添加控件
  6. 求水仙花_500万株水仙花争相绽放,这个日本小岛人少冬天还能看花!| 日本淡路岛...
  7. Spark入门(十三)之分组求平均值
  8. CodeForces - 1152B二进制+思维
  9. 前端学习(1360) :学生档案信息管理2
  10. 2020云栖大会编程限时抢答赛 - 早中晚3场题解
  11. 测试开发——flask视图函数与路由 实战重点
  12. C语言函数大全 chm含示例
  13. RJ45-线序 586B 白橙 橙 白绿 蓝 白蓝 绿 白棕 棕
  14. 文件系统测试工具IOZONE
  15. 为什么《百家讲坛》上的中学教师收视率最高?
  16. 舒淇放下黎明战胜抑郁 自称没责任感且不会结婚q1h
  17. 新手转行学大数据开发难吗?新手学需要注意的6个方面!
  18. python:pandas全DataFrame查询定位赋值数值所在行列
  19. 图像处理------图像加噪
  20. linux命令--dmesg

热门文章

  1. 509.斐波那契数列
  2. 学校计算机的使用英语作文180字,计算机(Computer)
  3. c语言中位数怎么求,C计算中位数参考
  4. java监听微信_fastweixin: 极其方便的实现微信公众平台服务端开发,2行代码完成服务器绑定,3行代码实现用户消息监听...
  5. 生产车间问题频发如何解决?
  6. MATLAB数学实验——Jacobi迭代法Gauss-Seidel迭代法
  7. 解Invalid character found in the request target.
  8. 智多星骗子行为让人发指
  9. springboot花店商城系统鲜花店系统含后台管理源码
  10. formality软件使用教程