0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

狀態(tài)機(jī)的基本術(shù)語 傳統(tǒng)有限狀態(tài)機(jī)Fsm實(shí)現(xiàn)方法

STM32嵌入式開發(fā) ? 來源:CSDN-取經(jīng)的孫猴兒 ? 2023-09-08 09:56 ? 次閱讀

狀態(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)”了。 02b5f0bc-4de4-11ee-a25d-92fbcf53809c.png

傳統(tǒng)有限狀態(tài)機(jī)Fsm實(shí)現(xiàn)方法

02fa9956-4de4-11ee-a25d-92fbcf53809c.png ????

如圖,是一個(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;
          }
      }

030c227a-4de4-11ee-a25d-92fbcf53809c.png



優(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)的躍遷。 03259160-4de4-11ee-a25d-92fbcf53809c.png

(*) 僅當(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)換表

0346254c-4de4-11ee-a25d-92fbcf53809c.png ????實(shí)現(xiàn)原理:
0360b72c-4de4-11ee-a25d-92fbcf53809c.png

 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>



類和單一繼承。 03845d12-4de4-11ee-a25d-92fbcf53809c.png


工具



QM ,一個(gè)通過UML類圖來描述狀態(tài)機(jī)的軟件,并且可以自動(dòng)生成C代碼: 039a66de-4de4-11ee-a25d-92fbcf53809c.png ??

QS軟件追蹤工具: 03d62fca-4de4-11ee-a25d-92fbcf53809c.png0459b106-4de4-11ee-a25d-92fbcf53809c.png



QEP實(shí)現(xiàn)有限狀態(tài)機(jī)Fsm

0489ec54-4de4-11ee-a25d-92fbcf53809c.png

    /* 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簡介

049d76de-4de4-11ee-a25d-92fbcf53809c.png初始化 04b2d808-4de4-11ee-a25d-92fbcf53809c.png


初始化層次狀態(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)切換

04c6c1ec-4de4-11ee-a25d-92fbcf53809c.png

 /*.................................................................*/
    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 */
    }

04ecb05a-4de4-11ee-a25d-92fbcf53809c.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 */
                        ....

QP實(shí)時(shí)框架的組成

05015d98-4de4-11ee-a25d-92fbcf53809c.png05228a54-4de4-11ee-a25d-92fbcf53809c.png


內(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è)塊的對齊。 05463ba2-4de4-11ee-a25d-92fbcf53809c.png


事件隊(duì)列



每一個(gè)活動(dòng)對象維護(hù)一個(gè)事件隊(duì)列,事件都是由基礎(chǔ)事件派生的,不同類型的事件只需要將其基礎(chǔ)事件成員添加到活動(dòng)對象的隊(duì)列中即可,最終在取出的時(shí)候通過一個(gè)強(qiáng)制轉(zhuǎn)換便能獲得附加的參數(shù)。 05677b00-4de4-11ee-a25d-92fbcf53809c.png



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

0584d59c-4de4-11ee-a25d-92fbcf53809c.png

定時(shí)事件



