type
status
date
slug
summary
tags
category
icon
password
状态机的概念
概念
状态机(State Machine)是一种行为模型,描述了一个对象(在嵌入式系统中,通常是程序或模块)在其生命周期内所经历的所有可能状态,以及状态之间转换的条件和行为。 简单来说,它将复杂的逻辑分解为一组定义明确的状态,并通过事件触发状态之间的切换。
在嵌入式中的应用
在资源受限的 STM32 或者其他嵌入式系统中,状态机是一种非常有效的设计方法,用于管理程序的复杂性和响应各种事件。 它提供了一种结构化的方式来组织代码,使其更易于理解、维护和调试。
理解
有限的世界: 状态机将嵌入式系统的行为限制在一个预先定义好的有限状态集合内。 这意味着你可以穷举系统所有可能的状态和状态之间的转换,从而降低出现意外行为的可能性。
事件驱动的响应: 状态机对外部事件(例如,按键按下、传感器触发、数据接收)做出反应。 每个状态定义了在接收到特定事件时应该执行的操作以及要转换到的下一个状态。
状态转换逻辑的清晰表达: 通过状态转移图或者状态转移表,可以清晰地表达系统在不同状态下的行为,以及状态之间转换的条件。
降低复杂性,提高可维护性: 将复杂的控制逻辑分解成多个状态,每个状态负责处理一部分任务,可以显著降低代码的复杂性,并提高代码的可读性和可维护性。 模块化的设计也使得修改和扩展变得更加容易。
Example
按键扫描: 状态机可以用于检测按键是否被按下、释放以及长按等状态。状态包括:IDLE (空闲), PRESSED (按下), RELEASED (释放), LONG_PRESSED (长按)。
电机控制: 状态机可以用于控制电机的启动、停止、加速、减速等状态。状态包括:STOPPED (停止), STARTING (启动), RUNNING (运行), DECELERATING (减速)。
通信协议处理: 状态机可以用于处理复杂的通信协议,例如 Modbus、TCP/IP 等。每个状态对应协议处理的特定阶段。
简单代码实战
状态切换框图:

`FSM`即表示状态机
代码示例:
实现描述:
三个状态:state1,state2,state3.
四个事件:event1-4
event1发生时,状态从state1进入state2;
event2发生时,状态从state2进入state3;
event3发生时,状态从state3进入state4;
event4发生时,状态从state1进入state3;
每个状态内部有三个函数:enty( ),do( ),exit( ).每次执行切换时,执行例如state1切到state2,执行一此A的exit( )和B的entry( )。循环执行本状态时,只执行本状态do( )。
对于不同应用需要修改的是,`状态表` 、`事件表` 和`不同状态`这三个值。
自我总结(先研究定义,再研究具体运行)

首先,从代码的定义部分来看: 
- FSM_t可以用来描述一个完整的状态机,内部有四个属性 :当前状态,下一个状态(初始时未知,用state_end表示未定义或未知),动作表,事件表。
- 所有代码片段中,使用了两个枚举用于表述当前状态和事件
state和event
typedef enum
{
EVENT1=0,
EVENT2,
EVENT3,
EVENT4,
EVENT_MAP_END //事件遍历的边界,并表示未知
}EVENT_t;//事件表
typedef enum
{
state1=0,
state2,
state3,
state_end
}STATE_t;//状态表
当前状态和当前事件

- 关于事件表和动作表的构成
结构体
行为结构体内部描述了:当前状态ID ,三个动作函数【进入,执行,离开】。事件切换时,执行A的离开,B的进入。
事件的结构体内部描述了:当前事件ID,对应的状态ID,下一个状态ID。
精华的部分(个人觉得):
通过两个数组封装状态和事件,直接管理动作和状态表:
结构体数组 action 和 event
以上主要是构建FSM状态机和对应的状态和行为表联系起来。

其次,研究比较重要的函数实现:
FSM_init(void)原型
函数解析
FSM_init 初始化函数,形象的描述:定位到第一个状态,联系状态和行为表。
FSM_state_transfer(FSM_t* pFsm,EVENT_t stEventID)原型
函数解析
参数:状态机的ID,事件ID
内部实现:
1).if(pFSM->pEventMAP[i].stEventID<EVENT_MAP_END)
遍历状态机的事件表并获取表内的事件ID,EVENT_MAP_END枚举为4。也就是说i的最大值加到3。刚好遍历了event1-event4.
2).在1)的实现下
if((stEventID==pFSM->pEventMAP[i].stEventID) && (pFSM->stCurState==pFSM->pEventMAP[i].stCurState))
参数的事件ID与遍历的事件ID相匹配,状态机的当前状态ID和事件表中的当前状态ID相匹配.
3).在2)下,pFsm->stNextState = pFsm->pEventMap[i].stNextState
使状态机的下一个状态ID等于事件表中的下一个ID。
综上,该函数实现:输入状态机ID,当前状态当前事件ID,可以得出状态机下一个状态ID。
简而言之,根据事件表,匹配出下个状态ID。
action_perform(FSM_t* pFsm)原型
函数解析
输入状态机ID,如果状态机ID为准确值,说明事件发生,退出A,进入B。否则事件未发生,执行本状态函数。
最后,看在main函数中的实际应用:
main.c
i的值0-10依次循环,switch调用FSM_state_transfer函数启动状态切换。

- 作者:乌龟の笔记
- 链接:https://tangly1024.com/article/%E7%8A%B6%E6%80%81%E6%9C%BA%E7%9A%84%E5%88%9D%E6%AD%A5%E8%AE%A4%E8%AF%86
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。



