電子產(chǎn)業(yè)一站式賦能平臺

PCB聯(lián)盟網(wǎng)

搜索
查看: 52|回復(fù): 0
收起左側(cè)

嵌入式軟件架構(gòu)設(shè)計中的狀態(tài)模式

[復(fù)制鏈接]

418

主題

418

帖子

4293

積分

四級會員

Rank: 4

積分
4293
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2024-12-9 11:45:00 | 只看該作者 |只看大圖 回帖獎勵 |倒序瀏覽 |閱讀模式
關(guān)注+星標(biāo)公眾,不錯過精彩內(nèi)容
整理&排版 | 嵌入式應(yīng)用研究院

狀態(tài)機基本術(shù)語
現(xiàn)態(tài):是指當(dāng)前所處的狀態(tài)。
條件:又稱為“事件”,當(dāng)一個條件被滿足,將會觸發(fā)一個動作,或者執(zhí)行一次狀態(tài)的遷移。
動作:條件滿足后執(zhí)行的動作。動作執(zhí)行完畢后,可以遷移到新的狀態(tài),也可以仍舊保持原狀態(tài)。動作不是必需的,當(dāng)條件滿足后,也可以不執(zhí)行任何動作,直接遷移到新狀態(tài)。
次態(tài):條件滿足后要遷往的新狀態(tài)!按螒B(tài)”是相對于“現(xiàn)態(tài)”而言的,“次態(tài)”一旦被激活,就轉(zhuǎn)變成新的“現(xiàn)態(tài)”了。



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



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

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

計時狀態(tài)
按下“+” “-” 會進行密碼的輸入“+”表示1 ,“-”表示輸入0 ,密碼共有4位確認(rèn)鍵:只有輸入的密碼等于默認(rèn)密碼,按確認(rèn)鍵才能停止計時,否則計時直接到零,并執(zhí)行相關(guān)操作
嵌套switch

/***************************************
1.列出所有的狀態(tài)
***************************************/
typedef enum{
    SETTING,
    TIMING
} STATE_TYPE;
/***************************************
2.列出所有的事件
***************************************/
typedef enum{
    UP_EVT,
    DOWN_EVT,
    ARM_EVT,
    TICK_EVT
} EVENT_TYPE;
/***************************************
3.定義和狀態(tài)機相關(guān)結(jié)構(gòu)
***************************************/
struct  bomb
{
    uint8_t state;
    uint8_t timeout;
    uint8_t code;
    uint8_t defuse_code;
} bomb1;
/***************************************
4.初始化狀態(tài)機
***************************************/
void bomb1_init(void)
{
    bomb1.state = SETTING;
    bomb1.defuse_code = 6;    //0110
}
/***************************************
5. 狀態(tài)機事件派發(fā)
***************************************/
void bomb1_fsm_dispatch(EVENT_TYPE evt ,void* param)
{
    switch(bomb1.state)
    {
        case SETTING:
        {
            switch(evt)
            {
                case UP_EVT:    // "+"   按鍵按下事件
                    if(bomb1.timeout60)  
                        ++bomb1.timeout;
                    bsp_display(bomb1.timeout);
                break;
               
                case DOWN_EVT:  // "-"   按鍵按下事件
                    if(bomb1.timeout > 0)  
                        --bomb1.timeout;
                    bsp_display(bomb1.timeout);
                break;
               
                case ARM_EVT:   // "確認(rèn)" 按鍵按下事件
                    bomb1.state = TIMING;
                    bomb1.code  = 0;
                break;
            }
        }
        break;
        
        case TIMING:
        {
            switch(evt)
            {
                case UP_EVT:     // "+"   按鍵按下事件
                    bomb1.code = (bomb1.code 1) | 0x01;
                break;
               
                case DOWN_EVT:  // "-"   按鍵按下事件
                    bomb1.code = (bomb1.code 1);
                break;
               
                case ARM_EVT:   // "確認(rèn)" 按鍵按下事件
                    if(bomb1.code == bomb1.defuse_code)
                    {
                        bomb1.state = SETTING;
                    }
                    else
                    {
                        bsp_display("bomb!")
                    }
                break;
               
                case TICK_EVT:
                    if(bomb1.timeout)
                    {
                        --bomb1.timeout;
                        bsp_display(bomb1.timeout);
                    }
                    if(bomb1.timeout == 0)
                    {
                        bsp_display("bomb!")
                    }
                break;      
            }   
        }
        break;
    }
}

