|
關(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)。“次態(tài)”是相對于“現(xiàn)態(tài)”而言的,“次態(tài)”一旦被激活,就轉(zhuǎn)變成新的“現(xiàn)態(tài)”了。
maakcob424164091890730.png (215.07 KB, 下載次數(shù): 0)
下載附件
保存到相冊
maakcob424164091890730.png
2024-12-11 07:12 上傳
傳統(tǒng)有限狀態(tài)機Fsm實現(xiàn)方法
mbcuaxwckeq64091890830.png (21.85 KB, 下載次數(shù): 0)
下載附件
保存到相冊
mbcuaxwckeq64091890830.png
2024-12-11 07:12 上傳
如圖,是一個定時計數(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;
}
}
owgdr0aeg4d64091890930.png (48.01 KB, 下載次數(shù): 0)
下載附件
保存到相冊
owgdr0aeg4d64091890930.png
2024-12-11 07:12 上傳
優(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)的躍遷。
e1pqljpsupu64091891030.png (73.66 KB, 下載次數(shù): 0)
下載附件
保存到相冊
e1pqljpsupu64091891030.png
2024-12-11 07:12 上傳
(*) 僅當(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)換表
lpybjzdqq1j64091891130.png (82.75 KB, 下載次數(shù): 0)
下載附件
保存到相冊
lpybjzdqq1j64091891130.png
2024-12-11 07:12 上傳
實現(xiàn)原理:
4z0d3ocgg1d64091891230.png (20.28 KB, 下載次數(shù): 0)
下載附件
保存到相冊
4z0d3ocgg1d64091891230.png
2024-12-11 07:12 上傳
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ù)量?蓪崿F(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>類和單一繼承
qsx4zv1l50n64091891330.png (37.17 KB, 下載次數(shù): 0)
下載附件
保存到相冊
qsx4zv1l50n64091891330.png
2024-12-11 07:12 上傳
工具QM :一個通過UML類圖來描述狀態(tài)機的軟件,并且可以自動生成C代碼
5psfllvtvak64091891431.png (219.95 KB, 下載次數(shù): 0)
下載附件
保存到相冊
5psfllvtvak64091891431.png
2024-12-11 07:12 上傳
QS軟件追蹤工具
ghyvxl5j0sc64091891531.png (460.97 KB, 下載次數(shù): 0)
下載附件
保存到相冊
ghyvxl5j0sc64091891531.png
2024-12-11 07:12 上傳
u3fjwrypz0w64091891631.png (250.29 KB, 下載次數(shù): 0)
下載附件
保存到相冊
u3fjwrypz0w64091891631.png
2024-12-11 07:12 上傳
QEP 實現(xiàn)有限狀態(tài)機 Fsm
實現(xiàn)
eze0bgqjeoe64091891731.png (20.92 KB, 下載次數(shù): 0)
下載附件
保存到相冊
eze0bgqjeoe64091891731.png
2024-12-11 07:12 上傳
/* 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簡介
kcqqeuvhlxe64091891831.png (45.3 KB, 下載次數(shù): 0)
下載附件
保存到相冊
kcqqeuvhlxe64091891831.png
2024-12-11 07:12 上傳
初始化:
bn5f5u4xjjc64091891931.png (24.19 KB, 下載次數(shù): 0)
下載附件
保存到相冊
bn5f5u4xjjc64091891931.png
2024-12-11 07:12 上傳
初始化層次狀態(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;
}
1b12cynbf1z64091892031.png (34.28 KB, 下載次數(shù): 0)
下載附件
保存到相冊
1b12cynbf1z64091892031.png
2024-12-11 07:12 上傳
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實時框架的組成
dlzmm2r4czz64091892131.png (45.68 KB, 下載次數(shù): 0)
下載附件
保存到相冊
dlzmm2r4czz64091892131.png
2024-12-11 07:12 上傳
hx45e2etr3d64091892231.png (56.46 KB, 下載次數(shù): 0)
下載附件
保存到相冊
hx45e2etr3d64091892231.png
2024-12-11 07:12 上傳
內(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ù)留空間,保證每個塊的對齊。
er3uhv24lbk64091892331.png (40.11 KB, 下載次數(shù): 0)
下載附件
保存到相冊
er3uhv24lbk64091892331.png
2024-12-11 07:12 上傳
事件隊列每一個活動對象維護一個事件隊列,事件都是由基礎(chǔ)事件派生的,不同類型的事件只需要將其基礎(chǔ)事件成員添加到活動對象的隊列中即可,最終在取出的時候通過一個強制轉(zhuǎn)換便能獲得附加的參數(shù)。
zljstepkmsm64091892431.png (73.64 KB, 下載次數(shù): 0)
下載附件
保存到相冊
zljstepkmsm64091892431.png
2024-12-11 07:12 上傳
事件派發(fā)
直接事件發(fā)送QActive_postLIFO()發(fā)行訂閱事件發(fā)送豎軸表示信號(為事件的基類)活動對象支持64個優(yōu)先級,每一個活動對象要求擁有唯一優(yōu)先級通過優(yōu)先級的bit位來表示某個事件被哪些活動對象訂閱,并在事件觸發(fā)后根據(jù)優(yōu)先級為活動對象派發(fā)事件。
voewdcedlqg64091892531.png (42.62 KB, 下載次數(shù): 0)
下載附件
保存到相冊
voewdcedlqg64091892531.png
2024-12-11 07:12 上傳
定時事件非有序鏈表
2a3kfj2onkv64091892631.png (37.9 KB, 下載次數(shù): 0)
下載附件
保存到相冊
2a3kfj2onkv64091892631.png
2024-12-11 07:12 上傳
合作式調(diào)度器QV
可搶占式調(diào)度器QK
QP nano 的簡介
完全支持層次式狀態(tài)嵌套,包括在最多4 層狀態(tài)嵌套情況下,對任何狀態(tài)轉(zhuǎn)換拓?fù)涞目杀WC的進入/ 退出動作
支持高達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)容。
點擊“閱讀原文”查看更多分享。 |
|