非有序鏈表:
05a9bc5e-4de4-11ee-a25d-92fbcf53809c.png ????合作式調(diào)度器QV: 05bb992e-4de4-11ee-a25d-92fbcf53809c.png


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)格: 05da9ee6-4de4-11ee-a25d-92fbcf53809c.png05fb1842-4de4-11ee-a25d-92fbcf53809c.png06120e80-4de4-11ee-a25d-92fbcf53809c.png0651ebf4-4de4-11ee-a25d-92fbcf53809c.png067fef54-4de4-11ee-a25d-92fbcf53809c.png ?   

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 計(jì)數(shù)器
    +關(guān)注

    關(guān)注

    32

    文章

    2253

    瀏覽量

    94287
  • 有限狀態(tài)機(jī)

    關(guān)注

    0

    文章

    52

    瀏覽量

    10311
  • 狀態(tài)機(jī)
    +關(guān)注

    關(guān)注

    2

    文章

    491

    瀏覽量

    27457
  • fsm
    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)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    如何去實(shí)現(xiàn)有限狀態(tài)機(jī)FSM的程序設(shè)計(jì)呢

    什么是有限狀態(tài)機(jī)FSM呢?如何去實(shí)現(xiàn)有限狀態(tài)機(jī)FSM的程序設(shè)計(jì)呢?
    發(fā)表于 01-21 07:04

    fsm有限狀態(tài)機(jī)pdf

    利用 VHDL 設(shè)計(jì)的許多實(shí)用邏輯系統(tǒng)中,有許多是可以利用有限狀態(tài)機(jī)的設(shè)計(jì)方案來描述和實(shí)現(xiàn)的。無論與基于 VHDL的其它設(shè)計(jì)方案相比,還是與可完成相似功能的 CPU 相比,狀
    發(fā)表于 06-04 10:33 ?75次下載

    有限狀態(tài)機(jī)_FSM_的實(shí)現(xiàn)

    本文主要介紹了IP模塊的有限狀態(tài)機(jī)實(shí)現(xiàn)。
    發(fā)表于 03-22 15:42 ?0次下載

    有限狀態(tài)機(jī)FSM在PLD中的實(shí)現(xiàn)分析

    本文通過舉例 利用VHDL 語言描述了不同模式的有限狀態(tài)機(jī) 分析了有限狀態(tài)機(jī)在 PLD 中綜合的特點(diǎn) 。
    發(fā)表于 03-22 15:41 ?3次下載

    有限狀態(tài)機(jī)的建模與優(yōu)化設(shè)計(jì)

    本文提出一種優(yōu)秀 、高效的 Verilog HDL 描述方式來進(jìn)行有限狀態(tài)機(jī)設(shè)計(jì) 介紹了 有限狀態(tài)機(jī)的建模原則 并通過一個(gè)可綜合的實(shí)例 驗(yàn)證了 該方法設(shè)計(jì)的有限狀態(tài)機(jī)在面積和功耗上的優(yōu)
    發(fā)表于 03-22 15:19 ?1次下載

    VHDL有限狀態(tài)機(jī)設(shè)計(jì)-ST

    EDA的有限狀態(tài)機(jī),廣義而言是指只要涉及觸發(fā)器的電路,無論電路大小都可以歸結(jié)為狀態(tài)機(jī)。有限狀態(tài)機(jī)設(shè)計(jì)在學(xué)習(xí)EDA時(shí)是很重要的一章。
    發(fā)表于 06-08 16:46 ?3次下載

    初學(xué)者對有限狀態(tài)機(jī)FSM)的設(shè)計(jì)的認(rèn)識

    有限狀態(tài)機(jī)FSM)是一種常見的電路,由時(shí)序電路和組合電路組成。設(shè)計(jì)有限狀態(tài)機(jī)的第一步是確定采用Moore狀態(tài)機(jī)還是采用Mealy狀態(tài)機(jī)。
    發(fā)表于 02-11 13:51 ?4260次閱讀
    初學(xué)者對<b class='flag-5'>有限狀態(tài)機(jī)</b>(<b class='flag-5'>FSM</b>)的設(shè)計(jì)的認(rèn)識

    使用函數(shù)指針的方法實(shí)現(xiàn)狀態(tài)機(jī)

    之前寫過一篇狀態(tài)機(jī)的實(shí)用文章,很多朋友說有幾個(gè)地方有點(diǎn)難度不易理解,今天給大家換種簡單寫法,使用函數(shù)指針的方法實(shí)現(xiàn)狀態(tài)機(jī)。 狀態(tài)機(jī)簡介 有限狀態(tài)機(jī)
    的頭像 發(fā)表于 10-19 09:36 ?2368次閱讀
    使用函數(shù)指針的<b class='flag-5'>方法</b><b class='flag-5'>實(shí)現(xiàn)狀態(tài)機(jī)</b>

    如何使用FPGA實(shí)現(xiàn)序列檢測有限狀態(tài)機(jī)

    有限狀態(tài)機(jī)是絕大部分控制電路的核心結(jié)構(gòu), 是表示有限個(gè)狀態(tài)以及在這些狀態(tài)之間轉(zhuǎn)移和動(dòng)作等行為的數(shù)學(xué)模型。有限狀態(tài)機(jī)是指輸出取決于過去輸入部分
    發(fā)表于 11-04 17:17 ?12次下載
    如何使用FPGA<b class='flag-5'>實(shí)現(xiàn)</b>序列檢測<b class='flag-5'>有限狀態(tài)機(jī)</b>

    基于事件驅(qū)動(dòng)的有限狀態(tài)機(jī)介紹

    ? 一、介紹 EFSM(event finite state machine,事件驅(qū)動(dòng)型有限狀態(tài)機(jī)),是一個(gè)基于事件驅(qū)動(dòng)的有限狀態(tài)機(jī),主要應(yīng)用于嵌入式設(shè)備的軟件系統(tǒng)中。 EFSM的設(shè)計(jì)原則是:簡單
    的頭像 發(fā)表于 11-16 15:29 ?2298次閱讀

    基于事件驅(qū)動(dòng)的有限狀態(tài)機(jī)介紹

    EFSM(event finite state machine,事件驅(qū)動(dòng)型有限狀態(tài)機(jī)),是一個(gè)基于事件驅(qū)動(dòng)的有限狀態(tài)機(jī),主要應(yīng)用于嵌入式設(shè)備的軟件系統(tǒng)中。
    的頭像 發(fā)表于 02-11 10:17 ?1015次閱讀

    Verilog狀態(tài)機(jī)的類型

    有限狀態(tài)機(jī)(Finite-State Machine,FSM),簡稱狀態(tài)機(jī),是表示有限個(gè)狀態(tài)以及在這些
    的頭像 發(fā)表于 06-01 15:23 ?1758次閱讀
    Verilog<b class='flag-5'>狀態(tài)機(jī)</b>的類型

    有限狀態(tài)機(jī)分割設(shè)計(jì)

    有限狀態(tài)機(jī)分割設(shè)計(jì),其實(shí)質(zhì)就是一個(gè)狀態(tài)機(jī)分割成多個(gè)狀態(tài)機(jī)
    的頭像 發(fā)表于 10-09 10:47 ?603次閱讀

    什么是狀態(tài)機(jī)?狀態(tài)機(jī)的種類與實(shí)現(xiàn)

    狀態(tài)機(jī),又稱有限狀態(tài)機(jī)(Finite State Machine,FSM)或米利狀態(tài)機(jī)(Mealy Machine),是一種描述系統(tǒng)狀態(tài)變化
    的頭像 發(fā)表于 10-19 10:27 ?8842次閱讀

    什么是有限狀態(tài)機(jī)?如何解決傳統(tǒng)有限狀態(tài)機(jī)狀態(tài)爆炸」問題?

    有限狀態(tài)機(jī)(Finite State Machine,簡稱FSM)是一種用來進(jìn)行對象行為建模的工具,其作用主要是描述對象在它的生命周期內(nèi)所經(jīng)歷的狀態(tài)序列以及如何響應(yīng)來自外界的各種事件。
    的頭像 發(fā)表于 02-17 16:09 ?5904次閱讀
    什么是<b class='flag-5'>有限狀態(tài)機(jī)</b>?如何解決<b class='flag-5'>傳統(tǒng)</b><b class='flag-5'>有限狀態(tài)機(jī)</b>「<b class='flag-5'>狀態(tài)</b>爆炸」問題?