優(yōu)點
簡單,代碼閱讀連貫,容易理解
缺點
當(dāng)狀態(tài)或事件增多時,代碼狀態(tài)函數(shù)需要經(jīng)常改動,狀態(tài)事件處理函數(shù)會代碼量會不斷增加
狀態(tài)機沒有進行封裝,移植性差。
沒有實現(xiàn)狀態(tài)的進入和退出的操作。進入和退出在狀態(tài)機中尤為重要:
進入事件:只會在剛進入時觸發(fā)一次,主要作用是對狀態(tài)進行必要的初始化
退出事件:只會在狀態(tài)切換時觸發(fā)一次 ,主要的作用是清除狀態(tài)產(chǎn)生的中間參數(shù),為下次進入提供干凈環(huán)境

狀態(tài)表

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

(*) 僅當(dāng)( code == defuse_code) 時才發(fā)生到 setting 的轉(zhuǎn)換。
/*1.列出所有的狀態(tài)*/
enum
{
    SETTING,
    TIMING,
    MAX_STATE
};
/*2.列出所有的事件*/
enum
{
    UP_EVT,
    DOWN_EVT,
    ARM_EVT,
    TICK_EVT,
    MAX_EVT
};
      
/*3.定義狀態(tài)表*/
typedef void (*fp_state)(EVT_TYPE evt , void* param);
static  const fp_state  bomb2_table[MAX_STATE][MAX_EVENT] =
{
    {setting_UP, setting_DOWN, setting_ARM, null},
    {setting_UP, setting_DOWN, setting_ARM, timing_TICK}
};
      
struct bomb_t
{
    const fp_state const *state_table;  /* the State-Table */
    uint8_t state;  /* the current active state */
         
    uint8_t timeout;
    uint8_t code;
    uint8_t defuse_code;
};
struct bomb bomb2=
{
    .state_table = bomb2_table;
}
void bomb2_init(void)
{
    bomb2.defuse_code = 6; // 0110
    bomb2.state = SETTING;
}
      
void bomb2_dispatch(EVT_TYPE evt , void* param)
{
    fp_state  s = NULL;
    if(evt > MAX_EVT)
    {
        LOG("EVT type error!");
        return;
    }
    s = bomb2.state_table[bomb2.state * MAX_EVT + evt];
    if(s != NULL)
    {
        s(evt , param);
    }
}
/*列出所有的狀態(tài)對應(yīng)的事件處理函數(shù)*/
void setting_UP(EVT_TYPE evt, void* param)
{
    if(bomb1.timeout60)  
        ++bomb1.timeout;
    bsp_display(bomb1.timeout);
}
優(yōu)點各個狀態(tài)面向用戶相對獨立,增加事件和狀態(tài)不需要去修改先前已存在的狀態(tài)事件函數(shù)?蓪顟B(tài)機進行封裝,有較好的移植性函數(shù)指針的安全轉(zhuǎn)換 , 利用下面的特性,用戶可以擴展帶有私有屬性的狀態(tài)機和事件而使用統(tǒng)一的基礎(chǔ)狀態(tài)機接口typedef void (*Tran)(struct StateTableTag *me, Event const *e);
void Bomb2_setting_ARM (Bomb2 *me, Event const *e);

typedef struct Bomb
{
    struct StateTableTag *me;  //必須為第一個成員
    uint8_t private;
}
缺點
函數(shù)粒度太小是最明顯的一個缺點,一個狀態(tài)和一個事件就會產(chǎn)生一個函數(shù),當(dāng)狀態(tài)和事件較多時,處理函數(shù)將增加很快,在閱讀代碼時,邏輯分散。
沒有實現(xiàn)進入退出動作。
一維狀態(tài)轉(zhuǎn)換表

實現(xiàn)原理:

typedef void (*fp_action)(EVT_TYPE evt,void* param);
   
