Estrutura e princípio QP da máquina de estado clássica

Siga + estrela conta oficial , não perca conteúdo emocionante

8420d5010e175720223d1d33b03ba158.gif

Fonte | CSDN

Autor |

A máquina de estado é um método comum no desenvolvimento embarcado, mas existem muitas formas de máquina de estado.Aqui vou compartilhar com você o princípio da estrutura QP clássica.

Terminologia básica da máquina de estado

Status atual: Refere-se ao estado atual. Condição: Também conhecido como "evento", quando uma condição for atendida, uma ação será acionada ou uma transição de estado será executada. Ação: A ação a ser executada após a condição ser atendida. Depois que a ação é executada, ela pode migrar para um novo estado ou pode permanecer no estado original.

Ações não são necessárias. Quando as condições forem atendidas, você poderá migrar diretamente para o novo estado sem executar nenhuma ação. Próximo estado: o novo estado para o qual mover depois que a condição for atendida. O "estado secundário" é relativo ao "estado presente". Uma vez que o "estado secundário" é ativado, ele se transformará em um novo "estado presente".

9c28d8ce2b4e3bb3b122f89afeb27ccb.png

Método de implementação da máquina de estado finito tradicional Fsm

4e3dc1ef0702b0224e2161cad1fa1abc.png

Conforme mostrado na figura, é um contador de tempo.O contador tem dois estados, um é o estado de configuração e o outro é o estado de temporização.

definir estado

Os botões "+" e "-" são usados ​​para definir a contagem regressiva inicial. Quando o valor da contagem estiver definido, clique no botão OK para iniciar a cronometragem, ou seja, mude para o estado de cronometragem

status de tempo

Pressione "+" "-" para inserir a senha. "+" significa 1, "-" significa entrada 0, a senha tem 4 dígitos 

Tecla de confirmação: Somente quando a senha inserida for igual à senha padrão, pressione a tecla de confirmação para parar o cronômetro, caso contrário, o cronômetro irá para zero diretamente e as operações relacionadas serão executadas

Interruptores aninhados

/***************************************
      1.列出所有的状态
      ***************************************/
      typedef enum{
          SETTING,
          TIMING
      }STATE_TYPE;
      /***************************************
      2.列出所有的事件
      ***************************************/
      typedef enum{
         UP_EVT,
          DOWN_EVT,
          ARM_EVT,
          TICK_EVT
      }EVENT_TYPE;
      /***************************************
      3.定义和状态机相关结构
      ***************************************/
      struct  bomb
      {
          uint8_t state;
          uint8_t timeout;
          uint8_t code;
          uint8_t defuse_code;
      }bomb1;
      /***************************************
      4.初始化状态机
      ***************************************/
      void bomb1_init(void)
      {
          bomb1.state = SETTING;
          bomb1.defuse_code = 6;    //0110 
      }
      /***************************************
      5. 状态机事件派发
      ***************************************/
      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:   // "确认" 按键按下事件
                          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;
          }
      }
b11ed0ce1bcb972a52bf038674bc29f5.png

vantagem:

  • Leitura de código simples e coerente, fácil de entender

deficiência

  • Quando o número de estados ou eventos aumenta, a função de estado do código precisa ser alterada com frequência e a quantidade de código da função de processamento de eventos de estado continuará a aumentar

  • A máquina de estado não é encapsulada e a portabilidade é ruim.

  • Nenhuma operação de entrada e saída de estado é implementada. Entrada e saída são especialmente importantes em máquinas de estado

  • Evento de entrada: será acionado apenas uma vez ao ser inserido, e sua principal função é inicializar o estado necessário

  • Evento de saída: Só será acionado uma vez quando o estado for trocado. A função principal é limpar os parâmetros intermediários gerados pelo estado e fornecer um ambiente limpo para a próxima entrada

tabela de estados

Tabela de transição de estado bidimensional

A máquina de estado pode ser dividida em estados e eventos.As transições de estado são conduzidas por eventos, então uma tabela bidimensional pode ser usada para representar as transições de estado.

50e69a706fc223e712957aa76f707099.png

(*) A transição para configuração só acontece se (code == defuse_code).

