1.08 例2
例2使用了帶擴展功能的任務建立函數OSTaskCreateExt()和uCOS-II的堆疊檢查操作(要使用堆疊檢查操作必須用OSTaskCreateExt()建立任務—譯者注)。當用戶不知道應該給任務分配多少堆疊空間時,堆疊檢查功能是很有用的。在這個例子裏,先分配足夠的堆疊空間給任務,然後用堆疊檢查操作看看任務到底需要多少堆疊空間。顯然,任務要運行足夠長時間,並要考慮各種情況才能得到正確資料。最後決定的堆疊大小還要考慮系統今後的擴展,一般多分配10%,25%或者更多。如果系統對穩定性要求高,則應該多一倍以上。
uCOS-II的堆疊檢查功能要求任務建立時堆疊清零。OSTaskCreateExt()可以執行此項操作(設置選項OS_TASK_OPT_STK_CHK和OS_TASK_OPT_STK_CLR打開此項操作)。如果任務運行過程中要進行建立、刪除任務的操作,應該設置好上述的選項,確保任務建立後堆疊是清空的。同時要意識到OSTaskCreateExt()進行堆疊清零操作是一項很費時的工作,而且取決於堆疊的大小。執行堆疊檢查操作的時候,uCOS-II從棧底向棧頂搜索非0元素(參看圖F 1.1),同時用一個計數器記錄0元素的個數。
例2的磁片文件爲\SOFTWARE\uCOS-II\EX2_x86L,它包含9個任務。加上uCOS-II本身的兩個任務:空閒任務(idle task)和統計任務。與例1一樣TaskStart()由main()函數建立,其功能是建立其他任務並在螢幕上顯示如下的統計資料:
每秒種任務切換的次數;
CPU利用率的百分比;
當前日期和時間;
uCOS_II的版本號;
圖F 1.1 µC/OS-II stack checking.
1.08.01 main()
例2的main()函數和例1的看起來差不多(參看程式清單L1.11),但是有兩處不同。第一,main()函數調用PC_ElapsedInit()[程式清單L1.11(1)]來初始化計時器記錄OSTaskStkChk()的執行時間。第二,所有的任務都使用OSTaskCreateExt()函數來建立任務[程式清單L1.11(2)](替代老版本的OSTaskCreate()),這使得每一個任務都可進行堆疊檢查。
程式清單 L 1.11 例2中的Main()函數. |
void main (void) |
{ |
PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK); |
OSInit(); |
PC_DOSSaveReturn(); |
PC_VectSet(uCOS, OSCtxSw); |
PC_ElapsedInit(); (1) |
OSTaskCreateExt(TaskStart, (2) |
(void *)0, |
&TaskStartStk[TASK_STK_SIZE-1], |
TASK_START_PRIO, |
TASK_START_ID, |
&TaskStartStk[0], |
TASK_STK_SIZE, |
(void *)0, |
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); |
OSStart(); |
} |
除了OSTaskCreate()函數的四個參數外,OSTaskCreateExt()還需要五個參數(一共9個):任務的ID,一個指向任務堆疊棧底的指標,堆疊的大小(以堆疊單元爲單位,80X86中爲字),一個指向用戶定義的TCB擴展資料結構的指標,和一個用於指定對任務操作的變數。該變數的一個選項就是用來設定uCOS-II堆疊檢查是否允許。例2中並沒有用到TCB擴展資料結構指標。
1.08.02TaskStart()
程式清單L1.12列出了TaskStart()的虛擬碼。前五項操作和例1中相同。TaskStart()建立了兩個郵箱,分別提供給任務4和任務5[程式清單L1.12(1)]。除此之外,還建立了一個專門顯示時間和日期的任務。
程式清單 L 1.12 TaskStart()的虛擬碼。. |
void TaskStart (void *data) |
{ |
Prevent compiler warning by assigning ‘data’ to itself; |
Display a banner and non-changing text; |
Install uC/OS-II’s tick handler; |
Change the tick rate to 200 Hz; |
Initialize the statistics task; |
Create 2 mailboxes which are used by Task #4 and #5; (1) |
Create a task that will display the date and time on the screen; (2) |
Create 5 application tasks; |
for (;;) { |
Display #tasks running; |
Display CPU usage in %; |
Display #context switches per seconds; |
Clear the context switch counter; |
Display uC/OS-II’s version; |
If (Key was pressed) { |
if (Key pressed was the ESCAPE key) { |
Return to DOS; |
} |
} |
Delay for 1 second; |
} |
} |
1.08.03 TaskN()
任務1將檢查其他七個任務堆疊的大小,同時記錄OSTackStkChk()函數的執行時間[程式清單L1.13(1)–(2)],並與堆疊大小一起顯示出來。注意所有堆疊的大小都是以位元組爲單位的。任務1每秒執行10次[程式清單L1.13(3)](間隔100ms)。
程式清單 L 1.13 例2, 任務1 |
void Task1 (void *pdata) |
{ |
INT8U err; |
OS_STK_DATA data; |
INT16U time; |
INT8U i; |
char s[80]; |
pdata = pdata; |
for (;;) { |
for (i = 0; i < 7; i++) { |
PC_ElapsedStart(); (1) |
err = OSTaskStkChk(TASK_START_PRIO+i, &data) |
time = PC_ElapsedStop(); (2) |
if (err == OS_NO_ERR) { |
sprintf(s, "%3ld %3ld %3ld %5d", |
data.OSFree + data.OSUsed, |
data.OSFree, |
data.OSUsed, |
time); |
PC_DispStr(19, 12+i, s, DISP_FGND_YELLOW); |
} |
} |
OSTimeDlyHMSM(0, 0, 0, 100); (3) |
} |
} |
程式清單L1.14所示的任務2在螢幕上顯示一個順時針旋轉的指標(用橫線,斜線等字元表示—譯者注),每200ms旋轉一格。
程式清單 L 1.14 任務2 |
void Task2 (void *data) |
{ |
data = data; |
for (;;) { |
PC_DispChar(70, 15, '|', DISP_FGND_WHITE + DISP_BGND_RED); |
OSTimeDly(10); |
PC_DispChar(70, 15, '/', DISP_FGND_WHITE + DISP_BGND_RED); |
OSTimeDly(10); |
PC_DispChar(70, 15, '-', DISP_FGND_WHITE + DISP_BGND_RED); |
OSTimeDly(10); |
PC_DispChar(70, 15, '\\', DISP_FGND_WHITE + DISP_BGND_RED); |
OSTimeDly(10); |
} |
} |
任務3(程式清單 L1.15)也顯示了與任務2相同的一個旋轉指標,但是旋轉的方向不同。任務3在堆疊中分配了一個很大的陣列,將堆疊填充掉,使得OSTaskStkChk()只需花費很少的時間來確定堆疊的利用率,尤其是當堆疊已經快滿的時候。
程式清單 L 1.15 任務3 |
void Task3 (void *data) |
{ |
char dummy[500]; |
INT16U i; |
data = data; |
for (I = 0; i < 499; i++) { |
dummy[i] = '?'; |
} |
for (;;) { |
PC_DispChar(70, 16, '|', DISP_FGND_WHITE + DISP_BGND_BLUE); |
OSTimeDly(20); |
PC_DispChar(70, 16, '\\', DISP_FGND_WHITE + DISP_BGND_BLUE); |
OSTimeDly(20); |
PC_DispChar(70, 16, '-', DISP_FGND_WHITE + DISP_BGND_BLUE); |
OSTimeDly(20); |
PC_DispChar(70, 16, '/', DISP_FGND_WHITE + DISP_BGND_BLUE); |
OSTimeDly(20); |
} |
} |
任務4(程式清單L1.16)向任務5發送消息並等待確認[程式清單L1.16(1)]。發送的消息是一個指向字元的指標。每當任務4從任務5收到確認[程式清單L1.16(2)],就將傳遞的ASCII碼加1再發送[程式清單L1.16(3)],結果是不斷的傳送“ABCDEFG....”。
程式清單 L 1.16 任務4 |
void Task4 (void *data) |
{ |
char txmsg; |
INT8U err; |
data = data; |
txmsg = 'A'; |
for (;;) { |
while (txmsg <= 'Z') { |
OSMboxPost(TxMbox, (void *)&txmsg); (1) |
OSMboxPend(AckMbox, 0, &err); (2) |
txmsg++; (3) |
} |
txmsg = 'A'; |
} |
} |
當任務5 [程式清單L1.17]接收消息後[程式清單L1.17(1)](發送的字元),就將消息顯示到螢幕上[程式清單L1.17(2)],然後延時1秒[程式清單L1.17(3)],再向任務4發送確認資訊。
程式清單 L 1.17 任務5 |
void Task5 (void *data) |
{ |
char *rxmsg; |
INT8U err; |
data = data; |
for (;;) { |
rxmsg = (char *)OSMboxPend(TxMbox, 0, &err); (1) |
PC_DispChar(70, 18, *rxmsg, DISP_FGND_YELLOW+DISP_BGND_RED); (2) |
OSTimeDlyHMSM(0, 0, 1, 0); (3) |
OSMboxPost(AckMbox, (void *)1); (4) |
} |
} |
TaskClk()函數[程式清單L1.18]顯示當前日期和時間,每秒更新一次。
程式清單 L 1.18 時鐘顯示任務 |
void TaskClk (void *data) |
{ |
Struct time now; |
Struct date today; |
char s[40]; |
data = data; |
for (;;) { |
PC_GetDateTime(s); |
PC_DispStr(0, 24, s, DISP_FGND_BLUE + DISP_BGND_CYAN); |
OSTimeDly(OS_TICKS_PER_SEC); |
} |
} |
1.09例3
例3中使用了許多uCOS-II提供的附加功能。任務3使用了OSTaskCreateExt()中TCB的擴展資料結構,用戶定義的任務切換對外介面函數(OSTaskSwHook()),用戶定義的統計任務(statistic task )的對外介面函數(OSTaskStatHook())以及消息佇列。例3的磁片文件是\SOFTWARE\uCOS-II\EX3_x86L,它包括9個任務。除了空閒任務(idle task)和統計任務(statistic task ),還有7個任務。與例1,例2一樣,TaskStart()由main()函數建立,其功能是建立其他任務,並顯示統計資訊。
1.09.01 main()
main()函數[程式清單L1.19]和例2中的相不多,不同的是在用戶定義的TCB擴展資料結構中可以保存每個任務的名稱[程式清單L1.19(1)](擴展結構的聲明在INCLUDES.H中定義,也可參看程式清單L1.20)。筆者定義了30個位元組來存放任務名(包括空格)[程式清單L1.20(1)]。本例中沒有用到堆疊檢查操作,TaskStart()中禁止該操作[程式清單L1.19(2)]。
程式清單 L 1.19 例3的main()函數 |
void main (void) |
{ |
PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK); |
OSInit(); |
PC_DOSSaveReturn(); |
PC_VectSet(uCOS, OSCtxSw); |
PC_ElapsedInit(); |
Strcpy(TaskUserData[TASK_START_ID].TaskName, "StartTask"); (1) |
OSTaskCreateExt(TaskStart, |
(void *)0, |
&TaskStartStk[TASK_STK_SIZE-1], |
TASK_START_PRIO, |
TASK_START_ID, |
&TaskStartStk[0], |
TASK_STK_SIZE, |
&TaskUserData[TASK_START_ID], |
0); (2) |
OSStart(); |
} |
程式清單 L 1.20 TCB擴展資料結構。 |
typedef struct { |
char TaskName[30]; (1) |
INT16U TaskCtr; |
INT16U TaskExecTime; |
INT32U TaskTotExecTime; |
} TASK_USER_DATA; |
1.09.02任務
TaskStart()的虛擬碼