1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        嵌入式狀態(tài)機編程-QP狀態(tài)機框架與常見狀態(tài)機方法

        共 14057字,需瀏覽 29分鐘

         ·

        2021-12-18 11:57

        ? ?關(guān)注、星標(biāo)公眾號,直達精彩內(nèi)容

        來源:取經(jīng)的孫猴兒


        狀態(tài)機基本術(shù)語

        現(xiàn)態(tài):是指當(dāng)前所處的狀態(tài)。條件:又稱為“事件”,當(dāng)一個條件被滿足,將會觸發(fā)一個動作,或者執(zhí)行一次狀態(tài)的遷移。動作:條件滿足后執(zhí)行的動作。動作執(zhí)行完畢后,可以遷移到新的狀態(tài),也可以仍舊保持原狀態(tài)。

        動作不是必需的,當(dāng)條件滿足后,也可以不執(zhí)行任何動作,直接遷移到新狀態(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)機Fsm實現(xiàn)方法

        如圖,是一個定時計數(shù)器,計數(shù)器存在兩種狀態(tài),一種為設(shè)置狀態(tài),一種為計時狀態(tài)

        設(shè)置狀態(tài)

        “+” “-” 按鍵對初始倒計時進行設(shè)置 當(dāng)計數(shù)值設(shè)置完成,點擊確認(rèn)鍵啟動計時 ,即切換到計時狀態(tài)

        計時狀態(tài)

        按下“+” “-” 會進行密碼的輸入。“+”表示1 ,“-”表示輸入0 ,密碼共有4位?

        確認(rèn)鍵:只有輸入的密碼等于默認(rèn)密碼,按確認(rèn)鍵才能停止計時,否則計時直接到零,并執(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)機相關(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:???//?"確認(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)點:

        • 簡單,代碼閱讀連貫,容易理解

        缺點

        • 當(dāng)狀態(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)境

        狀態(tài)表

        二維狀態(tài)轉(zhuǎn)換表

        狀態(tài)機可以分為狀態(tài)和事件 ,狀態(tài)的躍遷都是受事件驅(qū)動的,因此可以通過一個二維表格來表示狀態(tài)的躍遷。

        (*) 僅當(dāng)( 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);
        ??????}

        優(yōu)點

        • 各個狀態(tài)面向用戶相對獨立,增加事件和狀態(tài)不需要去修改先前已存在的狀態(tài)事件函數(shù)。

        • 可將狀態(tài)機進行封裝,有較好的移植性 函數(shù)指針的安全轉(zhuǎn)換 , 利用下面的特性,用戶可以擴展帶有私有屬性的狀態(tài)機和事件而使用統(tǒng)一的基礎(chǔ)狀態(tài)機接口

          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;
          }

        缺點

        • 函數(shù)粒度太小是最明顯的一個缺點,一個狀態(tài)和一個事件就會產(chǎn)生一個函數(shù),當(dāng)狀態(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;
        ????????
        ????????/*獲取當(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)//事件會觸發(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í)行進入動作
        ????????????????}
        ????????????????/*更新當(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)點

        • 各個狀態(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)復(fù)雜邏輯,如上一次狀態(tài),增加監(jiān)護條件來減少事件的數(shù)量。可實現(xiàn)非完全事件驅(qū)動

        缺點

        • 函數(shù)粒度較?。ū榷S小且增長慢),可以看到,每一個狀態(tài)需要至少3個函數(shù),還需要列出所有的轉(zhuǎn)換關(guān)系。

        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ū)ο?/h4>

        類和單一繼承。

        工具

        QM ,一個通過UML類圖來描述狀態(tài)機的軟件,并且可以自動生成C代碼:

        QS軟件追蹤工具:

        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è)計事件。

        QP 實現(xiàn)層次狀態(tài)機 Hsm簡介

        初始化:

        初始化層次狀態(tài)機的實現(xiàn):在初始化時,用戶所選取的狀態(tài)永遠(yuǎn)是最底層的狀態(tài),如上圖,我們在計算器開機后,應(yīng)該進入的是開始狀態(tài),這就涉及到一個問題,由最初top(頂狀態(tài))到begin 是有一條狀態(tài)切換路徑的,當(dāng)我們設(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);?//當(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?*/
        ????}
        img
        ?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實時框架的組成

        內(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ā)事件。

        定時事件

        非有序鏈表:

        合作式調(diào)度器QV:

        QP nano的簡介

        • 完全支持層次式狀態(tài)嵌套,包括在最多4 層狀態(tài)嵌套情況下,對任何狀態(tài)轉(zhuǎn)換拓?fù)涞目杀?證的進入/ 退出動作
        • 支持高達8 個并發(fā)執(zhí)行的,可確定的,線程安全的事件隊列的活動對象57
        • 支持一個字節(jié)寬( 255 個信號)的信號,和一個可伸縮的參數(shù),它可被配置成0 (沒有參 數(shù)), 1 , 2 或4 字節(jié)
        • 使用先進先出FIFO排隊策略的直接事件派發(fā)機制
        • 每個活動對象有一個一次性時間事件(定時器),它的可配置動態(tài)范圍是0(沒有時間事 件) , 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),用來方便的實現(xiàn)節(jié)省功耗模式。
        • 在代碼里為流行的低端CPU架構(gòu)的C編譯器的非標(biāo)準(zhǔn)擴展進行了準(zhǔn)備(例如,在代碼空 間分配常數(shù)對象,可重入函數(shù),等等)
        • 基于斷言的錯誤處理策略

        代碼風(fēng)格:

        參考資料

        http://www.state-machine.com/ (QP官網(wǎng))

        來源:https://blog.csdn.net/qq_36969440/article/details/110387716

        版權(quán)歸原作者所有。如涉及作品版權(quán)問題,請聯(lián)系我進行刪除,感謝~

        ???????????????? ?END ?????????????????

        關(guān)注我的微信公眾號,回復(fù)“加群”按規(guī)則加入技術(shù)交流群。

        歡迎關(guān)注我的視頻號:

        點擊“閱讀原文”查看更多分享,歡迎點分享、收藏、點贊、在看。

        瀏覽 143
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产大黄在线观看 | 91探花偷偷拔套在线播放 | 黄片免费在线观看av | 伊人久操 | 插逼逼网站 | 久久午夜电影院 | 欧美熟女视频 | 少妇又紧又色又爽又刺激视频网站 | 色婷婷丁香五月综合 | 1级大片 爆乳极品自慰91久久久久 |