/*1.列出所有的状态*/
      enum
      {
          SETTING,
          TIMING,
          MAX_STATE
      };
      /*2.列出所有的事件*/
      enum
      {
          UP_EVT,
          DOWN_EVT,
          ARM_EVT,
          TICK_EVT,
          MAX_EVT
      };
      
      /*3.定义状态表*/
      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);
          }
      }
      /*列出所有的状态对应的事件处理函数*/
      void setting_UP(EVT_TYPE evt, void* param)
      {
          if(bomb1.timeout< 60)  ++bomb1.timeout;
          bsp_display(bomb1.timeout);
      }

vantagem

  • Cada estado é relativamente independente para os usuários, adicionando eventos e estados não é necessário modificar as funções de eventos de estado pré-existentes.

  • A máquina de estado pode ser encapsulada, o que tem melhor portabilidade. Conversão segura de ponteiros de função. Usando os seguintes recursos, os usuários podem expandir máquinas de estado e eventos com atributos privados e usar uma interface de máquina de estado básica unificada

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

deficiência

  • A desvantagem mais óbvia é que a granularidade da função é muito pequena. Um estado e um evento gerarão uma função. Quando houver muitos estados e eventos, a função de processamento aumentará rapidamente. Ao ler o código, a lógica é dispersa.

  • As ações de entrada e saída não são implementadas.

Tabela de transição de estado unidimensional

683151b7f460bb52a278c3878cd77c1b.png

Princípio de implementação:

943ac5b21aad9b374462c30ef12771d7.png
typedef void (*fp_action)(EVT_TYPE evt,void* param);
    
    /*转换表基础结构*/
    struct tran_evt_t
    {
       EVT_TYPE evt;
        uint8_t next_state;
    };
    /*状态的描述*/
    struct  fsm_state_t
    {
        fp_action  enter_action;      //进入动作
        fp_action  exit_action;   //退出动作
        fp_action  action;           
        
        tran_evt_t* tran;    //转换表
        uint8_t     tran_nb; //转换表的大小
        const char* name;
    }
    /*状态表本体*/
    #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" }
    };
    
    /*构建一个状态机*/
    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;
    
    /*初始化状态机*/
    void  bomb3_init(void)
    {
        bomb3.state_table = state_table;  //指向状态表
        bomb3.cur_state = setting;
        bomb3.defuse_code = 8; //1000
    }
    /*状态机事件派发*/
    void  fsm_dispatch(EVT_TYPE evt , void* param)
    {
        tran_evt_t* p_tran = NULL;
        
        /*获取当前状态的转换表*/
        p_tran = bomb3.state_table[bomb3.cur_state]->tran;
        
        /*判断所有可能的转换是否与当前触发的事件匹配*/
        for(uint8_t i=0;i<x;i++)
        {
            if(p_tran[i]->evt == evt)//事件会触发转换
            {
                if(NULL != bomb3.state_table[bomb3.cur_state].exit_action){
              bomb3.state_table[bomb3.cur_state].exit_action(NULL);  //执行退出动作
             }
                if(bomb3.state_table[_tran[i]->next_state].enter_action){
                   bomb3.state_table[_tran[i]->next_state].enter_action(NULL);//执行进入动作
                }
                /*更新当前状态*/
                bomb3.cur_state = p_tran[i]->next_state;
            }
            else
            {
                 bomb3.state_table[bomb3.cur_state].action(evt,param);
            }
        }
    }
    /*************************************************************************
    setting状态相关
    ************************************************************************/
    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 状态相关*/

vantagem

  • Cada estado é relativamente independente para os usuários, adicionando eventos e estados não é necessário modificar as funções de eventos de estado pré-existentes.

  • Entrada e saída de estado implementadas

  • É fácil projetar de acordo com o diagrama de transição de estado (o diagrama de transição de estado lista as possibilidades de transição de cada estado, que é a tabela de transição aqui)

  • A implementação é flexível e lógica complexa pode ser realizada, como o último estado, e o número de eventos pode ser reduzido aumentando as condições de monitoramento. Não totalmente orientado a eventos

