嵌入式组件-----状态机
此篇文章在2023年7月10日被记录
1、简述状态机
在C语言编程中,可以使用状态机来实现复杂的控制流程和状态管理。
状态机通常由以下几个组成部分构成:
- 状态(State):系统或程序可能处于的不同状态,例如"待机"、"运行"、"暂停"等。每个状态代表了系统或程序的一种行为或模式。
- 事件(Event):触发状态转换的信号或条件,可以是外部输入、定时器到期、传感器触发等。事件发生时,状态机将根据当前状态和事件来确定下一个状态。
- 转换(Transition):状态之间的切换过程,由事件触发。每个转换定义了从一个状态到另一个状态的条件和操作。
- 动作(Action):状态转换时执行的操作或任务,例如更新变量、调用函数、发送消息等。动作可以在转换时执行,也可以在进入或离开某个状态时执行。
这张图阐述了一个洗衣机的状态机,主要分为六个子状态,分别是模式选择状态、进水状态、漂洗状态、甩干状态、完成状态与暂停状态,其中模式选择状态完成后可以进入洗衣状态(进水->漂洗->甩干->完成)也可以直接进入甩干状态,这就是状态转换,在洗衣服的过程中,可能会接收到暂停按键被按下的事件,因此要对暂停事件进行处理,这就是事件,在每种子状态下,又有些任务需要不断完成,这就是动作.
2、状态机实现
我们可以使用if else 或者switch case来实现,但是我们更希望将它设计成一个组件,来实现多种情况下的移植使用
状态机节点
状态机中的每个状态都有进入、退出、运行、事件四个元素,使用回调函数的方式实现:
typedef void (*FUN_FSM_WORK)(FSM_STATE *fsm);
typedef void (*FUN_FSM_EVENT)(FSM_STATE *fsm, uint32_t event, uint32_t param);
typedef struct
{
void (*init)(FSM_STATE *fsm);
void (*exit)(FSM_STATE *fsm);
FUN_FSM_WORK work;
FUN_FSM_EVENT event;
} FSM_NODE;
一个状态机可以拥有多个状态,可以将子状态使用数组保存起来,并记录每个状态所对应的ID号
typedef struct __FSM_STATE
{
const FSM_NODE **NODE;
const uint16_t num; // 状态数量
uint16_t state; // 当前所在状态
uintptr_t param; // 传参
} FSM_STATE;
一个状态机包括初始化、状态转移、状态任务运行、事件响应四个函数:
void FSM_INIT(FSM_STATE *fsm, uint16_t initState, uintptr_t param);
void FSM_TRANS(FSM_STATE *fsm, uint16_t newState);
void FSM_WORK(FSM_STATE *fsm);
void FSM_EVENT(FSM_STATE *fsm, uint32_t event, uint32_t param);
状态机初始化实现如下,实质上是运行的子状态的进入函数
void FSM_INIT(FSM_STATE *fsm, uint16_t initState, uintptr_t param)
{
ASSERT(fsm != NULL);
ASSERT(initState < fsm->num);
fsm->state = initState;
fsm->param = param;
if(fsm->NODE[fsm->state]->init)
{
fsm->NODE[fsm->state]->init(fsm);
}
}
状态转换的实现如下,主要流程为:1、运行上一个状态的退出函数 2、状态编号改变 3、运行新状态的进入函数
void FSM_TRANS(FSM_STATE *fsm, uint16_t newState)
{
ASSERT(fsm != NULL);
ASSERT(newState < fsm->num);
if (fsm->NODE[fsm->state]->exit)
{
fsm->NODE[fsm->state]->exit(fsm);
}
fsm->state = newState;
if (fsm->NODE[fsm->state]->init)
{
fsm->NODE[fsm->state]->init(fsm);
}
}
状态任务运行的实现如下,主要是循环运行当天状态的work函数
void FSM_WORK(FSM_STATE *fsm)
{
ASSERT(fsm != NULL);
if (fsm->NODE[fsm->state]->work)
{
fsm->NODE[fsm->state]->work(fsm);
}
}
事件处理的实现如下,主要是函数回调到当前子状态下的event函数;
void FSM_EVENT(FSM_STATE *fsm, uint32_t event, uint32_t param)
{
ASSERT(fsm != NULL);
if (fsm->NODE[fsm->state]->event)
{
fsm->NODE[fsm->state]->event(fsm, event, param);
}
}