狀態(tài)機(jī)基本術(shù)語
現(xiàn)態(tài):是指當(dāng)前所處的狀態(tài)。條件:又稱為“事件”,當(dāng)一個(gè)條件被滿足,將會觸發(fā)一個(gè)動(dòng)作,或者執(zhí)行一次狀態(tài)的遷移。動(dòng)作:條件滿足后執(zhí)行的動(dòng)作。動(dòng)作執(zhí)行完畢后,可以遷移到新的狀態(tài),也可以仍舊保持原狀態(tài)。
動(dòng)作不是必需的,當(dāng)條件滿足后,也可以不執(zhí)行任何動(dòng)作,直接遷移到新狀態(tài)。次態(tài):條件滿足后要遷往的新狀態(tài)?!按螒B(tài)”是相對于“現(xiàn)態(tài)”而言的,“次態(tài)”一旦被激活,就轉(zhuǎn)變成新的“現(xiàn)態(tài)”了。
傳統(tǒng)有限狀態(tài)機(jī)Fsm實(shí)現(xiàn)方法
????
如圖,是一個(gè)定時(shí)計(jì)數(shù)器,計(jì)數(shù)器存在兩種狀態(tài),一種為設(shè)置狀態(tài),一種為計(jì)時(shí)狀態(tài)
設(shè)置狀態(tài)
“+” “-” 按鍵對初始倒計(jì)時(shí)進(jìn)行設(shè)置當(dāng)計(jì)數(shù)值設(shè)置完成,點(diǎn)擊確認(rèn)鍵啟動(dòng)計(jì)時(shí) ,即切換到計(jì)時(shí)狀態(tài)
計(jì)時(shí)狀態(tài)
按下“+” “-” 會進(jìn)行密碼的輸入?!?”表示1 ,“-”表示輸入0 ,密碼共有4位
確認(rèn)鍵:只有輸入的密碼等于默認(rèn)密碼,按確認(rèn)鍵才能停止計(jì)時(shí),否則計(jì)時(shí)直接到零,并執(zhí)行相關(guān)操作
嵌套switch
/*************************************** 1.列出所有的狀態(tài) ***************************************/ typedef enum{ SETTING, TIMING }STATE_TYPE; /*************************************** 2.列出所有的事件 ***************************************/ typedef enum{ UP_EVT, DOWN_EVT, ARM_EVT, TICK_EVT }EVENT_TYPE; /*************************************** 3.定義和狀態(tài)機(jī)相關(guān)結(jié)構(gòu) ***************************************/ struct bomb { uint8_t state; uint8_t timeout; uint8_t code; uint8_t defuse_code; }bomb1; /*************************************** 4.初始化狀態(tài)機(jī) ***************************************/ void bomb1_init(void) { bomb1.state = SETTING; bomb1.defuse_code = 6; //0110 } /*************************************** 5. 狀態(tài)機(jī)事件派發(fā) ***************************************/ 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: // "確認(rèn)" 按鍵按下事件 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: // "確認(rèn)" 按鍵按下事件 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; } }
優(yōu)點(diǎn):
簡單,代碼閱讀連貫,容易理解
缺點(diǎn):
當(dāng)狀態(tài)或事件增多時(shí),代碼狀態(tài)函數(shù)需要經(jīng)常改動(dòng),狀態(tài)事件處理函數(shù)會代碼量會不斷增加
狀態(tài)機(jī)沒有進(jìn)行封裝,移植性差。
沒有實(shí)現(xiàn)狀態(tài)的進(jìn)入和退出的操作。進(jìn)入和退出在狀態(tài)機(jī)中尤為重要
進(jìn)入事件:只會在剛進(jìn)入時(shí)觸發(fā)一次,主要作用是對狀態(tài)進(jìn)行必要的初始化
退出事件:只會在狀態(tài)切換時(shí)觸發(fā)一次 ,主要的作用是清除狀態(tài)產(chǎn)生的中間參數(shù),為下次進(jìn)入提供干凈環(huán)境
狀態(tài)表
二維狀態(tài)轉(zhuǎn)換表
狀態(tài)機(jī)可以分為狀態(tài)和事件 ,狀態(tài)的躍遷都是受事件驅(qū)動(dòng)的,因此可以通過一個(gè)二維表格來表示狀態(tài)的躍遷。
(*) 僅當(dāng)( code == defuse_code) 時(shí)才發(fā)生到setting 的轉(zhuǎn)換。
/*1.列出所有的狀態(tài)*/ enum { SETTING, TIMING, MAX_STATE }; /*2.列出所有的事件*/ enum { UP_EVT, DOWN_EVT, ARM_EVT, TICK_EVT, MAX_EVT }; /*3.定義狀態(tài)表*/ 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); } } /*列出所有的狀態(tài)對應(yīng)的事件處理函數(shù)*/ void setting_UP(EVT_TYPE evt, void* param) { if(bomb1.timeout< 60) ++bomb1.timeout; bsp_display(bomb1.timeout); }
優(yōu)點(diǎn):
各個(gè)狀態(tài)面向用戶相對獨(dú)立,增加事件和狀態(tài)不需要去修改先前已存在的狀態(tài)事件函數(shù)。
可將狀態(tài)機(jī)進(jìn)行封裝,有較好的移植性函數(shù)指針的安全轉(zhuǎn)換 , 利用下面的特性,用戶可以擴(kuò)展帶有私有屬性的狀態(tài)機(jī)和事件而使用統(tǒng)一的基礎(chǔ)狀態(tài)機(jī)接口
typedef void (*Tran)(struct StateTableTag *me, Event const *e); void Bomb2_setting_ARM (Bomb2 *me, Event const *e); typedef struct Bomb { struct StateTableTag *me; //必須為第一個(gè)成員 uint8_t private; }
缺點(diǎn):
函數(shù)粒度太小是最明顯的一個(gè)缺點(diǎn),一個(gè)狀態(tài)和一個(gè)事件就會產(chǎn)生一個(gè)函數(shù),當(dāng)狀態(tài)和事件較多時(shí),處理函數(shù)將增加很快,在閱讀代碼時(shí),邏輯分散。
沒有實(shí)現(xiàn)進(jìn)入退出動(dòng)作。
一維狀態(tài)轉(zhuǎn)換表
????實(shí)現(xiàn)原理:
typedef void (*fp_action)(EVT_TYPE evt,void* param); /*轉(zhuǎn)換表基礎(chǔ)結(jié)構(gòu)*/ struct tran_evt_t { EVT_TYPE evt; uint8_t next_state; }; /*狀態(tài)的描述*/ struct fsm_state_t { fp_action enter_action; //進(jìn)入動(dòng)作 fp_action exit_action; //退出動(dòng)作 fp_action action; tran_evt_t* tran; //轉(zhuǎn)換表 uint8_t tran_nb; //轉(zhuǎn)換表的大小 const char* name; } /*狀態(tài)表本體*/ #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" } }; /*構(gòu)建一個(gè)狀態(tài)機(jī)*/ 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; /*初始化狀態(tài)機(jī)*/ void bomb3_init(void) { bomb3.state_table = state_table; //指向狀態(tài)表 bomb3.cur_state = setting; bomb3.defuse_code = 8; //1000 } /*狀態(tài)機(jī)事件派發(fā)*/ void fsm_dispatch(EVT_TYPE evt , void* param) { tran_evt_t* p_tran = NULL; /*獲取當(dāng)前狀態(tài)的轉(zhuǎn)換表*/ p_tran = bomb3.state_table[bomb3.cur_state]->tran; /*判斷所有可能的轉(zhuǎn)換是否與當(dāng)前觸發(fā)的事件匹配*/ for(uint8_t i=0;ievt == evt)//事件會觸發(fā)轉(zhuǎn)換 { if(NULL != bomb3.state_table[bomb3.cur_state].exit_action){ bomb3.state_table[bomb3.cur_state].exit_action(NULL); //執(zhí)行退出動(dòng)作 } if(bomb3.state_table[_tran[i]->next_state].enter_action){ bomb3.state_table[_tran[i]->next_state].enter_action(NULL);//執(zhí)行進(jìn)入動(dòng)作 } /*更新當(dāng)前狀態(tài)*/ bomb3.cur_state = p_tran[i]->next_state; } else { bomb3.state_table[bomb3.cur_state].action(evt,param); } } } /************************************************************************* setting狀態(tài)相關(guān) ************************************************************************/ 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 狀態(tài)相關(guān)*/
優(yōu)點(diǎn):
各個(gè)狀態(tài)面向用戶相對獨(dú)立,增加事件和狀態(tài)不需要去修改先前已存在的狀態(tài)事件函數(shù)。
實(shí)現(xiàn)了狀態(tài)的進(jìn)入和退出
容易根據(jù)狀態(tài)躍遷圖來設(shè)計(jì) (狀態(tài)躍遷圖列出了每個(gè)狀態(tài)的躍遷可能,也就是這里的轉(zhuǎn)換表)
實(shí)現(xiàn)靈活,可實(shí)現(xiàn)復(fù)雜邏輯,如上一次狀態(tài),增加監(jiān)護(hù)條件來減少事件的數(shù)量??蓪?shí)現(xiàn)非完全事件驅(qū)動(dòng)
缺點(diǎn):
函數(shù)粒度較小(比二維小且增長慢),可以看到,每一個(gè)狀態(tài)需要至少3個(gè)函數(shù),還需要列出所有的轉(zhuǎn)換關(guān)系。
QP嵌入式實(shí)時(shí)框架
特點(diǎn):
事件驅(qū)動(dòng)型編程
好萊塢原則:和傳統(tǒng)的順序式編程方法例如“超級循環(huán)”,或傳統(tǒng)的RTOS 的任務(wù)不同。絕大多數(shù)的現(xiàn)代事件驅(qū)動(dòng)型系統(tǒng)根據(jù)好萊塢原則被構(gòu)造,(Don’t call me; I’ll call you.)
面向?qū)ο?/strong>
類和單一繼承。
工具
QM ,一個(gè)通過UML類圖來描述狀態(tài)機(jī)的軟件,并且可以自動(dòng)生成C代碼: ??
QS軟件追蹤工具:
QEP實(shí)現(xiàn)有限狀態(tài)機(jī)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*/ } } //實(shí)現(xiàn)上面定時(shí)器例子 #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(); }
優(yōu)點(diǎn):
采用面向?qū)ο蟮脑O(shè)計(jì)方法,很好的移植性
實(shí)現(xiàn)了進(jìn)入退出動(dòng)作
合適的粒度,且事件的粒度可控
狀態(tài)切換時(shí)通過改變指針,效率高
可擴(kuò)展成為層次狀態(tài)機(jī)
缺點(diǎn):
對事件的定義以及事件粒度的控制是設(shè)計(jì)的最大難點(diǎn),如串口接收到一幀數(shù)據(jù),這些變量的更新單獨(dú)作為某個(gè)事件,還是串口收到數(shù)據(jù)作為一個(gè)事件。再或者顯示屏,如果使用此種編程方式,如何設(shè)計(jì)事件。
QP 實(shí)現(xiàn)層次狀態(tài)機(jī) Hsm簡介
初始化
初始化層次狀態(tài)機(jī)的實(shí)現(xiàn):在初始化時(shí),用戶所選取的狀態(tài)永遠(yuǎn)是最底層的狀態(tài),如上圖,我們在計(jì)算器開機(jī)后,應(yīng)該進(jìn)入的是開始狀態(tài),這就涉及到一個(gè)問題,由最初top(頂狀態(tài))到begin 是有一條狀態(tài)切換路徑的,當(dāng)我們設(shè)置狀態(tài)為begin如何搜索這條路徑成為關(guān)鍵(知道了路徑才能正確的進(jìn)入begin,要執(zhí)行路徑中過渡狀態(tài)的進(jìn)入和退出事件)
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; /* 這里的狀態(tài)為begin */ /*通過執(zhí)行空信號,從底層狀態(tài)找到頂狀態(tài)的路徑*/ (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. */ /* 鉆到最底層的狀態(tài),執(zhí)行路徑中的所有進(jìn)入事件 */ 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; }
狀態(tài)切換
/*.................................................................*/ QState result(Calc *me, QEvent const *e) { switch (e->sig) {you 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(&reday); } /*.ready為result和begin的超狀態(tài)................................................*/ 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); //當(dāng)前狀態(tài)不能處理事件 ,直到找到能處理事件的狀態(tài) 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 */ }
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 */ ....
QP實(shí)時(shí)框架的組成
內(nèi)存管理
使用內(nèi)存池,對于低性能mcu,內(nèi)存極為有限,引入內(nèi)存管理主要是整個(gè)架構(gòu)中,是以事件作為主要的任務(wù)通信手段,且事件是帶參數(shù)的,可能相同類型的事件會多次觸發(fā),而事件處理完成后,需要清除事件,無法使用靜態(tài)的事件,因此是有必要為不同事件創(chuàng)建內(nèi)存池的。
對于不同塊大小的內(nèi)存池,需要考慮的是每個(gè)塊的起始地址對齊問題。在進(jìn)行內(nèi)存池初始化時(shí),我們是根據(jù)blocksize+header大小來進(jìn)行劃分內(nèi)存池的。假設(shè)一個(gè)2字節(jié)的結(jié)構(gòu),如果以2來進(jìn)行劃分,假設(shè)mcu 4字節(jié)對齊,那么將有一半的結(jié)構(gòu)起始地址無法對齊,這時(shí)需要為每個(gè)塊預(yù)留空間,保證每個(gè)塊的對齊。
事件隊(duì)列
每一個(gè)活動(dòng)對象維護(hù)一個(gè)事件隊(duì)列,事件都是由基礎(chǔ)事件派生的,不同類型的事件只需要將其基礎(chǔ)事件成員添加到活動(dòng)對象的隊(duì)列中即可,最終在取出的時(shí)候通過一個(gè)強(qiáng)制轉(zhuǎn)換便能獲得附加的參數(shù)。
事件派發(fā)
直接事件發(fā)送:
QActive_postLIFO()
發(fā)行訂閱事件發(fā)送:
豎軸表示信號(為事件的基類)
活動(dòng)對象支持64個(gè)優(yōu)先級,每一個(gè)活動(dòng)對象要求擁有唯一優(yōu)先級
通過優(yōu)先級的bit位來表示某個(gè)事件被哪些活動(dòng)對象訂閱,并在事件觸發(fā)后根據(jù)優(yōu)先級為活動(dòng)對象派發(fā)事件。
定時(shí)事件
非有序鏈表:
????合作式調(diào)度器QV:
QP nano的簡介
完全支持層次式狀態(tài)嵌套,包括在最多4 層狀態(tài)嵌套情況下,對任何狀態(tài)轉(zhuǎn)換拓?fù)涞目杀WC的進(jìn)入/ 退出動(dòng)作
支持高達(dá)8 個(gè)并發(fā)執(zhí)行的,可確定的,線程安全的事件隊(duì)列的活動(dòng)對象57
支持一個(gè)字節(jié)寬( 255 個(gè)信號)的信號,和一個(gè)可伸縮的參數(shù),它可被配置成0 (沒有參數(shù)), 1 , 2 或4 字節(jié)
使用先進(jìn)先出FIFO排隊(duì)策略的直接事件派發(fā)機(jī)制
每個(gè)活動(dòng)對象有一個(gè)一次性時(shí)間事件(定時(shí)器),它的可配置動(dòng)態(tài)范圍是0(沒有時(shí)間事件) , 1 , 2 或4 字節(jié)
內(nèi)建的合作式vanilla 內(nèi)核
內(nèi)建的名為QK-nano 的可搶占型RTC內(nèi)核(見第六章6.3.8節(jié))
帶有空閑回調(diào)函數(shù)的低功耗架構(gòu),用來方便的實(shí)現(xiàn)節(jié)省功耗模式。
在代碼里為流行的低端CPU架構(gòu)的C編譯器的非標(biāo)準(zhǔn)擴(kuò)展進(jìn)行了準(zhǔn)備(例如,在代碼空間分配常數(shù)對象,可重入函數(shù),等等)
基于斷言的錯(cuò)誤處理策略
代碼風(fēng)格: ?
審核編輯:湯梓紅
-
計(jì)數(shù)器
+關(guān)注
關(guān)注
32文章
2253瀏覽量
94287 -
有限狀態(tài)機(jī)
+關(guān)注
關(guān)注
0文章
52瀏覽量
10311 -
狀態(tài)機(jī)
+關(guān)注
關(guān)注
2文章
491瀏覽量
27457 -
fsm
+關(guān)注
關(guān)注
0文章
35瀏覽量
12804
原文標(biāo)題:QP狀態(tài)機(jī)框架與常見狀態(tài)機(jī)方法 | 長文收藏
文章出處:【微信號:c-stm32,微信公眾號:STM32嵌入式開發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論