?狀態(tài)機在嵌入式軟件中隨處可見,可能你會說狀態(tài)機有什么難的,不就是?switch 嗎? switch僅僅是最基礎(chǔ)的一個點,關(guān)于狀態(tài)機的更多操作,或許你都沒有見過,下面分享幾種實現(xiàn)方法。
1. 狀態(tài)機基本術(shù)語
現(xiàn)態(tài):是指當前所處的狀態(tài)。
條件:又稱為“事件”,當一個條件被滿足,將會觸發(fā)一個動作,或者執(zhí)行一次狀態(tài)的遷移。
動作:條件滿足后執(zhí)行的動作。動作執(zhí)行完畢后,可以遷移到新的狀態(tài),也可以仍舊保持原狀態(tài)。動作不是必需的,當條件滿足后,也可以不執(zhí)行任何動作,直接遷移到新狀態(tài)。
次態(tài):條件滿足后要遷往的新狀態(tài)。“次態(tài)”是相對于“現(xiàn)態(tài)”而言的,“次態(tài)”一旦被激活,就轉(zhuǎn)變成新的“現(xiàn)態(tài)”了。
2. 傳統(tǒng)有限狀態(tài)機FSM
如下圖所示,這是一個定時計數(shù)器,計數(shù)器存在兩種狀態(tài),一種為設(shè)置狀態(tài),一種為計時狀態(tài)。
設(shè)置狀態(tài):
“+” “-” 按鍵對初始倒計時進行設(shè)置
當計數(shù)值設(shè)置完成,點擊確認鍵啟動計時 ,即切換到計時狀態(tài)
計時狀態(tài):
按下“+” “-” 會進行密碼的輸入“+”表示1 ,“-”表示輸入0 ,密碼共有4位
確認鍵:只有輸入的密碼等于默認密碼,按確認鍵才能停止計時,否則計時直接到零,并執(zhí)行相關(guān)操作
3. 嵌套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)機相關(guān)結(jié)構(gòu) ***************************************/ struct??bomb { ??uint8_t?state; ??uint8_t?timeout; ??uint8_t?code; ??uint8_t?defuse_code; }bomb1; /*************************************** 4.初始化狀態(tài)機 ***************************************/ void?bomb1_init(void) { ??bomb1.state?=?SETTING; ??bomb1.defuse_code?=?6;????//0110? } /*************************************** 5.?狀態(tài)機事件派發(fā) ***************************************/ void?bomb1_fsm_dispatch(EVENT_TYPE?evt?,void*?param) { ??switch(bomb1.state) ??{ ??????case?SETTING: ??????{ ??????????switch(evt) ??????????{ ??????????????case?UP_EVT:????//?"+"???按鍵按下事件 ????????????????if(bomb1.timeout60)??++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; ??} }
![432877c8-659b-11ed-8abf-dac502259ad0.png](https://file1.elecfans.com//web2/M00/98/0B/wKgaomTnPxWAVkkIAAGKqGlsw0Y224.png)
?
優(yōu)點:簡單,代碼閱讀連貫,容易理解
缺點:
當狀態(tài)或事件增多時,代碼狀態(tài)函數(shù)需要經(jīng)常改動,狀態(tài)事件處理函數(shù)會代碼量會不斷增加
狀態(tài)機沒有進行封裝,移植性差。
沒有實現(xiàn)狀態(tài)的進入和退出的操作。進入和退出在狀態(tài)機中尤為重要。進入事件:只會在剛進入時觸發(fā)一次,主要作用是對狀態(tài)進行必要的初始化。退出事件:只會在狀態(tài)切換時觸發(fā)一次 ,主要的作用是清除狀態(tài)產(chǎn)生的中間參數(shù),為下次進入提供干凈環(huán)境
4. 狀態(tài)表
二維狀態(tài)轉(zhuǎn)換表
狀態(tài)機可以分為狀態(tài)和事件 ,狀態(tài)的躍遷都是受事件驅(qū)動的,因此可以通過一個二維表格來表示狀態(tài)的躍遷。
僅當(code == defuse_code) 時才發(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.timeout60)??++bomb1.timeout; ??bsp_display(bomb1.timeout); }
?
缺點:函數(shù)粒度太小是最明顯的一個缺點,一個狀態(tài)和一個事件就會產(chǎn)生一個函數(shù),當狀態(tài)和事件較多時,處理函數(shù)將增加很快,在閱讀代碼時,邏輯分散。沒有實現(xiàn)進入退出動作。
一維狀態(tài)轉(zhuǎn)換表
實現(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;??????//進入動作 ????????fp_action??exit_action;???//退出動作 ????????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)建一個狀態(tài)機*/ ????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)機*/ ????void??bomb3_init(void) ????{ ????????bomb3.state_table?=?state_table;??//指向狀態(tài)表 ????????bomb3.cur_state?=?setting; ????????bomb3.defuse_code?=?8;?//1000 ????} ????/*狀態(tài)機事件派發(fā)*/ ????void??fsm_dispatch(EVT_TYPE?evt?,?void*?param) ????{ ????????tran_evt_t*?p_tran?=?NULL; ???????? ????????/*獲取當前狀態(tài)的轉(zhuǎn)換表*/ ????????p_tran?=?bomb3.state_table[bomb3.cur_state]->tran; ???????? ????????/*判斷所有可能的轉(zhuǎn)換是否與當前觸發(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í)行退出動作 ?????????????} ????????????????if(bomb3.state_table[_tran[i]->next_state].enter_action){ ???????????????????bomb3.state_table[_tran[i]->next_state].enter_action(NULL);//執(zhí)行進入動作 ????????????????} ????????????????/*更新當前狀態(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)*/ ;i++)>
?
優(yōu)點:
各個狀態(tài)面向用戶相對獨立,增加事件和狀態(tài)不需要去修改先前已存在的狀態(tài)事件函數(shù)。
實現(xiàn)了狀態(tài)的進入和退出
容易根據(jù)狀態(tài)躍遷圖來設(shè)計 (狀態(tài)躍遷圖列出了每個狀態(tài)的躍遷可能,也就是這里的轉(zhuǎn)換表)
實現(xiàn)靈活,可實現(xiàn)復雜邏輯,如上一次狀態(tài),增加監(jiān)護條件來減少事件的數(shù)量。可實現(xiàn)非完全事件驅(qū)動
缺點:
函數(shù)粒度較小(比二維小且增長慢),可以看到,每一個狀態(tài)需要至少3個函數(shù),還需要列出所有的轉(zhuǎn)換關(guān)系。
5. QP嵌入式實時框架
事件驅(qū)動型編程
好萊塢原則:和傳統(tǒng)的順序式編程方法例如“超級循環(huán)”,或傳統(tǒng)的RTOS 的任務(wù)不同。絕大多數(shù)的現(xiàn)代事件驅(qū)動型系統(tǒng)根據(jù)好萊塢原則被構(gòu)造(Don’t call me; I’ll call you.)
面向?qū)ο?/p>
類和單一繼承:
工具
QM :一個通過UML類圖來描述狀態(tài)機的軟件,并且可以自動生成C代碼
QS軟件追蹤工具:
6. QEP實現(xiàn)有限狀態(tài)機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*/ ????} ??} 實現(xiàn)上面定時器例子 ??#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)點:
采用面向?qū)ο蟮脑O(shè)計方法,很好的移植性
實現(xiàn)了進入退出動作
合適的粒度,且事件的粒度可控
狀態(tài)切換時通過改變指針,效率高
可擴展成為層次狀態(tài)機
缺點:
對事件的定義以及事件粒度的控制是設(shè)計的最大難點,如串口接收到一幀數(shù)據(jù),這些變量的更新單獨作為某個事件,還是串口收到數(shù)據(jù)作為一個事件。再或者顯示屏,如果使用此種編程方式,如何設(shè)計事件。
7. QP實現(xiàn)層次狀態(tài)機
初始化層次狀態(tài)機的實現(xiàn):在初始化時,用戶所選取的狀態(tài)永遠是最底層的狀態(tài),如上圖,我們在計算器開機后,應(yīng)該進入的是開始狀態(tài),這就涉及到一個問題,由最初top(頂狀態(tài))到begin 是有一條狀態(tài)切換路徑的,當我們設(shè)置狀態(tài)為begin如何搜索這條路徑成為關(guān)鍵(知道了路徑才能正確的進入begin,要執(zhí)行路徑中過渡狀態(tài)的進入和退出事件)。
?
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í)行路徑中的所有進入事件?*/ ????????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);?//當前狀態(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?*/ }
![44a54c52-659b-11ed-8abf-dac502259ad0.png](https://file1.elecfans.com//web2/M00/98/0B/wKgaomTnPxeANaZQAACLsfrDH-M084.png)
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?*/ ????????????????????....
?
8. QP實時框架的組成
內(nèi)存管理
使用內(nèi)存池,對于低性能mcu,內(nèi)存極為有限,引入內(nèi)存管理主要是整個架構(gòu)中,是以事件作為主要的任務(wù)通信手段,且事件是帶參數(shù)的,可能相同類型的事件會多次觸發(fā),而事件處理完成后,需要清除事件,無法使用靜態(tài)的事件,因此是有必要為不同事件創(chuàng)建內(nèi)存池的。對于不同塊大小的內(nèi)存池,需要考慮的是每個塊的起始地址對齊問題。在進行內(nèi)存池初始化時,我們是根據(jù)blocksize+header大小來進行劃分內(nèi)存池的。假設(shè)一個2字節(jié)的結(jié)構(gòu),如果以2來進行劃分,假設(shè)mcu 4字節(jié)對齊,那么將有一半的結(jié)構(gòu)起始地址無法對齊,這時需要為每個塊預(yù)留空間,保證每個塊的對齊。
事件隊列
每一個活動對象維護一個事件隊列,事件都是由基礎(chǔ)事件派生的,不同類型的事件只需要將其基礎(chǔ)事件成員添加到活動對象的隊列中即可,最終在取出的時候通過一個強制轉(zhuǎn)換便能獲得附加的參數(shù)。
事件派發(fā)
直接事件發(fā)送QActive_postLIFO()
發(fā)行訂閱事件發(fā)送豎軸表示信號(為事件的基類)活動對象支持64個優(yōu)先級,每一個活動對象要求擁有唯一優(yōu)先級通過優(yōu)先級的bit位來表示某個事件被哪些活動對象訂閱,并在事件觸發(fā)后根據(jù)優(yōu)先級為活動對象派發(fā)事件。
代碼風格
評論