原文連結:嵌入式狀态機程式設計-QP狀态機架構與常見狀态機方法
狀态機基本術語
- 現态:是指目前所處的狀态。
- 條件:又稱為“事件”,當一個條件被滿足,将會觸發一個動作,或者執行一次狀态的遷移。
- 動作:條件滿足後執行的動作。動作執行完畢後,可以遷移到新的狀态,也可以仍舊保持原狀态。動作不是必需的,當條件滿足後,也可以不執行任何動作,直接遷移到新狀态。
- 次态:條件滿足後要遷往的新狀态。“次态”是相對于“現态”而言的,“次态”一旦被激活,就轉變成新的“現态”了。
傳統有限狀态機Fsm實作方法
如圖,是一個定時計數器,計數器存在兩種狀态,一種為設定狀态,一種為計時狀态
設定狀态
- “+” “-” 按鍵對初始倒計時進行設定
- 當計數值設定完成,點選确認鍵啟動計時 ,即切換到計時狀态
計時狀态
- 按下“+” “-” 會進行密碼的輸入“+”表示1 ,“-”表示輸入0 ,密碼共有4位
- 确認鍵:隻有輸入的密碼等于預設密碼,按确認鍵才能停止計時,否則計時直接到零,并執行相關操作
嵌套switch
/***************************************
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;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 優點
- 簡單,代碼閱讀連貫,容易了解
- 缺點
- 當狀态或事件增多時,代碼狀态函數需要經常改動,狀态事件處理函數會代碼量會不斷增加
- 狀态機沒有進行封裝,移植性差。
- 沒有實作狀态的進入和退出的操作。進入和退出在狀态機中尤為重要
- 進入事件: 隻會在剛進入時觸發一次,主要作用是對狀态進行必要的初始化
- 退出事件:隻會在狀态切換時觸發一次 ,主要的作用是清除狀态産生的中間參數,為下次進入提供幹淨環境
狀态表
二維狀态轉換表
狀态機可以分為狀态和事件 ,狀态的躍遷都是受事件驅動的,是以可以通過一個二維表格來表示狀态的躍遷。
(*) 僅當( code == defuse_code) 時才發生到setting 的轉換。
/*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);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 優點
- 各個狀态面向使用者相對獨立,增加事件和狀态不需要去修改先前已存在的狀态事件函數。
-
可将狀态機進行封裝,有較好的移植性
函數指針的安全轉換 , 利用下面的特性,使用者可以擴充帶有私有屬性的狀态機和事件而使用統一的基礎狀态機接口
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; }
- 1
- 2
- 3
- 4
- 5
- 缺點
- 函數粒度太小是最明顯的一個缺點,一個狀态和一個事件就會産生一個函數,當狀态和事件較多時,處理函數将增加很快,在閱讀代碼時,邏輯分散。
- 沒有實作進入退出動作。
一維狀态轉換表
實作原理:
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 狀态相關*/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 優點
- 各個狀态面向使用者相對獨立,增加事件和狀态不需要去修改先前已存在的狀态事件函數。
- 實作了狀态的進入和退出
- 容易根據狀态躍遷圖來設計 (狀态躍遷圖列出了每個狀态的躍遷可能,也就是這裡的轉換表)
- 實作靈活,可實作複雜邏輯,如上一次狀态,增加監護條件來減少事件的數量。可實作非完全事件驅動
- 缺點
- 函數粒度較小(比二維小且增長慢),可以看到,每一個狀态需要至少3個函數,還需要列出所有的轉換關系。
QP嵌入式實時架構
特點
- 事件驅動型程式設計
-
好萊塢原則:和傳統的順序式程式設計方法例如“超級循環”,或傳統
的RTOS 的任務不同。絕大多數的現代事件驅動型系統根據好萊塢原則被構造,
(Don’t call me; I’ll call you.)
-
- 面向對象
- 類和單一繼承
- 工具
- QM :一個通過UML類圖來描述狀态機的軟體,并且可以自動生成C代碼
- QS軟體追蹤工具
QEP實作有限狀态機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*/
}
}
實作上面定時器例子
#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();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 優點
- 采用面向對象的設計方法,很好的移植性
- 實作了進入退出動作
- 合适的粒度,且事件的粒度可控
- 狀态切換時通過改變指針,效率高
- 可擴充成為層次狀态機
- 缺點
- 對事件的定義以及事件粒度的控制是設計的最大難點,如序列槽接收到一幀資料,這些變量的更新單獨作為某個事件,還是序列槽收到資料作為一個事件。再或者顯示屏,如果使用此種程式設計方式,如何設計事件。
QP 實作層次狀态機 Hsm簡介
初始化
初始化層次狀态機的實作:在初始化時,使用者所選取的狀态永遠是最底層的狀态,如上圖,我們在電腦開機後,應該進入的是開始狀态,這就涉及到一個問題,由最初top(頂狀态)到begin 是有一條狀态切換路徑的,當我們設定狀态為begin如何搜尋這條路徑成為關鍵(知道了路徑才能正确的進入begin,要執行路徑中過渡狀态的進入和退出事件)
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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
狀态切換
/*.................................................................*/
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 */
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
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 */
....
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
QP實時架構的組成
- 記憶體管理
使用記憶體池,對于低性能mcu,記憶體極為有限,引入記憶體管理主要是整個架構中,是以事件作為主要的任務通信手段,且事件是帶參數的,可能相同類型的事件會多次觸發,而事件處理完成後,需要清除事件,無法使用靜态的事件,是以是有必要為不同僚件建立記憶體池的。
對于不同塊大小的記憶體池,需要考慮的是每個塊的起始位址對齊問題。在進行記憶體池初始化時,我們是根據blocksize+header大小來進行劃分記憶體池的。假設一個2位元組的結構,如果以2來進行劃分,假設mcu 4位元組對齊,那麼将有一半的結構起始位址無法對齊,這時需要為每個塊預留白間,保證每個塊的對齊。
- 事件隊列
- 每一個活動對象維護一個事件隊列,事件都是由基礎事件派生的,不同類型的事件隻需要将其基礎事件成員添加到活動對象的隊列中即可,最終在取出的時候通過一個強制轉換便能獲得附加的參數。
- 事件派發
- 直接事件發送
- QActive_postLIFO()
- 發行訂閱事件發送
- 豎軸表示信号(為事件的基類)
- 活動對象支援64個優先級,每一個活動對象要求擁有唯一優先級
- 通過優先級的bit位來表示某個事件被哪些活動對象訂閱,并在事件觸發後根據優先級為活動對象派發事件。
- 定時事件
- 非有序連結清單
- 合作式排程器QV
- 可搶占式排程器QK
QP nano的簡介
-
完全支援層次式狀态嵌套,包括在最多4 層狀态嵌套情況下,對任何狀态轉換拓撲的可保
證的進入/ 退出動作
- 支援高達8 個并發執行的,可确定的,線程安全的事件隊列的活動對象57
-
支援一個位元組寬( 255 個信号)的信号,和一個可伸縮的參數,它可被配置成0 (沒有參
數), 1 , 2 或4 位元組
- 使用先進先出FIFO排隊政策的直接事件派發機制
-
每個活動對象有一個一次性時間事件(定時器),它的可配置動态範圍是0(沒有時間事
件) , 1 , 2 或4 位元組
- 内建的合作式vanilla 核心
- 内建的名為QK-nano 的可搶占型RTC核心(見第六章6.3.8節)
- 帶有空閑回調函數的低功耗架構,用來友善的實作節省功耗模式。
-
在代碼裡為流行的低端CPU架構的C編譯器的非标準擴充進行了準備(例如,在代碼空
間配置設定常數對象,可重入函數,等等)
- 基于斷言的錯誤處理政策
- 代碼風格
參考資料
連結:https://pan.baidu.com/s/18LQhr7qumRSQvHqQgE4UnA
提取碼:qqqq
http://www.state-machine.com/ (QP官網)