/*轉(zhuǎn)換表基礎(chǔ)結(jié)構(gòu)*/
struct tran_evt_t
{
    EVT_TYPE evt;
    uint8_t next_state;
};
/*狀態(tài)的描述*/
struct  fsm_state_t
{
    fp_action  enter_action;  // 進入動作
    fp_action  exit_action;   // 退出動作
    fp_action  action;           
        
    tran_evt_t* tran;    // 轉(zhuǎn)換表
    uint8_t     tran_nb; // 轉(zhuǎn)換表的大小
    const char* name;
}
/*狀態(tài)表本體*/
#define  ARRAY(x)   x,sizeof(x)/sizeof(x[0])
const struct fsm_state_t state_table[]=
{
    {setting_enter, setting_exit, setting_action, ARRAY(set_tran_evt), "setting" },
    {timing_enter, timing_exit, timing_action, ARRAY(time_tran_evt), "timing" }
};
   
/*構(gòu)建一個狀態(tài)機*/
struct fsm
{
    const struct state_t * state_table; /* the State-Table */
    uint8_t cur_state;     /* the current active state */
        
    uint8_t timeout;
    uint8_t code;
    uint8_t defuse_code;
} bomb3;
   
/*初始化狀態(tài)機*/
void bomb3_init(void)
{
    bomb3.state_table = state_table;  // 指向狀態(tài)表
    bomb3.cur_state = setting;
    bomb3.defuse_code = 8;  //1000
}
/*狀態(tài)機事件派發(fā)*/
void  fsm_dispatch(EVT_TYPE evt, void* param)
{
    tran_evt_t* p_tran = NULL;
        
    /*獲取當(dāng)前狀態(tài)的轉(zhuǎn)換表*/
    p_tran = bomb3.state_table[bomb3.cur_state]->tran;
        
    /*判斷所有可能的轉(zhuǎn)換是否與當(dāng)前觸發(fā)的事件匹配*/
    for(uint8_t i=0; iif(p_tran->evt == evt)  // 事件會觸發(fā)轉(zhuǎn)換
        {
            if(NULL != bomb3.state_table[bomb3.cur_state].exit_action)
            {
                bomb3.state_table[bomb3.cur_state].exit_action(NULL);  // 執(zhí)行退出動作
            }
            if(bomb3.state_table[_tran->next_state].enter_action)
            {
                bomb3.state_table[_tran->next_state].enter_action(NULL);  // 執(zhí)行進入動作
            }
            
            /*更新當(dāng)前狀態(tài)*/
            bomb3.cur_state = p_tran->next_state;
        }
        else
        {
            bomb3.state_table[bomb3.cur_state].action(evt, param);
        }
    }
}
/*************************************************************************
setting狀態(tài)相關(guān)
************************************************************************/
void setting_enter(EVT_TYPE evt, void* param)
{
        
}
void setting_exit(EVT_TYPE evt, void* param)
{
        
}
void setting_action(EVT_TYPE evt, void* param)
{
        
}
tran_evt_t set_tran_evt[] =
{
    {ARM , timing},
}
/*timing 狀態(tài)相關(guān)*/
優(yōu)點各個狀態(tài)面向用戶相對獨立,增加事件和狀態(tài)不需要去修改先前已存在的狀態(tài)事件函數(shù)。實現(xiàn)了狀態(tài)的進入和退出容易根據(jù)狀態(tài)躍遷圖來設(shè)計 (狀態(tài)躍遷圖列出了每個狀態(tài)的躍遷可能,也就是這里的轉(zhuǎn)換表)實現(xiàn)靈活,可實現(xiàn)復(fù)雜邏輯,如上一次狀態(tài),增加監(jiān)護條件來減少事件的數(shù)量。可實現(xiàn)非完全事件驅(qū)動缺點
函數(shù)粒度較。ū榷S小且增長慢),可以看到,每一個狀態(tài)需要至少3個函數(shù),還需要列出所有的轉(zhuǎn)換關(guān)系。

QP嵌入式實時框架

特點
事件驅(qū)動型編程好萊塢原則:和傳統(tǒng)的順序式編程方法例如“超級循環(huán)”,或傳統(tǒng)的RTOS 的任務(wù)不同。絕大多數(shù)的現(xiàn)代事件驅(qū)動型系統(tǒng)根據(jù)好萊塢原則被構(gòu)造,(Don’t call me; I’ll call you.)
面向?qū)ο?u>類和單一繼承


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


QS軟件追蹤工具






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


/* qevent.h ----------------------------------------------------------------*/
typedef struct QEventTag
{  
    QSignal sig;     
    uint8_t dynamic_;  
} QEvent;
/* qep.h -------------------------------------------------------------------*/
typedef uint8_t QState;  /* status returned from a state-handler function */
typedef QState (*QStateHandler) (void *me, QEvent const *e); /* argument list */
typedef struct QFsmtag  /* Finite State Machine */
{
    QStateHandler state;  /* current active state */
} QFsm;
      
#define QFsm_ctor(me_, initial_) ((me_)->state = (initial_))
void QFsm_init (QFsm *me, QEvent const *e);
void QFsm_dispatch(QFsm *me, QEvent const *e);
      
#define Q_RET_HANDLED ((QState)0)
#define Q_RET_IGNORED ((QState)1)
#define Q_RET_TRAN    ((QState)2)
#define Q_HANDLED()   (Q_RET_HANDLED)
#define Q_IGNORED()   (Q_RET_IGNORED)
      
#define Q_TRAN(target_) (((QFsm *)me)->state = (QStateHandler)   (target_),Q_RET_TRAN)
      
enum QReservedSignals
{
    Q_ENTRY_SIG = 1,
    Q_EXIT_SIG,
    Q_INIT_SIG,
    Q_USER_SIG
};
      
/* file qfsm_ini.c ---------------------------------------------------------*/
#include "qep_port.h"  /* the port of the QEP event processor */
#include "qassert.h"  /* embedded systems-friendly assertions */
void QFsm_init(QFsm *me, QEvent const *e)
{
    (*me->state)(me, e);  /* execute the top-most initial transition */
    /* enter the target */
    (void)(*me->state)(me , &QEP_reservedEvt_[Q_ENTRY_SIG]);
}
/* file qfsm_dis.c ---------------------------------------------------------*/
void QFsm_dispatch(QFsm *me, QEvent const *e)
{
    QStateHandler s = me->state;  /* save the current state */
    QState r = (*s)(me, e);  /* call the event handler */
    if (r == Q_RET_TRAN)  /* transition taken? */
    {
        (void)(*s)(me, &QEP_reservedEvt_[Q_EXIT_SIG]); /* exit the source */
        (void)(*me->state)(me, &QEP_reservedEvt_[Q_ENTRY_SIG]);/*enter target*/
    }
}
// 實現(xiàn)上面定時器例子
#include "qep_port.h" /* the port of the QEP event processor */
#include "bsp.h"      /* board support package */
      
enum BombSignals  /* all signals for the Bomb FSM */
{
    UP_SIG = Q_USER_SIG,
    DOWN_SIG,
    ARM_SIG,
    TICK_SIG
};
typedef struct TickEvtTag
{
    QEvent super;      /* derive from the QEvent structure */
    uint8_t fine_time; /* the fine 1/10 s counter */
} TickEvt;
      
typedef struct Bomb4Tag
{
    QFsm super;      /* derive from QFsm */
    uint8_t timeout; /* number of seconds till explosion */
    uint8_t code;    /* currently entered code to disarm the bomb */
    uint8_t defuse;  /* secret defuse code to disarm the bomb */
} Bomb4;
      