deficiência

  • A granularidade da função é pequena (menor que bidimensional e mais lenta para crescer).Pode ser visto que cada estado requer pelo menos 3 funções e todas as relações de conversão precisam ser listadas.

QP Embedded Real-Time Framework

características

programação orientada a eventos

Princípios de Hollywood: Diferente dos métodos tradicionais de programação sequencial, como "hyperloop" ou tarefas RTOS tradicionais. A maioria dos sistemas modernos orientados a eventos são estruturados de acordo com os princípios de Hollywood (não me ligue; eu ligo para você).

Orientado a Objeto

Classes e herança única.

d9b187fadebcc5607631875e9d6d8087.png

ferramenta

QM, um software que descreve máquinas de estado por meio de diagramas de classe UML e pode gerar código C automaticamente:

7e1b898386255bfad81ead34cb8c6824.png

Ferramenta de Rastreamento de Software QS:

79301725806c935393c2ae56876d0ee6.png eba748ec03e22cb8249e5d6ceac85c96.png

QEP implementa máquina de estado finito Fsm

ad103ee5b3c31abb3676611cc217d8af.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*/
       }
      }
  实现上面定时器例子
      #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();
      }

vantagem

  • Método de design orientado a objetos, boa portabilidade

  • Ações de entrada e saída implementadas

  • Granularidade apropriada, e a granularidade dos eventos é controlável

  • Ao alterar o ponteiro quando o estado é alternado, a eficiência é alta

  • Pode ser estendido para se tornar uma máquina de estado hierárquica

deficiência

  • A definição dos eventos e o controle da granularidade dos eventos são as maiores dificuldades do projeto. Por exemplo, se a porta serial recebe um quadro de dados, a atualização dessas variáveis ​​é considerada como um evento isolado, ou os dados recebidos pela porta serial é considerado um evento. Ou a tela de exibição, se este método de programação for usado, como projetar eventos.

QP implementa uma breve introdução à máquina de estado hierárquica Hsm

729ebb86c63c563968bb79c675f4a1af.png

inicialização:

e0fc92e5d5b2fcbfa166662481065988.png

Implementação da máquina de estados do nível de inicialização: Durante a inicialização, o estado selecionado pelo usuário é sempre o estado inferior. Conforme mostrado na figura acima, após ligarmos a calculadora, devemos entrar no estado inicial, o que envolve um problema. ( Estado superior) Há um caminho de troca de estado para começar. Quando definimos o estado para começar, como procurar esse caminho torna-se a chave (conhecer o caminho pode iniciar corretamente e os eventos de entrada e saída do estado de transição no caminho deve ser executado)

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; /* 这里的状态为begin */
            
            /*通过执行空信号,从底层状态找到顶状态的路径*/
        (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. */
      /* 钻到最底层的状态,执行路径中的所有进入事件 */
        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;
    }

Interruptor de estado:

d522419abb1a478c7a1a788edcbe2a4b.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的超状态................................................*/
    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); //当前状态不能处理事件 ,直到找到能处理事件的状态
        
     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 */
    }
1b0c9756cc6c95cb26a7e4f2877c8410.png
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 */
                        ....

Composição da estrutura em tempo real do QP

2bdcea5ecde657b2fbcdd6a7d8c19423.png 3e71ae5e928364532b99f7a90c7576ce.png

gerenciamento de memória

Usando um pool de memória, para MCUs de baixo desempenho, a memória é extremamente limitada. A introdução do gerenciamento de memória é principalmente em toda a arquitetura. Os eventos são usados ​​como o principal meio de comunicação da tarefa e os eventos têm parâmetros. Eventos do mesmo tipo pode ser acionado várias vezes. Após a conclusão do processamento do evento, o evento precisa ser limpo e os eventos estáticos não podem ser usados, portanto, é necessário criar pools de memória para diferentes eventos.

Para pools de memória com tamanhos de bloco diferentes, o que precisa ser considerado é o alinhamento do endereço inicial de cada bloco. Ao inicializar o pool de memória, dividimos o pool de memória de acordo com o tamanho do bloco + tamanho do cabeçalho. Supondo uma estrutura de 2 bytes, se ela for dividida por 2, supondo que o mcu esteja alinhado em 4 bytes, então metade do endereço inicial da estrutura não estará alinhado. Neste momento, é necessário reservar espaço para cada bloco para certifique-se de que cada bloco esteja alinhado.5fe8d90c2592e3176b8cac09cec7a88f.png

