嵌入式狀態(tài)機(jī)編程-QP狀態(tài)機(jī)框架與常見狀態(tài)機(jī)方法
狀態(tài)機(jī)基本術(shù)語
現(xiàn)態(tài):是指當(dāng)前所處的狀態(tài)。條件:又稱為“事件”,當(dāng)一個(gè)條件被滿足,將會(huì)觸發(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)。“次態(tài)”是相對(duì)于“現(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)
“ ” “-” 按鍵對(duì)初始倒計(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)
按下“ ” “-” 會(huì)進(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.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:???//?"確認(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ù)會(huì)代碼量會(huì)不斷增加
- 狀態(tài)機(jī)沒有進(jìn)行封裝,移植性差。
- 沒有實(shí)現(xiàn)狀態(tài)的進(jìn)入和退出的操作。進(jìn)入和退出在狀態(tài)機(jī)中尤為重要
- 進(jìn)入事件:只會(huì)在剛進(jìn)入時(shí)觸發(fā)一次,主要作用是對(duì)狀態(tài)進(jìn)行必要的初始化
- 退出事件:只會(huì)在狀態(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)對(duì)應(yīng)的事件處理函數(shù)*/
??????void?setting_UP(EVT_TYPE?evt,?void*?param)
??????{
??????????if(bomb1.timeout60)?? bomb1.timeout;
??????????bsp_display(bomb1.timeout);
??????}
優(yōu)點(diǎn)
- 各個(gè)狀態(tài)面向用戶相對(duì)獨(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è)事件就會(huì)產(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;i????????{
????????????if(p_tran[i]->evt?==?evt)//事件會(huì)觸發(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)面向用戶相對(duì)獨(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ù)粒度較?。ū榷S小且增長慢),可以看到,每一個(gè)狀態(tài)需要至少3個(gè)函數(shù),還需要列出所有的轉(zhuǎn)換關(guān)系。
QP嵌入式實(shí)時(shí)框架
特點(diǎn)
事件驅(qū)動(dòng)型編程
好萊塢原則:和傳統(tǒng)的順序式編程方法例如“超級(jí)循環(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ū)ο?/h4>類和單一繼承。工具
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?,?
??????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?,?