void Bomb4_ctor (Bomb4 *me, uint8_t defuse);
QState Bomb4_initial(Bomb4 *me, QEvent const *e);
QState Bomb4_setting(Bomb4 *me, QEvent const *e);
QState Bomb4_timing (Bomb4 *me, QEvent const *e);
/*--------------------------------------------------------------------------*/
/* the initial value of the timeout */
#define INIT_TIMEOUT 10
/*..........................................................................*/
void Bomb4_ctor(Bomb4 *me, uint8_t defuse) {
    QFsm_ctor_(&me->super, (QStateHandler)&Bomb4_initial);
    me->defuse = defuse;  /* the defuse code is assigned at instantiation */
}
/*..........................................................................*/
QState Bomb4_initial(Bomb4 *me, QEvent const *e)
{
    (void)e;
    me->timeout = INIT_TIMEOUT;
    return Q_TRAN(&Bomb4_setting);
}
/*..........................................................................*/
QState Bomb4_setting(Bomb4 *me, QEvent const *e)
{
    switch (e->sig)
    {
        case UP_SIG:
        {
            if (me->timeout 60)
            {
                ++me->timeout;
                BSP_display(me->timeout);
            }
            return Q_HANDLED();
        }
        
        case DOWN_SIG:
        {
            if (me->timeout > 1)
            {
                --me->timeout;
                BSP_display(me->timeout);
            }
            return Q_HANDLED();
        }
        case ARM_SIG:
        {
            return Q_TRAN(&Bomb4_timing); /* transition to "timing" */
        }
    }
    return Q_IGNORED();
}
      
/*..........................................................................*/
void Bomb4_timing(Bomb4 *me, QEvent const *e)
{
    switch (e->sig)
    {
        case Q_ENTRY_SIG:
        {
            me->code = 0; /* clear the defuse code */
            return Q_HANDLED();
        }
        
        case UP_SIG:
        {
            me->code 1;
            me->code |= 1;
            return Q_HANDLED();
        }
        
        case DOWN_SIG:
        {
            me->code 1;
            return Q_HANDLED();
        }
        
        case ARM_SIG:
        {
            if (me->code == me->defuse)
            {
                return Q_TRAN(&Bomb4_setting);
            }
            return Q_HANDLED();
        }
        
        case TICK_SIG:
        {
            if (((TickEvt const *)e)->fine_time == 0)
            {
                --me->timeout;
                BSP_display(me->timeout);
                if (me->timeout == 0)
                {
                    BSP_boom(); /* destroy the bomb */
                }
            }
            return Q_HANDLED();
        }
    }
    return Q_IGNORED();
}
優(yōu)點
采用面向?qū)ο蟮脑O(shè)計方法,很好的移植性
實現(xiàn)了進入退出動作
合適的粒度,且事件的粒度可控
狀態(tài)切換時通過改變指針,效率高
可擴展成為層次狀態(tài)機
缺點對事件的定義以及事件粒度的控制是設(shè)計的最大難點,如串口接收到一幀數(shù)據(jù),這些變量的更新單獨作為某個事件,還是串口收到數(shù)據(jù)作為一個事件。再或者顯示屏,如果使用此種編程方式,如何設(shè)計事件。

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


初始化:

初始化層次狀態(tài)機的實現(xiàn):在初始化時,用戶所選取的狀態(tài)永遠(yuǎn)是最底層的狀態(tài),如上圖,我們在計算器開機后,應(yīng)該進入的是開始狀態(tài)。這就涉及到一個問題,由最初top(頂狀態(tài))到begin 是有一條狀態(tài)切換路徑的,當(dāng)我們設(shè)置狀態(tài)為 begin,如何搜索這條路徑成為關(guān)鍵(知道了路徑才能正確的進入begin,要執(zhí)行路徑中過渡狀態(tài)的進入和退出事件)void QHsm_init(QHsm *me, QEvent const *e)
{
    Q_ALLEGE((*me->state)(me, e) == Q_RET_TRAN);
    t = (QStateHandler)&QHsm_top; /* HSM starts in the top state */
    do
    {  /* drill into the target... */
        QStateHandler path[QEP_MAX_NEST_DEPTH_];
        int8_t ip = (int8_t)0; /* transition entry path index */
        path[0] = me->state; /* 這里的狀態(tài)為begin */
            
        /*通過執(zhí)行空信號,從底層狀態(tài)找到頂狀態(tài)的路徑*/
        (void)QEP_TRIG_(me->state, QEP_EMPTY_SIG_);
        while (me->state != t)
        {
            path[++ip] = me->state;
            (void)QEP_TRIG_(me->state, QEP_EMPTY_SIG_);
        }
        /*切換為begin*/
        me->state = path[0]; /* restore the target of the initial tran. */
        /* 鉆到最底層的狀態(tài),執(zhí)行路徑中的所有進入事件 */
        Q_ASSERT(ip int8_t)QEP_MAX_NEST_DEPTH_);
        do
        {  /* retrace the entry path in reverse (desired) order... */
            QEP_ENTER_(path[ip]); /* enter path[ip] */
        }
        while ((--ip) >= (int8_t)0);
            
        t = path[0]; /* current state becomes the new source */
    }
    while (QEP_TRIG_(t, Q_INIT_SIG) == Q_RET_TRAN);
    me->state = t;
}

