?? rtos.c
字號:
一種裸奔多任務模型
一個網友的總結:stateMachine + timerTick + queue。
在RTOS環境下的多任務模型:
任務通常阻塞在一個OS調用上(比如從消息隊列取數據)。
外部如果想讓該任務運轉,就要向消息隊列發送消息。
任務收到消息時,根據當前狀態,決定如何處理消息。這就是狀態機。
任務將消息隊列中的消息處理完畢后,重新進入阻塞狀態。
任務在處理中,有時要延時一段時間,然后才繼續工作:
為了充分使用CPU,可以通過OS調用讓其它任務去工作。
OS通常會提供一個taskDelay調用。
當任務調用taskDelay時,即進入阻塞狀態,直到超時,才重新進入可工作狀態(就緒狀態)。
下面說說裸奔環境下的多任務模型:
裸奔也可以多任務,但調度是由用戶自主控制。
在RTOS環境下,一般提供搶占式調度。在裸奔時,一般是任務在處理告一段落后,主動結束處理。
RTOS環境下的任務,一般處于一個while(1)循環中。
while(1){
從消息隊列接收消息。如果沒有,將阻塞。
處理消息。
}
裸奔下的任務,一般采用查詢方式:
{
查詢是否有待處理的事件。
如果沒有,返回。
如果有,根據任務的當前狀態,進行處理。處理完畢后,可能返回,也可能將待處理事件全部處理完畢后再返回。
}
裸奔任務其實也處于一個while(1)循環中,只不過這個循環在任務外部。
main()
{
A_taskInit(); //任務的初始化
B_taskInit();
...
while(1){
A_taskProc(); //任務的處理
B_taskProc();
}
}
狀態機既適用于OS環境,也適用于裸奔環境。
但在裸奔環境下,狀態可能被切分得更細。例如后面講的如何在裸奔環境實現taskDelay()。
消息隊列既適用于OS環境,也適用于裸奔環境。
在OS環境下,消息隊列機制由OS提供。
在裸奔環境下,消息隊列要自己來實現。如果對隊列的概念不清楚,可參考《數據結構》教材。
這個隊列機制,可做成通用模塊,在不同的程序中復用。
消息隊列用于緩沖事件。事件不知道什么時候會到來,也不能保證來了就能迅速得到處理。
使用消息隊列,可以保證每個事件都被處理到,以及處理順序。
一般在兩種情況下會用到消息隊列:
存儲外部事件:外部事件由中斷收集,然后存儲到隊列。
串口接收程序中的接收循環緩沖區,可理解為消息隊列。
任務間通訊:一個任務給其它任務發送消息。
timerTick,就是系統中的時鐘基準。OS中總是有一個這樣的基準。
在裸奔時,我們要用一個定時器(或RTC或watchdog)來建立這個時間基準。
一個tick間隔可以設置為10ms(典型RTOS的缺省設置)。讓定時器10ms中斷一次,中斷發生時給tickNum++。
以前,我在定時器中斷中設置1S標志、200ms標志等等。時間相關的任務根據這些標志判斷是否要執行。
近來,一般讓任務直接去察看tickNum。兩次相減來判斷定時是否到達。
也可以在系統中建立一個通用定時器任務,管理與不同任務相關的多個定時器;在定時到達時,由定時器任務去調用相應的callback。
系統時鐘基準是所謂“零耗時裸奔”的基礎。
timerTick的分辨率,決定了只適于于較大的時間延時。
在做時序時的小延時,用傳統方法好了。
OS中的taskDelay()在裸奔環境下的一種實現:
OS環境:
void xxxTask(void)
{
while(1){
//waitEvent
//do step_1
taskDelay(TIME_OUT_TICK_NUM);
//do step_2
}
}
裸奔環境:
void xxxTask(void)
{
static unsigned int taskStat = STAT_GENERAL; //任務狀態變量
static timer_t startTick;
timer_t currTick;
if (taskStat == STAT_GENERAL)
{
//check event
//if no event
return;
//do step_1
startTick = sysGetTick(); //sysGetTick()就是察看系統時間
taskStat = STAT_WAIT;
return;
}
else if (taskStat == STAT_WAIT)
{
currTick = sysGetTick(); //sysGetTick()就是察看系統時間
if ((currTick - startTick) >= TIME_OUT_TICK_NUM)
{
//do step_2
taskStat = STAT_GENERAL;
return;
}
else
return;
}
}
//====================================================
uchar ucDisplayBarStatus;
uchar ucDisplayLength; //顯示長度
#define c_StatusBar_Start 1
#define c_StatusBar_Scroll 2
#define c_StatusBar_Off 3
uchar ucLoopCount;
uchar code ucStatusDispInformation[] = " WWW.21IC是一個好地方,歡迎大家經常光臨侃單片機版塊,古道熱腸版主研制Ver(1.00) ";
//滾動顯示幫助信息
void ScrollDisplayHelp(void)
{
if(ucDisplayBarStatus == c_StatusBar_Start)
{
//啟動狀態態顯示進程
ucDisplayLength = 0;
LcdShowString(4,1,ucStatusDispInformation);
//啟動定時器1進行延時
bDelayCompleteFlag_T1 = false;
TmrCfgFnct(1, Tmr1TDisp_P1, (void *)0); /* Execute when Timer #1 times out */
TmrSetMST(1,0, 0, 1);
TmrStart(1);
ucDisplayBarStatus = c_StatusBar_Scroll;
}
if(ucDisplayBarStatus == c_StatusBar_Scroll)
{
if(bDelayCompleteFlag_T1) //定時操作完成
{
bDelayCompleteFlag_T1 = false;
ucDisplayLength += 2;
if(ucDisplayLength > (strlen(ucStatusDispInformation)-16)) //結束滾動顯示
{
ucDisplayBarStatus = c_StatusBar_Off; //關閉顯示
LcdShowString(4,1," ");
}
else
{
LcdShowString(4,1,ucStatusDispInformation+ucDisplayLength);
TmrSetMST(1,0, 0, 1);
TmrStart(1);
}
}
}
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -