关注、星标公众号,直达精彩内容

ID:技术让梦想更伟大

整理:李肖遥

状态机的概念

状态机是软件编程中的一个重要概念。比这个概念更重要的是对它的灵活应用。在一个思路清晰而且高效的程序中,必然有状态机的身影浮现。

比如说一个按键命令解析程序,就可以被看做状态机:本来在A状态下,触发一个按键后切换到了B状态;再触发另一个键后切换到C状态,或者返回到A状态。这就是最简单的按键状态机例子。实际的按键解析程序会比这更复杂些,但这不影响我们对状态机的认识。

进一步看,击键动作本身也可以看做一个状态机。一个细小的击键动作包含了:释放、抖动、闭合、抖动和重新释放等状态。

同样,一个串行通信的时序(不管它是遵循何种协议,标准串口也好、I2C也好;也不管它是有线的、还是红外的、无线的)也都可以看做由一系列有限的状态构成。

显示扫描程序也是状态机;通信命令解析程序也是状态机;甚至连继电器的吸合/释放控制、发光管(LED)的亮/灭控制又何尝不是个状态机。

当我们打开思路,把状态机作为一种思想导入到程序中去时,就会找到解决问题的一条有效的捷径。有时候用状态机的思维去思考程序该干什么,比用控制流程的思维去思考,可能会更有效。这样一来状态机便有了更实际的功用。

程序其实就是状态机。

也许你还不理解上面这句话。请想想看,计算机的大厦不就是建立在“0”和“1”两个基本状态的地基之上么?

状态机的要素

状态机可归纳为4个要素,即现态、条件、动作、次态。这样的归纳,主要是出于对状态机的内在因果关系的考虑。“现态”和“条件”是因,“动作”和“次态”是果。详解如下:

  1. 现态

是指当前所处的状态。

  1. 条件

又称为“事件”。当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。

  1. 动作

条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。

  1. 次态

条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

如果我们进一步归纳,把“现态”和“次态”统一起来,而把“动作”忽略(降格处理),则只剩下两个最关键的要素,即:状态、迁移条件。

状态机的表示方法有许多种,我们可以用文字、图形或表格的形式来表示一个状态机。

纯粹用文字描述是很低效的,所以就不介绍了。接下来先介绍图形的方式。

状态迁移图(STD)

状态迁移图(STD),是一种描述系统的状态、以及相互转化关系的图形方式。状态迁移图的画法有许多种,不过一般都大同小异。我们结合一个例子来说明一下它的画法,如图1所示。

图1状态迁移图
  1. 状态框

用方框表示状态,包括所谓的“现态”和“次态”。

  1. 条件及迁移箭头

用箭头表示状态迁移的方向,并在该箭头上标注触发条件。

  1. 节点圆圈

当多个箭头指向一个状态时,可以用节点符号(小圆圈)连接汇总。

  1. 动作框

用椭圆框表示。

  1. 附加条件判断框

用六角菱形框表示。

状态迁移图和我们常见的流程图相比有着本质的区别,具体体现为:在流程图中,箭头代表了程序PC指针的跳转;而在状态迁移图中,箭头代表的是状态的改变。

我们会发现,这种状态迁移图比普通程序流程图更简练、直观、易懂。这正是我们需要达到的目的。

状态迁移表

除了状态迁移图,我们还可以用表格的形式来表示状态之间的关系。这种表一般称为状态迁移表。

表1就是前面介绍的那张状态迁移图的另一种描述形式。

表1状态迁移表
  1. 采用表格方式来描述状态机,优点是可容纳更多的文字信息。例如,我们不但可以在状态迁移表中描述状态的迁移关系,还可以把每个状态的特征描述也包含在内。

  2. 如果表格内容较多,过于臃肿不利于阅读,我们也可以将状态迁移表进行拆分。经过拆分后的表格根据其具体内容,表格名称也有所变化。

  3. 比如,我们可以把状态特征和迁移关系分开列表。被单独拆分出来的描述状态特征的表格,也可以称为“状态真值表”。这其中比较常见的就是把每个状态的显示内容单独列表。这种描述每个状态显示内容的表称之为“显示真值表”。同样,我们把单独表述基于按键的状态迁移表称为“按键功能真值表”。另外,如果每一个状态包含的信息量过多,我们也可以把每个状态单独列表。

  4. 由此可见,状态迁移表作为状态迁移图的有益补充,它的表现形式是灵活的。

  5. 状态迁移表优点是信息涵盖面大,缺点是视觉上不够直观,因此它并不能取代状态迁移图。比较理想的是将图形和表格结合应用。用图形展现宏观,用表格说明细节。二者互为参照,相得益彰。

