程序小白
认证:优质创作者
所在专题目录 查看专题
【第一章】有限状态机理论速成笔记
【第二章】HSM层次式状态机理论(进阶版)
【第三章】有限状态机(FSM)之炸弹项目(实战1)
【第四章】有限状态机(FSM)之炸弹项目(实战2)
【第五章】有限状态机(FSM)之炸弹项目(实战3)
【第六章】层次式状态机HSM = 有限状态机FSM + 面向对象
作者动态 更多
小小调度器——开局default,优雅总藏于细节之中
04-02 09:23
小小调度器——调度器核心的纯C实现
04-01 14:09
小小调度器——原来C代码还能这么写,原作者没少挨打吧!
03-27 15:42
解析由系统库引发的hardfault血案(RT-Thread+ESP32)
2023-07-17 16:04
TINA瞬时现象仿真分析——运放缓冲器振铃
2023-02-28 11:38

【第三章】有限状态机(FSM)之炸弹项目(实战1)

括号里面是本文的副标题,跟着作者本来是想搞明白QP是如何实现有限状态机的,结果万万没想到,其实作者的目的不纯,他不光想让搞明白QP的前世,还想让你的内功心法(C语言)在实战中进阶(好文不多,建议大家读一下原著,或许才能体会到作者的良苦用心,大部分书籍只是教你招式,让你感悟心法的并不多,感谢作者)。

下面还是由一个书中提到的定时炸弹的例子开启我们的侃大山模式,话说一个定时炸弹都有哪些功能,你能想到的:

      1.他得能倒计时,时间到,一声 bomb~!!!

      2.要倒计时 ,得有个定时时间 ,timer。

      3.有了定时时间,得支持定时功能吧 ,得有UP/DOWM按键进行timer调整。

      4.有了定时模式,你得切换到倒计时模式吧,这里有ARM按键,按下开启倒计时。

      5.我还想让这个炸弹有意思一点,给人一定的机会,提供一个炸弹解除密码吧(友情提示,剪线直接炸给你看~!)

      6.设置一个初始密码,你可以通过上下按键进行移位输入密码,输入完成按ARM键,这个时候如果输入正确,那么炸弹解除倒计时,进入定时模式(危险解除),如果输入错误,那么也不会立即爆炸 ,可以重新尝试,毕竟猜对密码难嘛。

      7.有了这一些内容,你还需要以一块显示屏,管理倒计时和输入密码的显示,基本就这些了,炸弹的原型就出来了:

如何构建状态机,一千个人有一千种状态机设计思路(可能),并没有标准答案,我总结了一个通用的黄金规则,简单的情况可以copy规则,直接生成状态机,复杂的情况还是需要认真的思考,不断地改进模型,打磨出更完美的设计,如下:

1. 一个项目中需要创建多个状态机,状态机的本质是管理资源,什么是资源,内存 IO 键盘、显示器等等,也可以是虚拟的资源,例如状态机扩展变量,当然一个状态机不一定只能管理一种资源,但当你不知道该怎么组合的时候,那么就一个状态机,一个资源,状态机会多一些,但没什么问题。

2.一个状态机会在多种状态之间进行切换,那么如何定义到底有几个状态,很简单,这取决于对事件的反应不同,有几种不同的动作就构造几个状态,相同的动作就放在一个父状态里(HSM中)。

这个状态机很简单,只需要创建一个状态机(bomb),两个状态setting和timming状态,再加一个伪状态(炸了状态)就实现了,状态图如下:

基于这个项目,我们提供三个版本的实现让我们的C彻底进阶:

初级版:

               1. 先定义状态机主体和状态定义:

               2. 定义事件主体和事件定义:

            3.一个状态机还需要三个函数来帮助他实现自身的功能,构造函数,初始化函数和事件分发处理函数,声明和定义如下:            

4.接下来我们来写一下main.c的核心部分,如下:

到这里整个方案的代码的核心部分就全了,这里你可以思考一下这么写的优点和缺点,想想如何改进,想10秒再往下看(答案固然重要,独立思考更重要),倒计时:

10

想一想优点

9

想一想缺点,别急

8

假如是你,你该如何改进它

7

好了,揭晓我的答案(不一定准确)

654321~!

总结,先说说这么写的优点:

1.符合我们对于事物的思考方式,你可以很快掌握他是符合运行,没有太大的难度。

2.他并没有用太多的复杂的设计方法,例如使用指针,复杂的类型变化,算法优化,不需要工程师太深的基础就能掌握。

3.基于他的设计方式,你可以很快上手,来改进自己的项目,让他看起来更加简洁明了。

接下来我们来说说他的缺点:

1.首先我们看到void Bomb1_dispatch(Bomb1 *me, Event const *e)这个函数的实现有点长,我们知道软件的设计的规则中,有一条让你写的函数尽可能的短小,简单明了,这里只有两个状态,三个事件就已经占了这么大的篇幅了,再加几个还得了~!

2.第二点,这并不是一个很好的构架,因为不论以后你怎么扩展这个状态机,你的主战场都是盯着这个dispatch函数,哪怕你可以把处理封装成函数,让它调用,但是它就好像是树根一样,每长一片叶子,这个树干都要做一些处理,这并不是一个很好的构架,从事多年程序设计的人员应该深有体会,慢慢的他会随着需求变得不再简单明了,远离我们的初衷。

片花:我们的初衷,dispatch不应该是这样,他只是需要传入一个状态机和事件,然后把控制权转交给这个状态机,等待处理完以后,控制权再回到dispatch函数中,这里可能会涉及一个课题,如何把状态机从dispatch中剥离出来,所谓的剥离并不是完全独立,而是让所有的状态机可以拥有一个dispatch,而不需要植入任何和自己相关的状态机代码,思想上升,如何设计一个通用的dispatch(事件处理器,简称QEP,先引出核心课题),下一篇我们开启进阶之路2。

声明:本内容为作者独立观点,不代表电子星球立场。未经允许不得转载。授权事宜与稿件投诉,请联系:editor@netbroad.com
觉得内容不错的朋友,别忘了一键三连哦!
赞 7
收藏 7
关注 117
成为作者 赚取收益
全部留言
0/200
  • chaochao1545 2020-09-05 19:03
    感谢分享!
    回复