fila de eventos

Cada objeto ativo mantém uma fila de eventos. Os eventos são derivados de eventos básicos. Diferentes tipos de eventos precisam apenas adicionar seus membros de eventos básicos à fila de objetos ativos e, finalmente, passar por uma conversão obrigatória ao retirá-los. Obtenha parâmetros adicionais.

4aacc7cbc7de7294644f3b6d07a0335f.png

envio de evento

Envio direto de eventos:

  • QActive_postLIFO()

Publicar Inscrever Evento Enviar:

  • O eixo vertical representa o sinal (a classe base do evento)

  • Os objetos ativos suportam 64 prioridades e cada objeto ativo requer uma prioridade exclusiva

  • O bit de prioridade indica quais objetos ativos estão inscritos em um evento e despacha eventos para objetos ativos de acordo com a prioridade após o evento ser acionado.

4d67c1bd0a6ab5b6a21f74cbfbbcc836.png

evento cronometrado

Lista encadeada não ordenada:

438a5789b37f264a053f6c44b5e12b62.png

Agendador cooperativo QV:

1ed651d58fb5e9f258a27babe94d130a.png

Introdução ao QP nano

  • Suporte total para aninhamento de estado hierárquico, incluindo ações de entrada/saída garantidas para qualquer topologia de transição de estado até 4 níveis de aninhamento de estado

  • Objetos ativos que suportam até 8 filas de eventos de execução simultânea, determinística e thread-safe57

  • Suporta sinais com largura de um byte (255 sinais) e um parâmetro escalável, que pode ser configurado como 0 (sem parâmetro), 1, 2 ou 4 bytes

  • Mecanismo de despacho direto de eventos usando a estratégia de enfileiramento FIFO primeiro a entrar, primeiro a sair

  • Cada objeto ativo tem um evento de tempo único (timer) com uma faixa dinâmica configurável de 0 (sem evento de tempo), 1, 2 ou 4 bytes

  • Kernel de baunilha cooperativo integrado

  • Kernel RTC preemptivo integrado chamado QK-nano (consulte o Capítulo 6, Seção 6.3.8)

  • Uma arquitetura de baixo consumo de energia com funções de retorno de chamada ocioso para fácil implementação de modos de economia de energia.

  • Preparado em código para extensões não padrão para compiladores C para arquiteturas populares de CPU de baixo custo (por exemplo, alocação de objetos constantes no espaço de código, funções reentrantes, etc.)

  • Estratégias de Tratamento de Erros Baseadas em Asserções

Estilo de código:

27e9906958761c0140d96b90fbc68a84.png 6ece219edb8d07606415b414bb1474d5.png 8e2d6eb33c4ffa43e7d3f99fe7aea466.png 1ffd7f83d338e6fb1e8d3a550ae7d5f4.png df2a80cb01e9927bc62bcfcdb7f9b521.png

Este artigo é compartilhado aqui, espero que seja útil para você.

Fonte: https://blog.csdn.net/qq_36969440/article/details/110387716

Isenção de responsabilidade: o material deste artigo vem da Internet e os direitos autorais pertencem ao autor original. Se envolver questões de direitos autorais, entre em contato comigo para excluir.

------------  FIM  ------------

f934ed2d8b9075edbd53c9c1b7049523.gif

●Coluna "Ferramentas incorporadas "

●Coluna "Desenvolvimento Integrado"

●Coluna "Tutorial Keil"

●Tutoriais selecionados na coluna incorporada

Preste atenção à conta oficial e responda " Jiagroup " para ingressar no grupo de intercâmbio técnico de acordo com as regras e responda " 1024 " para ver mais conteúdo.

71233eecf2f54969c2a830791d69b133.jpeg

8f2f3b8e80eb924dea0bf214c32c0923.png

Clique em " Ler o texto original " para visualizar mais compartilhamentos.

Acho que você gosta

Origin blog.csdn.net/ybhuangfugui/article/details/131862318
Recomendado
Clasificación