状态机思路实现一个时钟程序

接下来,我将就状态机的应用,结合流程图、状态迁移图和状态迁移,举一个实际例子。下面这张图是一个时钟程序的状态迁移图,如图2所示。

图2时钟程序状态迁移图

把这张图稍做归纳,就可以得到它的另一种表现形式——状态迁移表,如表2所示。

表2时钟程序状态迁移表

状态机应用的注意事项

基于状态机的程序调度机制,其应用的难点并不在于对状态机概念的理解,而在于对系统工作状态的合理划分。

初学者往往会把某个“程序动作”当作是一种“状态”来处理。我称之为“伪态”。那么如何区分“动作”和“状态”。本匠人的心得是看二者的本质:

  • “动作”是不稳定的,即使没有条件的触发,“动作”一旦执行完毕就结束了;

  • 而“状态”是相对稳定的,如果没有外部条件的触发,一个状态会一直持续下去。

初学者的另一种比较致命的错误,就是在状态划分时漏掉一些状态。我称之为“漏态”。

伪态和漏态 这两种错误的存在,将会导致程序结构的涣散。因此要特别小心避免。

更复杂的状态机

前面介绍的是一种简单的状态结构。它只有一级,并且只有一维,如图3所示。

图3 线性状态机结构

如果有必要,我们可以建立更复杂的状态机模型。

  1. 多级状态结构

状态机可以是多级的。在分层的多级状态机系统里面,一个“父状态”下可以划分多个“子状态”,这些子状态共同拥有上级父状态的某些共性,同时又各自拥有自己的一些个性。

在某些状态下,还可以进一步划分子状态。比如,我们可以把前面的时钟例子修改如下:

把所有和时钟功能有关的状态,合并成1个一级状态。在这个状态下,又可以划分出3个二级子状态,分别为显示时间、设置小时、设置分钟;

同样,我们也可以把所有和闹钟功能有关的状态,合并成1个一级状态。在这个状态下,再划分出4个二级子状态,分别为显示闹钟、设置“时”、设置“分”、设置鸣叫时间。

我们需要用另一个状态变量(寄存器)来表示这些子状态。

子状态下面当然还可以有更低一级的孙状态(子子孙孙无穷尽也),从而将整个状态体系变成了树状多级状态结构,如图4所示。

图4树状多级状态结构
  1. 多维状态结构

状态结构也可以是多维的。从不同的角度对系统进行状态的划分,这些状态的某些特性是交叉的。比如,在按照按键和显示划分状态的同时,又按照系统的工作进程做出另一种状态划分。这两种状态划分同时存在,相互交叉,从而构成了二维的状态结构空间。

举一个这方面的例子,如:空调遥控器,如图5所示。

图5多维状态机结构

同样,我们也可以构建三维、四维甚至更多维的状态结构。每一维的状态都需要用一个状态变量(寄存器)来表示。

无论多级状态结构和多维状态结构看上去多么迷人,匠人的忠告是:我们依然要尽可能地简化状态结构,能用单级、单维的结构,就不要给自己找事,去玩那噩梦般的复杂结构。

简单的才是最有效的。

-END-

整理文章为传播相关技术,版权归原作者所有 |

| 如有侵权,请联系删除 |

往期好文合集

嵌入式大佬 | 嵌入式C语言知识点万字总结

嵌入式牛人 | 这些单片机编程思想超硬核

嵌入式必会!C语言最常用的贪心算法就这么被攻略了

最 后

若觉得文章不错,转发分享,也是我们继续更新的动力。

5T资源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,PCB、FPGA、DSP、labview、单片机、等等

在公众号内回复「更多资源」,即可免费获取,期待你的关注~

