引言
- 状态机在嵌入式系统、协议解析等场景的核心作用
- 传统
switch-case实现状态机的局限性(冗余、难以维护) - 表驱动法的优势:逻辑与数据分离,扩展性高
核心概念解析
- 状态机三要素:状态(State)、事件(Event)、动作(Action)
- 二维函数指针数组:
- 定义:
void (*FsmTableChart[MAX_STATES][MAX_EVENTS])(void*) - 索引:行代表当前状态,列代表触发事件,单元存储处理函数
- 定义:
- 蛛织网隐喻:状态转移路径如同蜘蛛网结构,函数指针表是网的“编织规则”
实现步骤
状态与事件枚举
typedef enum fsm_state
{
idle =0,
running,
pause,
stop,
}fsm_state_t;
typedef struct fsm_struct
{
bool (*check)(void); //触发事件
void (*init)(void); //执行动作
fsm_state_t fsm_next_state; //跳转状态
fsm_state_t fsm_current_state; //下一状态
}fsm_struct_t;
函数指针表定义与初始化
const fsm_struct_t FsmTableChart[][3]=
{
/*状态 分支1: 判断条件,执行动作,跳转状态 分支2: 判断条件,执行动作,跳转状态 分支3: 判断条件,执行动作,跳转状态*/
[idle] = {check_idle, init_idle,running}, {check_pause,init_pause,stop }, {check_stop, init_stop, idle},
[running] = {check_running,init_running,pause}, {check_idle, init_idle,idle }, {check_stop ,init_stop ,idle},
[pause] = {check_pause, init_pause,stop}, {check_idle, init_idle,idle }, {check_stop ,init_stop ,idle}
}
FsmTableChart[][3],行:表示状态,可以自定义多种状态。列:事件分支,三个大括号里面 可以理解为三个分支。即在该状态下,不同的检查函数触发不同的分支。类似于剧本杀多种结局,多条分支的玩法。
状态期间持续动作
定义每个状态的持续动作,这一步的思想为:当处在当前状态时就持续执行当前状态动作。比如:处于空闲状态(IDLE),持续监测信号;处于运行状态(RUNNING),持续计算PID;处于停止状态(STOP),持续监测唤醒信号等等;
void (*State_inits_Func[])()= //定义状态机当前状态执行函数,该函数数组与状态一一对应
{
acting_idle, acting_running, acting_pause, acting_stop
};
状态机引擎实现(放入轮询或者定时器)
fsm_struct_t FsmPublicTask={
.check=NULL,
.init =NULL,
.fsm_next_state =idle,
.fsm_current_state=idle,
};
void Fsm_task_Polling()
{
State_inits_Func[FsmPublicTask.fsm_current_state](); //进入循环执行
for(int i=0;i<3;i++)
{
if(FsmTableChart[FsmPublicTask.fsm_current_state][i].check != NULL)
{
if(FsmTableChart[FsmPublicTask.fsm_current_state][i].check() == true)
{
if(FsmTableChart[FsmPublicTask.fsm_current_state][i].init!= NULL)
{
FsmTableChart[FsmPublicTask.fsm_current_state][i].init();
}
FsmPublicTask.fsm_current_state= FsmTableChart[FsmPublicTask.fsm_current_state][i].fsm_next_state;
}
}
}
}
优化技巧
- 空事件处理:用
NULL指针跳过无效状态-事件组合,减少条件判断 - 分层状态机:通过多级函数指针表实现层次化状态(如HFSM)
- 动态表切换:运行时更换指针表以支持多模式状态机
应用案例
- 通信协议解析:TCP状态机(SYN_RECEIVED → ESTABLISHED)
- UI系统:按钮状态迁移(PRESSED → HOLD → RELEASED)
- 工业控制:电机控制状态(STANDBY → ACCELERATION → CRUISE)
对比分析
- 与
switch-case对比:- 代码量减少50%以上(示例:Modbus协议解析器)
- 新增状态时仅需扩展表,无需修改逻辑代码
结语
- 二维函数指针表是C语言实现高效状态机的“银弹”
- 鼓励在资源敏感型项目中替代传统条件分支结构
- 第一次发帖有错误恳请各位友友指正


449

被折叠的 条评论
为什么被折叠?



