状态机基本术语现态:是指当前所处的状态。 条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。 动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。 次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。 
传统有限状态机Fsm实现方法
如图,是一个定时计数器,计数器存在两种状态,一种为设置状态,一种为计时状态 设置状态 计时状态 嵌套switch /***************************************
1.列出所有的状态
***************************************/ typedef enum{ SETTING, TIMING }STATE_TYPE; /***************************************
2.列出所有的事件
***************************************/ typedef enum{UP_EVT, DOWN_EVT, ARM_EVT, TICK_EVT }EVENT_TYPE; /***************************************
3.定义和状态机相关结构
***************************************/ struct bomb { uint8_t state; uint8_t timeout; uint8_t code; uint8_t defuse_code; }bomb1; /***************************************
4.初始化状态机
***************************************/ void bomb1_init(void) { bomb1.state = SETTING; bomb1.defuse_code = 6; //0110 } /***************************************
5. 状态机事件派发
***************************************/ 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; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

状态表二维状态转换表 状态机可以分为状态和事件 ,状态的跃迁都是受事件驱动的,因此可以通过一个二维表格来表示状态的跃迁。  (*) 仅当( code == defuse_code) 时才发生到setting 的转换。 /*1.列出所有的状态*/ enum { SETTING, TIMING, MAX_STATE }; /*2.列出所有的事件*/ enum { UP_EVT, DOWN_EVT, ARM_EVT, TICK_EVT, MAX_EVT };
/*3.定义状态表*/ 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; /* the State-Table */ uint8_t state; /* the current active 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); } } /*列出所有的状态对应的事件处理函数*/ void setting_UP(EVT_TYPE evt, void* param) { if(bomb1.timeout< 60) ++bomb1.timeout; bsp_display(bomb1.timeout); }1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636412345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364 优点 各个状态面向用户相对独立,增加事件和状态不需要去修改先前已存在的状态事件函数。 可将状态机进行封装,有较好的移植性 函数指针的安全转换 , 利用下面的特性,用户可以扩展带有私有属性的状态机和事件而使用统一的基础状态机接口 typedef void (*Tran)(struct StateTableTag *me, Event const *e); / || void Bomb2_setting_ARM (Bomb2 *me, Event const *e); typedef struct Bomb
{
struct StateTableTag *me; //必须为第一个成员
uint8_t private;
}
缺点 没有实现进入退出动作。
一维状态转换表 实现原理: 
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; /* the State-Table */uint8_t cur_state; /* the current active 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 i=0;i<x;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);}}}/*************************************************************************
setting状态相关
************************************************************************/void setting_enter(EVT_TYPE evt , void* param){}void setting_exit(EVT_TYPE evt , void* param){}void setting_action(EVT_TYPE evt , void* param){}tran_evt_t set_tran_evt[]={{ARM , timing},}/*timing 状态相关*/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 优点 各个状态面向用户相对独立,增加事件和状态不需要去修改先前已存在的状态事件函数。 实现了状态的进入和退出 容易根据状态跃迁图来设计 (状态跃迁图列出了每个状态的跃迁可能,也就是这里的转换表) 实现灵活,可实现复杂逻辑,如上一次状态,增加监护条件来减少事件的数量。可实现非完全事件驱动
缺点
QP嵌入式实时框架特点 事件驱动型编程 面向对象 类和单一继承 
工具
QEP实现有限状态机Fsm 实现 
/* qevent.h ----------------------------------------------------------------*/ typedef struct QEventTag
{
QSignal sig;
uint8_t dynamic_;
} QEvent; /* qep.h -------------------------------------------------------------------*/ typedef uint8_t QState; /* status returned from a state-handler function */ typedef QState (*QStateHandler) (void *me, QEvent const *e); /* argument list */ typedef struct QFsmTag /* Finite State Machine */ {
QStateHandler state; /* current active 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
};
/* file qfsm_ini.c ---------------------------------------------------------*/ #include 'qep_port.h' /* the port of the QEP event processor */ #include 'qassert.h' /* embedded systems-friendly assertions */ void QFsm_init(QFsm *me, QEvent const *e)
{ (*me->state)(me, e); /* execute the top-most initial transition */ /* enter the target */ (void)(*me->state)(me , &QEP_reservedEvt_[Q_ENTRY_SIG]); } /* file qfsm_dis.c ---------------------------------------------------------*/ void QFsm_dispatch(QFsm *me, QEvent const *e) { QStateHandler s = me->state; /* save the current state */ QState r = (*s)(me, e); /* call the event handler */ if (r == Q_RET_TRAN) /* transition taken? */ {(void)(*s)(me, &QEP_reservedEvt_[Q_EXIT_SIG]); /* exit the source */(void)(*me->state)(me, &QEP_reservedEvt_[Q_ENTRY_SIG]);/*enter target*/ } }
实现上面定时器例子 #include 'qep_port.h' /* the port of the QEP event processor */ #include 'bsp.h' /* board support package */
enum BombSignals /* all signals for the Bomb FSM */ {
UP_SIG = Q_USER_SIG, DOWN_SIG, ARM_SIG, TICK_SIG }; typedef struct TickEvtTag
{QEvent super; /* derive from the QEvent structure */uint8_t fine_time; /* the fine 1/10 s counter */ }TickEvt;
typedef struct Bomb4Tag
{ QFsm super; /* derive from QFsm */ uint8_t timeout; /* number of seconds till explosion */uint8_t code; /* currently entered code to disarm the bomb */uint8_t defuse; /* secret defuse code to disarm the bomb */ } 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); /*--------------------------------------------------------------------------*/ /* the initial value of the timeout */ #define INIT_TIMEOUT 10 /*..........................................................................*/ void Bomb4_ctor(Bomb4 *me, uint8_t defuse) { QFsm_ctor_(&me->super, (QStateHandler)&Bomb4_initial); me->defuse = defuse; /* the defuse code is assigned at instantiation */ } /*..........................................................................*/ 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); /* transition to 'timing' */ } } return Q_IGNORED(); } /*..........................................................................*/ void Bomb4_timing(Bomb4 *me, QEvent const *e) { switch (e->sig) { case Q_ENTRY_SIG: { me->code = 0; /* clear the defuse code */ 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(); /* destroy the bomb */ } } return Q_HANDLED(); } } return Q_IGNORED(); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
优点 采用面向对象的设计方法,很好的移植性 实现了进入退出动作 合适的粒度,且事件的粒度可控 状态切换时通过改变指针,效率高 可扩展成为层次状态机
缺点
QP 实现层次状态机 Hsm简介
初始化  初始化层次状态机的实现:在初始化时,用户所选取的状态永远是最底层的状态,如上图,我们在计算器开机后,应该进入的是开始状态,这就涉及到一个问题,由最初top(顶状态)到begin 是有一条状态切换路径的,当我们设置状态为begin如何搜索这条路径成为关键(知道了路径才能正确的进入begin,要执行路径中过渡状态的进入和退出事件) void QHsm_init(QHsm *me, QEvent const *e) {Q_ALLEGE((*me->state)(me, e) == Q_RET_TRAN);t = (QStateHandler)&QHsm_top; /* HSM starts in the top state */ do { /* drill into the target... */QStateHandler path[QEP_MAX_NEST_DEPTH_]; int8_t ip = (int8_t)0; /* transition entry path index */ path[0] = me->state; /* 这里的状态为begin *//*通过执行空信号,从底层状态找到顶状态的路径*/ (void)QEP_TRIG_(me->state, QEP_EMPTY_SIG_); while (me->state != t) { path[++ip] = me->state;(void)QEP_TRIG_(me->state, QEP_EMPTY_SIG_);}/*切换为begin*/ me->state = path[0]; /* restore the target of the initial tran. *//* 钻到最底层的状态,执行路径中的所有进入事件 */ Q_ASSERT(ip < (int8_t)QEP_MAX_NEST_DEPTH_);do { /* retrace the entry path in reverse (desired) order... */ QEP_ENTER_(path[ip]); /* enter path[ip] */ } while ((--ip) >= (int8_t)0);
t = path[0]; /* current state becomes the new source */ } while (QEP_TRIG_(t, Q_INIT_SIG) == Q_RET_TRAN); me->state = t;}123456789101112131415161718192021222324252627123456789101112131415161718192021222324252627 状态切换  /*.................................................................*/QState result(Calc *me, QEvent const *e) {switch (e->sig) {youcase 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(&reday);}/*.ready为result和begin的超状态................................................*/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);}void QHsm_dispatch(QHsm *me, QEvent const *e) {QStateHandler path[QEP_MAX_NEST_DEPTH_];QStateHandler s;QStateHandler t;QState r;t = me->state; /* save the current state */do { /* process the event hierarchically... */s = me->state;r = (*s)(me, e); /* invoke state handler s */} while (r == Q_RET_SUPER); //当前状态不能处理事件 ,直到找到能处理事件的状态if (r == Q_RET_TRAN) { /* transition taken? */int8_t ip = (int8_t)(-1); /* transition entry path index */int8_t iq; /* helper transition entry path index */path[0] = me->state; /* save the target of the transition */ path[1] = t;while (t != s) { /* exit current state to transition source s... */if (QEP_TRIG_(t, Q_EXIT_SIG) == Q_RET_HANDLED) {/*exit handled? */(void)QEP_TRIG_(t, QEP_EMPTY_SIG_); /* find superstate of t */}t = me->state; /* me->state holds the superstate */} . . .}me->state = t; /* set new state or restore the current state */} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71

t = path[0]; /* target of the transition */if (s == t) { /* (a) check source==target (transition to self) */ QEP_EXIT_(s) /* exit the source */ ip = (int8_t)0; /* enter the target */ } else { (void)QEP_TRIG_(t, QEP_EMPTY_SIG_); /* superstate of target */ t = me->state; if (s == t) { /* (b) check source==target->super */ ip = (int8_t)0; /* enter the target */ } else { (void)QEP_TRIG_(s, QEP_EMPTY_SIG_); /* superstate of src */ /* (c) check source->super==target->super */ if(me->state == t) { QEP_EXIT_(s) /* exit the source */ ip = (int8_t)0; /* enter the target */ } else { /* (d) check source->super==target */ if (me->state == path[0]) { QEP_EXIT_(s) /* exit the source */ } else { /* (e) check rest of source==target->super->super..
* and store the entry path along the way */....123456789101112131415161718192021222324252627123456789101112131415161718192021222324252627 QP实时框架的组成 
- 内存管理 使用内存池,对于低性能mcu,内存极为有限,引入内存管理主要是整个架构中,是以事件作为主要的任务通信手段,且事件是带参数的,可能相同类型的事件会多次触发,而事件处理完成后,需要清除事件,无法使用静态的事件,因此是有必要为不同事件创建内存池的。 对于不同块大小的内存池,需要考虑的是每个块的起始地址对齐问题。在进行内存池初始化时,我们是根据blocksize+header大小来进行划分内存池的。假设一个2字节的结构,如果以2来进行划分,假设mcu 4字节对齐,那么将有一半的结构起始地址无法对齐,这时需要为每个块预留空间,保证每个块的对齐。  - 事件队列 
- 事件派发 - 定时事件 非有序链表  合作式调度器QV  可抢占式调度器QK
QP nano的简介 完全支持层次式状态嵌套,包括在最多4 层状态嵌套情况下,对任何状态转换拓扑的可保 证的进入/ 退出动作 支持高达8 个并发执行的,可确定的,线程安全的事件队列的活动对象57 支持一个字节宽( 255 个信号)的信号,和一个可伸缩的参数,它可被配置成0 (没有参 数), 1 , 2 或4 字节 使用先进先出FIFO排队策略的直接事件派发机制 每个活动对象有一个一次性时间事件(定时器),它的可配置动态范围是0(没有时间事 件) , 1 , 2 或4 字节 内建的合作式vanilla 内核 内建的名为QK-nano 的可抢占型RTC内核(见第六章6.3.8节) 带有空闲回调函数的低功耗架构,用来方便的实现节省功耗模式。 在代码里为流行的低端CPU架构的C编译器的非标准扩展进行了准备(例如,在代码空 间分配常数对象,可重入函数,等等) 基于断言的错误处理策略 代码风格     
|