日本a√视频在线,久久青青亚洲国产,亚洲一区欧美二区,免费g片在线观看网站

        <style id="k3y6c"><u id="k3y6c"></u></style>
        <s id="k3y6c"></s>
        <mark id="k3y6c"></mark>
          
          

          <mark id="k3y6c"></mark>

          "); //-->

          博客專欄

          EEPW首頁 > 博客 > 真沒想到還可以這樣寫狀態(tài)機(jī)!QP嵌入式實(shí)時(shí)框架

          真沒想到還可以這樣寫狀態(tài)機(jī)!QP嵌入式實(shí)時(shí)框架

          發(fā)布人:xiaomaidashu 時(shí)間:2025-08-28 來源:工程師 發(fā)布文章
          1 QP嵌入式實(shí)時(shí)框架事件驅(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.)

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

          面向?qū)ο?/span>

          類和單一繼承:

          圖片

          工具

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

          圖片

          QS軟件追蹤工具:

          圖片圖片

          2 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ì)事件。
          3 QP實(shí)現(xiàn)層次狀態(tài)機(jī)

          圖片

          初始化層次狀態(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 */

                              ....

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

          圖片圖片

          內(nèi)存管理

          使用內(nèi)存池,對于低性能mcu,內(nèi)存極為有限,引入內(nèi)存管理主要是整個(gè)架構(gòu)中,是以事件作為主要的任務(wù)通信手段,且事件是帶參數(shù)的,可能相同類型的事件會(huì)多次觸發(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ā)事件。

          圖片

          代碼風(fēng)格

          圖片


          一鍵三連,公眾號后臺回復(fù)【QP】可以獲取資料


          *博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請聯(lián)系工作人員刪除。



          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