t = path[0]; /* target of the transition */
if (s == t)
{  /* (a) check source==target (transition to self) */
    QEP_EXIT_(s) /* exit the source */
    ip = (int8_t)0; /* enter the target */
}
else
{
    (void)QEP_TRIG_(t, QEP_EMPTY_SIG_); /* superstate of target */
    t = me->state;
    if (s == t)
    {  /* (b) check source==target->super */
        ip = (int8_t)0; /* enter the target */
    }
    else
    {
        (void)QEP_TRIG_(s, QEP_EMPTY_SIG_); /* superstate of src */
        /* (c) check source->super==target->super */
        if(me->state == t)
        {
            QEP_EXIT_(s) /* exit the source */
            ip = (int8_t)0; /* enter the target */
        }
        else
        {
            /* (d) check source->super==target */
            if (me->state == path[0])
            {
                QEP_EXIT_(s) /* exit the source */
            }
            else
            {  /* (e) check rest of source==target->super->super..
                * and store the entry path along the way */
                 ....

QP實時框架的組成



內(nèi)存管理
使用內(nèi)存池,對于低性能mcu,內(nèi)存極為有限,引入內(nèi)存管理主要是整個架構(gòu)中,是以事件作為主要的任務(wù)通信手段,且事件是帶參數(shù)的?赡芟嗤愋偷氖录䲡啻斡|發(fā),而事件處理完成后,需要清除事件,無法使用靜態(tài)的事件,因此是有必要為不同事件創(chuàng)建內(nèi)存池的。對于不同塊大小的內(nèi)存池,需要考慮的是每個塊的起始地址對齊問題。在進行內(nèi)存池初始化時,我們是根據(jù) blocksize+header 大小來進行劃分內(nèi)存池的。假設(shè)一個 2 字節(jié)的結(jié)構(gòu),如果以 2 來進行劃分,假設(shè) mcu 4 字節(jié)對齊,那么將有一半的結(jié)構(gòu)起始地址無法對齊,這時需要為每個塊預(yù)留空間,保證每個塊的對齊。


事件隊列每一個活動對象維護一個事件隊列,事件都是由基礎(chǔ)事件派生的,不同類型的事件只需要將其基礎(chǔ)事件成員添加到活動對象的隊列中即可,最終在取出的時候通過一個強制轉(zhuǎn)換便能獲得附加的參數(shù)。


事件派發(fā)
直接事件發(fā)送QActive_postLIFO()發(fā)行訂閱事件發(fā)送豎軸表示信號(為事件的基類)活動對象支持64個優(yōu)先級,每一個活動對象要求擁有唯一優(yōu)先級通過優(yōu)先級的bit位來表示某個事件被哪些活動對象訂閱,并在事件觸發(fā)后根據(jù)優(yōu)先級為活動對象派發(fā)事件。


定時事件非有序鏈表


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


可搶占式調(diào)度器QK
QP nano 的簡介

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















聲明:本文素材來源網(wǎng)絡(luò),版權(quán)歸原作者所有。如涉及作品版權(quán)問題,請與我聯(lián)系刪除。------------ END ------------

●專欄《嵌入式工具●專欄《嵌入式開發(fā)》●專欄《Keil教程》●嵌入式專欄精選教程
關(guān)注公眾號回復(fù)“加群”按規(guī)則加入技術(shù)交流群,回復(fù)“1024”查看更多內(nèi)容。
點擊“閱讀原文”查看更多分享。
回復(fù)

使用道具 舉報

發(fā)表回復(fù)

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則


聯(lián)系客服 關(guān)注微信 下載APP 返回頂部 返回列表