
嵌入式状态机编程QP框架与实现方法深度解析1. 状态机基础概念1.1 状态机基本术语状态机是嵌入式系统开发中常用的编程范式其核心概念包括现态(Current State)系统当前所处的状态条件(Event)触发状态迁移或动作执行的外部事件动作(Action)条件满足后执行的具体操作次态(Next State)条件满足后系统将迁移到的新状态状态迁移过程中动作执行是可选的。系统可以直接迁移到新状态而不执行任何操作。2. 传统有限状态机实现方法2.1 定时计数器案例以一个定时计数器为例系统包含两种状态设置状态(SETTING)/-按键调整初始倒计时值确认键启动计时切换到计时状态计时状态(TIMING)/-输入密码1-0共4位确认键验证密码正确则停止计时错误则继续计时至零2.2 嵌套switch实现typedef enum { SETTING, TIMING } STATE_TYPE; typedef enum { UP_EVT, DOWN_EVT, ARM_EVT, TICK_EVT } EVENT_TYPE; struct bomb { uint8_t state; uint8_t timeout; uint8_t code; uint8_t defuse_code; } bomb1; void bomb1_init(void) { bomb1.state SETTING; bomb1.defuse_code 6; // 0110 } void bomb1_fsm_dispatch(EVENT_TYPE evt, void* param) { switch(bomb1.state) { case SETTING: { switch(evt) { case UP_EVT: // 按键 if(bomb1.timeout 60) bomb1.timeout; BSP_display(bomb1.timeout); break; case DOWN_EVT: // -按键 if(bomb1.timeout 0) --bomb1.timeout; BSP_display(bomb1.timeout); break; case ARM_EVT: // 确认键 bomb1.state TIMING; bomb1.code 0; break; } } break; case TIMING: { switch(evt) { case UP_EVT: // 按键 bomb1.code (bomb1.code 1) | 0x01; break; case DOWN_EVT: // -按键 bomb1.code (bomb1.code 1); break; case ARM_EVT: // 确认键 if(bomb1.code bomb1.defuse_code) { bomb1.state SETTING; } else { BSP_display(bomb!); } break; case TICK_EVT: if(bomb1.timeout) { --bomb1.timeout; BSP_display(bomb1.timeout); } if(bomb1.timeout 0) { BSP_display(bomb!); } break; } } break; } }优点代码结构简单直观逻辑连贯易于理解缺点状态/事件增多时需频繁修改代码缺乏封装移植性差未实现状态的进入/退出操作3. 状态表实现方法3.1 二维状态转换表enum { SETTING, TIMING, MAX_STATE }; enum { UP_EVT, DOWN_EVT, ARM_EVT, TICK_EVT, MAX_EVT }; typedef void (*fp_state)(EVT_TYPE evt, void* param); static const fp_state bomb2_table[MAX_STATE][MAX_EVENT] { {setting_UP, setting_DOWN, setting_ARM, NULL}, {setting_UP, setting_DOWN, setting_ARM, timing_TICK} }; struct bomb_t { const fp_state const *state_table; /* 状态表 */ uint8_t state; /* 当前状态 */ uint8_t timeout; uint8_t code; uint8_t defuse_code; }; struct bomb bomb2 { .state_table bomb2_table }; void bomb2_init(void) { bomb2.defuse_code 6; // 0110 bomb2.state SETTING; } void bomb2_dispatch(EVT_TYPE evt, void* param) { fp_state s NULL; if(evt MAX_EVT) { LOG(EVT type error!); return; } s bomb2.state_table[bomb2.state * MAX_EVT evt]; if(s ! NULL) { s(evt, param); } }优点状态相对独立扩展性强可封装状态机提高移植性支持函数指针安全转换缺点函数粒度小逻辑分散仍未实现进入/退出动作3.2 一维状态转换表typedef void (*fp_action)(EVT_TYPE evt, void* param); /* 转换表基础结构 */ struct tran_evt_t { EVT_TYPE evt; uint8_t next_state; }; /* 状态描述 */ struct fsm_state_t { fp_action enter_action; // 进入动作 fp_action exit_action; // 退出动作 fp_action action; tran_evt_t* tran; // 转换表 uint8_t tran_nb; // 转换表大小 const char* name; }; /* 状态表 */ #define ARRAY(x) x, sizeof(x)/sizeof(x[0]) const struct fsm_state_t state_table[] { {setting_enter, setting_exit, setting_action, ARRAY(set_tran_evt), setting}, {timing_enter, timing_exit, timing_action, ARRAY(time_tran_evt), timing} }; /* 状态机结构 */ struct fsm { const struct state_t* state_table; /* 状态表 */ uint8_t cur_state; /* 当前状态 */ uint8_t timeout; uint8_t code; uint8_t defuse_code; } bomb3; /* 初始化状态机 */ void bomb3_init(void) { bomb3.state_table state_table; bomb3.cur_state setting; bomb3.defuse_code 8; // 1000 } /* 状态机事件派发 */ void fsm_dispatch(EVT_TYPE evt, void* param) { tran_evt_t* p_tran NULL; /* 获取当前状态的转换表 */ p_tran bomb3.state_table[bomb3.cur_state]-tran; /* 检查事件匹配 */ for(uint8_t i0; ix; i) { if(p_tran[i]-evt evt) { if(NULL ! bomb3.state_table[bomb3.cur_state].exit_action) { bomb3.state_table[bomb3.cur_state].exit_action(NULL); } if(bomb3.state_table[_tran[i]-next_state].enter_action) { bomb3.state_table[_tran[i]-next_state].enter_action(NULL); } bomb3.cur_state p_tran[i]-next_state; } else { bomb3.state_table[bomb3.cur_state].action(evt,param); } } }优点实现了状态的进入/退出操作可根据状态跃迁图设计转换表支持复杂逻辑和非完全事件驱动缺点每个状态需要多个函数需明确列出所有转换关系4. QP嵌入式实时框架4.1 框架特点QP框架是专为嵌入式系统设计的事件驱动型编程框架具有以下特点事件驱动编程遵循Dont call me; Ill call you的好莱坞原则面向对象设计支持类和单一继承工具支持QM通过UML类图描述状态机并生成C代码QS软件追踪工具4.2 QEP实现有限状态机/* qevent.h */ typedef struct QEventTag { QSignal sig; uint8_t dynamic_; } QEvent; /* qep.h */ typedef uint8_t QState; typedef QState (*QStateHandler)(void* me, QEvent const* e); typedef struct QFsmTag { QStateHandler state; /* 当前状态 */ } QFsm; #define QFsm_ctor(me_, initial_) ((me_)-state (initial_)) void QFsm_init(QFsm* me, QEvent const* e); void QFsm_dispatch(QFsm* me, QEvent const* e); #define Q_RET_HANDLED ((QState)0) #define Q_RET_IGNORED ((QState)1) #define Q_RET_TRAN ((QState)2) #define Q_HANDLED() (Q_RET_HANDLED) #define Q_IGNORED() (Q_RET_IGNORED) #define Q_TRAN(target_) (((QFsm*)me)-state (QStateHandler)(target_), Q_RET_TRAN) enum QReservedSignals { Q_ENTRY_SIG 1, Q_EXIT_SIG, Q_INIT_SIG, Q_USER_SIG };4.3 定时器案例实现#include qep_port.h #include bsp.h enum BombSignals { UP_SIG Q_USER_SIG, DOWN_SIG, ARM_SIG, TICK_SIG }; typedef struct TickEvtTag { QEvent super; uint8_t fine_time; } TickEvt; typedef struct Bomb4Tag { QFsm super; uint8_t timeout; uint8_t code; uint8_t defuse; } Bomb4; void Bomb4_ctor(Bomb4* me, uint8_t defuse); QState Bomb4_initial(Bomb4* me, QEvent const* e); QState Bomb4_setting(Bomb4* me, QEvent const* e); QState Bomb4_timing(Bomb4* me, QEvent const* e); #define INIT_TIMEOUT 10 void Bomb4_ctor(Bomb4* me, uint8_t defuse) { QFsm_ctor_(me-super, (QStateHandler)Bomb4_initial); me-defuse defuse; } QState Bomb4_initial(Bomb4* me, QEvent const* e) { (void)e; me-timeout INIT_TIMEOUT; return Q_TRAN(Bomb4_setting); } QState Bomb4_setting(Bomb4* me, QEvent const* e) { switch(e-sig) { case UP_SIG: { if(me-timeout 60) { me-timeout; BSP_display(me-timeout); } return Q_HANDLED(); } case DOWN_SIG: { if(me-timeout 1) { --me-timeout; BSP_display(me-timeout); } return Q_HANDLED(); } case ARM_SIG: { return Q_TRAN(Bomb4_timing); } } return Q_IGNORED(); } QState Bomb4_timing(Bomb4* me, QEvent const* e) { switch(e-sig) { case Q_ENTRY_SIG: { me-code 0; return Q_HANDLED(); } case UP_SIG: { me-code 1; me-code | 1; return Q_HANDLED(); } case DOWN_SIG: { me-code 1; return Q_HANDLED(); } case ARM_SIG: { if(me-code me-defuse) { return Q_TRAN(Bomb4_setting); } return Q_HANDLED(); } case TICK_SIG: { if(((TickEvt const*)e)-fine_time 0) { --me-timeout; BSP_display(me-timeout); if(me-timeout 0) { BSP_boom(); } } return Q_HANDLED(); } } return Q_IGNORED(); }优点面向对象设计移植性好实现了进入/退出动作状态切换效率高指针操作可扩展为层次状态机缺点事件定义和粒度控制是设计难点需要合理设计事件类型和参数5. QP层次状态机(Hsm)实现5.1 初始化过程层次状态机的初始化需要从顶状态(Top)到底层目标状态建立完整路径void QHsm_init(QHsm* me, QEvent const* e) { Q_ALLEGE((*me-state)(me, e) Q_RET_TRAN); t (QStateHandler)QHsm_top; do { QStateHandler path[QEP_MAX_NEST_DEPTH_]; int8_t ip (int8_t)0; path[0] me-state; (void)QEP_TRIG_(me-state, QEP_EMPTY_SIG_); while(me-state ! t) { path[ip] me-state; (void)QEP_TRIG_(me-state, QEP_EMPTY_SIG_); } me-state path[0]; Q_ASSERT(ip (int8_t)QEP_MAX_NEST_DEPTH_); do { QEP_ENTER_(path[ip]); } while((--ip) (int8_t)0); t path[0]; } while(QEP_TRIG_(t, Q_INIT_SIG) Q_RET_TRAN); me-state t; }5.2 状态切换示例QState result(Calc* me, QEvent const* e) { switch(e-sig) { case ENTER_SIG: break; case EXIT_SIG: break; case C_SIG: { printf(clear); return Q_HANDLED(); } case B_SIG: { return Q_TRAN(begin); } } return Q_SUPER(ready); } QState ready(Calc* me, QEvent const* e) { switch(e-sig) { case ENTER_SIG: break; case EXIT_SIG: break; case OPER_SIG: { return Q_TRAN(opEntered); } } return Q_SUPER(on); }6. QP实时框架组成6.1 内存管理QP使用内存池管理策略针对不同大小的事件分配固定大小的内存块并处理内存对齐问题。6.2 事件队列每个活动对象维护一个事件队列支持以下事件派发方式直接事件发送QActive_postLIFO()发布-订阅模式基于信号和优先级的事件派发6.3 定时事件支持非有序链表管理的定时事件提供合作式调度器QV。6.4 QP Nano特性QP Nano是QP框架的精简版本特点包括支持最多4层状态嵌套支持8个并发活动对象1字节宽信号(255个信号)可配置参数大小(0/1/2/4字节)每个活动对象一个定时器内置合作式vanilla内核和QK-nano抢占型RTC内核低功耗架构支持