状态机思路在嵌入式开发中的应用详解相关推荐

  1. 状态机思路在嵌入式开发中的应用说明及注意事项(普罗米修斯中不少功能节点用了状态机的方法来写)

    我感觉普罗米修斯很多功能模块里面都用到了状态机的思路 比如圆环穿越的,比如最新的二维码降落的代码. 摘自:https://mp.weixin.qq.com/s/qRmBDH-VIHM26AaNFoD9 ...

  2. IOS开发中单例模式使用详解

    第一.基本概念 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问. 第二.在IOS中使用单例模式的情 ...

  3. 3D游戏开发中的矩阵详解

    矩阵很多同学没有接触过,所以感觉很难,很复杂,其实只要学过矩阵的同学都知道,矩阵运算并不难.今天我们给大家讲讲游戏开发中的矩阵的运算. 这里有个游戏开发交流小组 大家可以一起来学习交流哦 1:矩阵是什 ...

  4. Python开发中的接口详解和使用方法

    在服务器环境实际开发过程中,往往要通过写接口来请求或者发送数据,那么到底什么是接口,如何通过调用接口发送或者请求数据呢? 一.接口的定义 简单来说,域名(地址)带上参数就是一个接口,然后通过调用此接口 ...

  5. 嵌入式开发ARM Cortex-M33处理器详解

    ARM Cortex系列处理器主要基于3大产品类型量身开发,A系列:运行复杂系统的精细高端应用:R系列:高性能硬实时系统:M系列:低功耗.确定性.成本敏感的微控制器,专门优化以满足其需求.基于ARM ...

  6. 单片机STM32在开发中常用库函数详解

    1.GPIO初始化 用法: voidGPIO_Configuration(void){GPIO_InitTypeDefGPIO_InitStructure;//GPIO状态恢复默认参数GPIO_Ini ...

  7. 嵌入式开发中常用的几种通信接口总结

    关注.星标公众号,直达精彩内容 在嵌入式系统中,板上通信接口是指用于将各种集成电路与其他外围设备交互连接的通信通路或总线. 以下内容为常用板上通信接口:包括I2C.SPI.UART.1-Wire: I ...

  8. 【嵌入式笔记】简要谈一谈嵌入式开发中重写printf的几种方法

    文章目录 抛砖引玉 1 宏定义替换 2 直接重写printf函数的实现 3 重写fputc/putchar接口 4 仿照POSIX的实现,从文件描述符0/1/2上面做文章 总结 更多分享 摘要 简单总 ...

  9. 嵌入式开发中DSP与FPGA的关系

    嵌入式开发中DSP与FPGA的关系 常所说的单片机侧重于控制,不支持信号处理,属于低端嵌入式处理器,arm可以看做是低端单片机升级版,支持操作系统管理,更多接口如网卡,处理能力更强;fpga是可编程逻 ...

最新文章

  1. mysqljs在koa2中的正确姿势
  2. 编写配置文件不能出现帮助信息
  3. 自定义类模板 重载遇到的问题
  4. linux snap安装redis-desktop-manager
  5. 计算机视觉之OpenCV教程 --- Mat图像类基础(二)
  6. discuz 不能上传头像提示can not write to the data/tmp folder
  7. ftp 服务器的目录文件,ftp服务器中文件目录下
  8. MSYS2+MinGW32 编译 QEMU需做的准备工作
  9. 三星Galaxy Z海报曝光 采用双铰链三折叠设计
  10. 【计算机网络笔记】编码与调制
  11. python中的数据存储-json
  12. java番茄钟_番茄时间管理 - java Swing版
  13. 域名转移应该怎么做?域名转入是什么意思?手把手教你将阿里云备案域名转入到腾讯云
  14. Pigeon发布流程
  15. element上传图片校验尺寸
  16. 《职场正能量》读后感
  17. google play电子市场和gmail如何安装在国产手机、三星手机、摩托手机里
  18. Synchronized你以为你真的懂?
  19. HDU 2681 MM Programming Club(miaos的线段树维护+ycy的暴力贪心)
  20. 高手常用的自我介绍套路

热门文章

  1. Cluster table import - BSP UI component source code is actually stored in cluster table
  2. 如何把SAP UI5应用部署到SAP云平台的Fiori Launchpad里去
  3. 针对SAP OData 模型Addresssable属性的不同处理
  4. SAP Cloud for Customer的Container应用设计原理
  5. 如何使用Kubernetes里的NetworkPolicy
  6. 如何把某个网站的SSL Server certificate链导入到ABAP Netweaver系统里
  7. ABAP Netweaver和Hybris里获得内存使用统计数据
  8. 最大流之最长递增子序列问题
  9. linux服务器无密码登录不了,设置ssh无密码登录linux服务器的方法
  10. java 高并发_Java 高并发之无锁(CAS)