Patrón de estado en el diseño de arquitectura de software integrado

Sigue la cuenta pública + star y nunca te pierdas contenido interesante

cf039b586e1874a2d49a92a310ae7591.gif

Fuente | Electrónica integrada

Disposición y composición tipográfica | Instituto de investigación de aplicaciones integradas

Terminología básica de la máquina de estados

  • Estado actual : se refiere al estado actual.

  • Condición : También conocida como "evento", cuando se cumple una condición, se activará una acción o se realizará una transición de estado.

  • Acción : Acción realizada después de que se cumple la condición. Una vez ejecutada la acción, se puede mover a un nuevo estado o permanecer en el estado original. No se requieren acciones. Cuando se cumplan las condiciones, podrá migrar directamente al nuevo estado sin realizar ninguna acción.

  • Estado secundario : el nuevo estado al que se trasladará una vez que se cumplan las condiciones. El "estado secundario" es relativo al "estado actual". Una vez que se activa el "estado secundario", se transforma en un nuevo "estado presente".

22fe7ec580fa6fd36cfc24bfe5418f50.png

Método de implementación tradicional de máquina de estados finitos Fsm

da71c2366e20899fb5db54fa2c586330.png

Como se muestra en la figura, es un contador de tiempo. El contador tiene dos estados, uno es el estado de configuración y el otro es el estado de tiempo.

Establecer estado

  • Botones "+" "-" para configurar la cuenta atrás inicial

  • Cuando se complete la configuración del valor de conteo, haga clic en el botón de confirmación para iniciar el cronometraje, es decir, cambiar al estado de cronometraje.

Estado de tiempo

  • Presione "+" "-" para ingresar la contraseña. "+" significa 1, "-" significa ingresar 0. La contraseña tiene 4 dígitos en total.

  • Tecla de confirmación: solo cuando la contraseña ingresada es igual a la contraseña predeterminada, presionar la tecla de confirmación puede detener el tiempo; de lo contrario, el tiempo llegará directamente a cero y se realizarán las operaciones relevantes.

Interruptores anidados

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

bc2cbe812118e5310926c5fd53998c9e.png

ventaja

Simple, el código es coherente y fácil de entender.

defecto

  • Cuando aumenta el número de estados o eventos, la función de estado del código debe cambiarse con frecuencia y la cantidad de código de la función de procesamiento de eventos de estado seguirá aumentando.

  • La máquina de estados no está encapsulada y tiene poca portabilidad.

  • No hay implementación de operaciones estatales de entrada y salida. La entrada y la salida son particularmente importantes en las máquinas de estados:

  • Evento de entrada: solo se activará una vez cuando ingrese por primera vez. Su función principal es realizar la inicialización necesaria del estado.

  • Evento de salida: Solo se activará una vez cuando el estado cambie, su función principal es borrar los parámetros intermedios generados por el estado y proporcionar un ambiente limpio para la siguiente entrada.

tabla de estado

Tabla de transición de estado bidimensional

Las máquinas de estados se pueden dividir en estados y eventos. Las transiciones de estado son impulsadas por eventos, por lo que las transiciones de estado se pueden representar mediante una tabla bidimensional.

10b8ff0efe899b97f909fc0283a0e5dd.png

(*)  La conversión a configuración solo ocurre cuando (código == 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);
}
  • ventaja

    • Cada estado es relativamente independiente para los usuarios. Agregar eventos y estados no requiere modificar las funciones de eventos de estado previamente existentes.

    • La máquina de estados se puede encapsular y tiene mejor portabilidad.

Conversión segura de punteros de función. Utilizando las siguientes funciones, los usuarios pueden ampliar las máquinas de estado y eventos con atributos privados y utilizar una interfaz 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;
}
  • defecto

  • La deficiencia más obvia es que la granularidad de la función es demasiado pequeña. Un estado y un evento generarán una función. Cuando hay muchos estados y eventos, las funciones de procesamiento aumentarán rápidamente y la lógica se dispersará al leer el código.

  • Las acciones de entrada y salida no se implementan.

Tabla de transición de estado unidimensional

ff174258d416f60820a2d445608c21b7.png

Principio de implementación:

d056a867a17b6f340c12ae34794b2cb2.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 状态相关*/
  • ventaja

    • Cada estado es relativamente independiente para los usuarios. Agregar eventos y estados no requiere modificar las funciones de eventos de estado previamente existentes.

    • Entrada y salida de estado implementada.

    • Fácil de diseñar basado en el diagrama de transición de estado (el diagrama de transición de estado enumera las posibles transiciones de cada estado, que es la tabla de transición aquí)

    • La implementación es flexible y puede implementar lógica compleja, como el último estado y agregar condiciones de monitoreo para reducir la cantidad de eventos. Se puede implementar como impulsado por eventos no completo

  • defecto

    • La granularidad de la función es menor (más pequeña que bidimensional y crece más lentamente). Se puede ver que cada estado requiere al menos 3 funciones y se deben enumerar todas las relaciones de conversión.

Marco QP integrado en tiempo real

Características

  • programación basada en eventos

    • Principio de Hollywood: diferente de los métodos tradicionales de programación secuencial, como el "superbucle" o las tareas RTOS tradicionales. La mayoría de los sistemas modernos basados ​​en eventos están estructurados según el principio de Hollywood (no me llames; yo te llamaré).

  • orientado a objetos

    • Clases y herencia única.

ae889861d885cedc1f3327b5a9a32e59.png

  • herramienta

    • QM  : un software que describe máquinas de estado a través de diagramas de clases UML y puede generar código C automáticamente.

df70b58ad6d1b5d263c5bac9e9c3f737.png

    • Herramienta de seguimiento del software QS

6c36e97ecf4bb938196041c36fedd932.png

cda53e363ee2cf736b48dbafd5d92d7d.png

QEP implementa la máquina de estados finitos Fsm

  • lograr

a255a96034d03da20f14efa83482e8cd.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();
}
  • ventaja

    • Adopte un método de diseño orientado a objetos y tenga buena portabilidad.

    • Acciones de entrada y salida implementadas.

    • Granularidad adecuada y la granularidad de los eventos es controlable.

    • Al cambiar el puntero al cambiar de estado, es eficiente

    • Extensible para convertirse en una máquina de estados jerárquica.

  • defecto

    • La definición de eventos y el control de la granularidad de los eventos son las mayores dificultades en el diseño. Por ejemplo, cuando el puerto serie recibe una trama de datos, la actualización de estas variables se trata como un evento separado, o el puerto serie recibe datos como un evento. O la pantalla de visualización. Si se utiliza este método de programación, cómo diseñar eventos.

Introducción a la implementación QP de la máquina de estados jerárquica Hsm

4cc21e7275da56673348c49150a87568.png

inicialización:

456aa95c8f2900c0cc8ade520533f300.png

Implementación de la máquina de estado jerárquico de inicialización: durante la inicialización, el estado seleccionado por el usuario es siempre el estado más bajo. Como se muestra en la figura anterior, después de encender la calculadora, debemos ingresar al estado inicial.

Esto implica un problema. Hay una ruta de cambio de estado desde la parte superior inicial hasta comenzar. Cuando configuramos el estado para comenzar, cómo buscar esta ruta se convierte en la clave (solo conociendo la ruta podemos ingresar el comienzo correctamente. Ejecutar Eventos de entrada y salida para estados de transición en la ruta)

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

23759703e7bffd70628d4f1051b9ca66.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 */
                 ....

La composición del marco de tiempo real QP.

b8767b1cee08967c898016bfc7e137e9.png

1c6a3737b06e3ba722c99f0cedf62af8.png

Gestión de la memoria

Al usar un grupo de memoria, para MCU de bajo rendimiento, la memoria es extremadamente limitada. La introducción de la administración de memoria se realiza principalmente en toda la arquitectura, utilizando eventos como el principal medio de comunicación de tareas, y los eventos tienen parámetros. El mismo tipo de evento puede activarse varias veces. Una vez completado el procesamiento del evento, es necesario borrar el evento. Los eventos estáticos no se pueden utilizar, por lo que es necesario crear grupos de memoria para diferentes eventos.

