Stateflow状态机建模:开关控制LED灯状态
汇聚博主多年工程经验的新书《Simulink嵌入式开发实战》现已上市,针对Simulink与MBD开发技术,形成了一套完整的知识体系和方法论。详细介绍和优惠链接可以参考博客《Simulink嵌入式开发实战》新书上市,感谢粉丝长期以来的支持!
文章目录
- 1 引入
- 2 Stateflow状态机的概念
- 3 状态机建模:开关控制LED灯状态
- 5 总结
1 引入
虽然之前写过一篇状态机的博客《Simulink算法建模: 状态机基础》,但是没有做更深入的学习。博主准备以LED灯开关为例,重新研究一下状态机建模中的一些常用技术点,形成一个相对完整的系列。
本文内容基于Matlab 2024b。
2 Stateflow状态机的概念
在嵌入式软件开发中,常常会遇到与“状态”和“流转”相关的场景。例如,汽车刚刚启动时处于【静止】状态,挂挡并踩下油门后会进入到【行驶】状态。在按下开关后,LED灯会进入【点亮】状态,再按一下开关会进入【熄灭】状态。
用传统的if-else或switch 语句去描述这些与状态相关的行为,代码往往会变得非常臃肿且出现大量的条件嵌套。通过Simulink中的Stateflow状态机建模,可以直观、清晰地描述系统的状态、以及状态之间的转换过程。Stateflow状态机由以下几个核心组成部分:
- 状态(State):系统在某一时刻所处的情况(如汽车“静止”、小灯“亮”)。
- 事件或条件(Event/Condition):引发状态变化的外部或内部触发因素(如“踩下油门”、“按下开关”)。
- 转移(Transition):系统从一个状态切换到另一个状态的过程。
- 动作(Action):进入状态、状态保持或退出状态时执行的代码或行为。
Stateflow集成在Simulink中,通过Chart模块的形式插入到 Simulink 模型中,通过输入端口接收 Simulink 中来的信号,通过输出端口输出控制命令、标志信号、状态量等。Stateflow可以弥补Simulink其他模块难以处理复杂控制逻辑的缺点,在下文的建模示例中可以直观地理解这些概念。
3 状态机建模:开关控制LED灯状态
在Simulink中添加一个Chart模块,如图所示。模块中央包含2个矩形和2个箭头,看起来就像是两个状态以及互相之间的跳转。
双击Chart模块进入模块内部,在其中可以进行状态机相关建模。在Chart模块内的上方的组件栏,包含了建模常用的子模块,如状态、跳转、真值表等。
点击组件栏中的Insert State,并拖入主界面,可以创建OFF和ON状态,如图所示。其中,状态名称需要用户自己定义,位于状态框的左上角。
注意第一个拖动出来的状态框图包含一个指向自身的箭头,该箭头表示在模型运行的第一个周期状态机默认跳转到这个状态。
在两个状态框图内分别用entry关键字定义进入动作,进入OFF和ON状态时分别将变量LED_State赋值为0和1,表示LED灯的亮和灭,如图所示。
这里的entry动作表示,在第一次进入该状态的周期内,执行下面的语句。entry也可简写成en。类似的动作还有during和exit动作,分别表示处于状态内和离开状态执行的语句。接着用鼠标在状态框图的边缘拖出2条转移线,表示建立2个状态之间的转移,如图所示。
建立的转移上并没有任何的条件或事件,需要在上面添加按下按钮的条件,如图所示。
转移上的方括号表示其中是跳转的条件,这里定义为Key_Pressed,表示按钮按下。状态机的搭建已经完成,其中包含2个状态OFF和ON,跳转条件Key_Pressed,互相之间的转移(带箭头的线)以及进入状态的动作。接下来,需要定义LED_State和Key_Pressed变量的属性。首先点击上面建模工具的Model Explorer,打开模型浏览器,如图所示。
在模型浏览器的左侧选中Chart模块时,上方会对应出现添加变量以及事件按钮,如图所示。这里需要通过添加变量按钮,分别定义按下按钮的条件Key_Pressed以及LED灯状态LED_State。
在中间的变量配置面板中需要分别配置变量名Name,变量类型Scope和数据类型DataType属性。Key_Pressed和LED_State的类型分别为Input和Output,代表是状态机的输入和输出。其类型都是boolean,即按钮和LED灯都只有开和关两种状态,如图所示。
定义完成后,返回到Chart模块外部。Chart模块自动变成一个输入端口和一个输出端口,端口名分别为Key_Pressed和LED_State,如图所示。
接下来通过输入和输出来验证状态机的效果。首先在Chart模块的输入端口,用Pulse Generator模块模拟按下按钮的过程,使用Unitdelay模块和逻辑运算模块来检测按下的上升沿时刻,如图所示。
该输入逻辑表明,当Pulse Generator本周期为1,且上周期为0的时候,Chart模块输入的Key_Pressed为1。该方法表明无论按下按钮多久,都只检测从0到1的那一个周期作为跳转的条件。双击Pulse Generator模块配置其中的参数如图所示,模拟多次按下开关。
在Chart模块的输出端口用Scope模块观测Pulse Generator模块的输出、Key_Pressed输入和LED_State的输出,如图所示。
参照2.3.1节的内容,将模型配置为固定步长、离散求解器,以及运行周期为0.01s,如图所示。
运行模型仿真,双击Scope模块,可以观察到各个信号如图所示。
输出结果可以结合状态机内部逻辑分析。PulseGenerator信号在1-2s、3-4s、5-6s、7-8s、9-10s时为1,表示这些时间段按下了开关,并且在第2、4、6、8、10时刻松手,开关回弹到0。由于模型中使用了Unitdelay模块检测上升沿,所以Key_Pressed只在1、3、5、7、9时刻产生一个周期置位为1,其余时候都处于0。在Chart模块内部,0时刻时通过默认跳转跳到OFF状态,如图所示。
在1、3、5、7、9s时,由于Key_Pressed产生了一个周期的置位1,激活了状态机内部的跳转条件,如图所示。因此,状态从OFF跳转到ON,或者从ON跳转到OFF。输出的LED_State表现为数值从0和1之间互相跳转,0表示状态OFF,1表示状态ON。
该状态机以状态跳转、条件、动作等概念,简单直观地描述了开关和LED灯的状态之间的关系,免去了if-else条件判断的复杂和繁琐。
注意如果直接将Pulse Generator输出的方波信号输入到Chart模块的Key_Pressed端口,在Pulse Generator置为1的一段时间内,状态机会在每个周期都满足[Key_Pressed]跳转条件,会在OFF和ON之间来回地跳转。显然这不符合开关控制LED灯的逻辑,因此只判断上升沿触发转移。
5 总结
本文以开关控制LED灯为例,研究了状态机的基本概念和建模方法。后续会继续以LED灯为例进行深入的学习。
>>返回个人博客总目录