Para grupos de memoria con diferentes tamaños de bloques, lo que se debe considerar es la alineación de la dirección inicial de cada bloque. Al inicializar el grupo de memoria, lo dividimos según el tamaño del bloque + el tamaño del encabezado. Suponiendo una estructura de 2 bytes, si se divide por 2, suponiendo que el mcu esté alineado con 4 bytes, la mitad de la dirección inicial de la estructura no estará alineada. En este caso, se debe reservar espacio para cada bloque para garantizar que cada bloque Alineación.

aeebd263e2a01dad910ba7208564d47b.png

cola de eventos

  • Cada objeto activo mantiene una cola de eventos. Los eventos se derivan de eventos básicos. Los diferentes tipos de eventos solo necesitan agregar sus miembros de eventos básicos a la cola del objeto activo. Finalmente, se pueden eliminar mediante una conversión forzada. Obtenga parámetros adicionales.

96089c0aa6fd104b955df5b5a3ed4f3a.png

despacho de eventos

  • Envío directo de eventos

    • QActive_postLIFO()

  • Envío de evento de suscripción de emisión

    • El eje vertical representa señales (la clase base de eventos)

    • Los objetos activos admiten 64 prioridades y cada objeto activo debe tener una prioridad única.

    • El bit de prioridad se utiliza para indicar qué objetos activos están suscritos a un evento y, una vez que se activa el evento, el evento se envía a los objetos activos de acuerdo con la prioridad.

c5aa6920fc8e3da56757d8c85f06a528.png

eventos cronometrados

  • Lista enlazada desordenada

7bc2a0cf58b965b18a52a6a61ddd3c4c.png

  • Programador cooperativo QV

e5a3a0a8ba0c779d40a0d7f06d9a7590.png

  • Programador preferente QK

Introducción a QP nano

  • Soporte completo para anidamiento de estados jerárquicos, incluidas acciones de entrada/salida garantizadas para cualquier topología de transición de estado hasta 4 niveles de anidamiento de estados.

  • Admite hasta 8 objetos activos de cola de eventos seguros para subprocesos, deterministas y que se ejecutan simultáneamente57

  • Admite señales de un byte de ancho (255 señales) y un parámetro escalable, que se puede configurar en 0 (sin parámetros), 1, 2 o 4 bytes.

  • Mecanismo de envío directo de eventos que utiliza la estrategia de cola FIFO de primero en entrar, primero en salir

  • Cada objeto activo tiene un evento de tiempo único (temporizador) con un rango dinámico configurable de 0 (sin evento de tiempo), 1, 2 o 4 bytes

  • Núcleo de vainilla cooperativo incorporado

  • Kernel RTC interrumpible incorporado llamado QK-nano

  • Una arquitectura de bajo consumo con una función de devolución de llamada inactiva para implementar fácilmente modos de ahorro de energía.

  • El código está preparado para extensiones no estándar de compiladores de C para arquitecturas de CPU populares de gama baja (por ejemplo, asignación de objetos constantes en el espacio de código, funciones reentrantes, etc.)

  • Estrategia de manejo de errores basada en afirmaciones

  • estilo de codificación

b7a8e9283dd21930aa65654fffac5deb.png

c5eab36d1b283d1fdb1cf5ba22e0e1be.png

cacdfc6b7be4e6ce236b309b0680c219.png

5ffa207468c897fafcdf2f7583715cab.png

9fff78fef20639776a5cc9517bb07650.png

Declaración: El material de este artículo proviene de Internet y los derechos de autor pertenecen al autor original. Si hay algún problema de derechos de autor con el trabajo, comuníquese conmigo para eliminarlo.

------------  FIN  ------------

e85b7f64bfd7002b4b82c4658fa3c8ec.gif

●Columna "Herramientas integradas "

●Columna "Desarrollo integrado"

●Columna "Tutorial de Keil"

●Tutoriales seleccionados de columnas integradas

Siga la cuenta oficial y responda " Agregar grupo " para unirse al grupo de intercambio técnico de acuerdo con las reglas, y responda " 1024 " para ver más contenido.

ed27e2e3578f5a23fbdd39ec2f557dce.jpeg

6bd65b7210957543e8187da13c615b61.png

Haga clic en " Leer el texto original " para ver más contenido compartido.

Supongo que te gusta

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