μC/OSII具有小巧、性能穩定、開源等眾多優點,并且μC/OSII大部分用ANSI C語言編寫,系統的移植非常容易。在μC/OSII I2.81及以后的版本中[2],加入了對軟件定時器的支持,使得μC/OSII操作系統更加完善。
μC/OSII是一種基于優先級的搶占式操作系統,實時性很強。而系統中軟件定時器沒有優先級,回調函數順序執行,這樣就降低了系統的實時性。因此,本文對軟件定時器進行改進,定時器中加入優先級,回調函數按優先級執行,從而提高系統的實時性。
1 對軟件定時器的介紹
μC/OSII系統中的時間管理功能包括任務延時與軟件定時器,而軟件定時器的主要作用是,對函數周期性或者一次性執行的定時,利用軟件定時器控制塊與“定時器輪”管理軟件定時器。定時器控制塊的結構如同任務控制塊,創建一個定時器時,從空閑定時器控制塊鏈表中得到一個空閑控制塊,并對其賦值。
軟件定時器也需要一個時鐘節拍驅動,而這個驅動一般是硬件實現的,一般使用μC/OSII操作系統中任務延時的時鐘節拍來驅動軟件定時器。每個時鐘節拍OSTmrCtr(全局變量,初始值為0)增1, 當OSTmrCtr的值等于為OS_TICKS_PER_SEC /OS_TMR_CFG_TICKS_PER_SEC(此兩者的商決定軟件定時器的頻率)時,調用函數OSTmrSignal(),此函數發送信號量OSTmrSemSignal(初始值為0,決定軟件定時器掃描任務OSTmr_Task的運行)。也就是說,對定時器的處理不在時鐘節拍中斷函數中進行,而是以發生信號量的方式激活任務OSTmr_Task(具有很高的優先級)。任務OSTmr_Task對定時器進行檢測處理,包括定時器定時完成的判斷、回調函數的執行。
μC/OSII 2.86中與軟件定時器相關的函數包括:
?、?軟件定時器內部靜態函數。獲取與釋放定時器控制塊函數OSTmr_Alloc()、OSTmr_Free();定時器插入相應“時間輪”組函數OSTmr_Link();從相應“時間輪”組中刪除定時器函數OSTmr_Unlink();軟件定時器任務初始化函數OSTmr_InitTask();定時器掃描任務OSTmr_Task;定時器上鎖與解鎖函數OSTmr_Lock()與OSTmr_Unlock()(在μC/OSII 2.91中,此兩函數被任務調度鎖定與解鎖函數代替)。
② 定時器外部接口函數。定時器創建與刪除函數OSTmrCreate()、OSTmrDel();定時器啟動與停止函數OSTmrStart()、OSTmrStop();定時器剩余時間與當前狀態查詢函數OSTmrRemainGet()、OSTmrStateGet();軟件定時器的初始化OSTmr_Init();發送信號量OSTmrSemSignal函數OSTmrSignal();定時器名稱查詢函數OSTmrNameGet()。
由于軟件定時器的回調函數的執行都是在任務OSTmr_Task中執行,如果多個定時器同時定時完成,則在定時器任務中執行多個定時器的回調函數,因此定時器任務的執行時間不確定。而且定時器回調函數是順序執行的,如果某個定時器回調函數需要盡快執行以實現精確定時,就難以實現了。由于各個定時器沒有優先級,因此了影響系統的實時性。
2 對軟件定時器的改進
為提高軟件定時器回調函數執行的實時性,給每個定時器賦予一個優先級。當定時完成時,并且定時器的回調函數不為空,則把定時器的優先級寫于軟件定時器就緒表中。任務OSTmr_Task對相應“時間輪”檢查結束后,如果在掃描各個定時器前軟件定時器就緒表為零而掃描之后不為零,則發送信號量激活回調函數任務OSTmr_TaskCallback。在此任務中,回調函數根據軟件定時器就緒表中的優先級執行相應的回調函數,這樣就提高了系統的實時性。
2.1 對軟件定時器相關數據結構改進
① 定義結構體OS_TMR_CALL,存儲定時器的回調函數、函數的參數、定時器指針,形式如下:
typedefstructos_tmr_call {
OS_TMR_CALLBACKOSTmrCallback; /*回調函數*/
void *OSTmrCallbackArg;/*回調函數指針*/
OS_TMR *OSTmr; /*定時器指針*/
} OS_TMR_CALL;
在頭文件ucos_ii.h中,定義OSTmrCallbackTbl[OS_TMR_CFG_MAX],OS_TMR_CFG_MAX表示系統中配置的軟件定時器數量。
?、?在軟件定時器控制塊中加入成員變量OSTmrPrio(定時器優先級),刪去變量OSTmrCallback(回調函數)、OSTmrCallbackArg(回調函數參數),為了測試的方便,可暫不刪除這兩個變量。
?、?定義定時器就緒表:
INT8UOSTmrRdyGrp;
INT8UOSTmrRdyTbl[OS_TMR_CFG_MAX/8 + 1];
當定時器定時完成時,把定時器優先級寫入就緒表,回調函數任務根據優先級執行回調函數。
?、?定義信號量OSTmrSemCallback(初始值0 ),當定時完成后,發送此信號量,激活回調函數任務,以執行回調函數。
2.2 與軟件定時器相關的函數函數與任務的改進
2.2.1 軟件定時器創建函數OSTmrCreate
在創建函數OSTmrCreate的參數中加入優先級參數prio。調用創建函數時,對定時器控制塊中的成員變量賦值,并給回調函數數組的相應單元賦值,形式如下:
OSTmrCallbackTbl [prio].OSTmrCallback = callback;
OSTmrCallbackTbl [prio].OSTmrCallbackArg = callback_arg;
OSTmrCallbackTbl [prio].OSTmr = ptmr;
2.2.2 對定時器任務OSTmr_Task的改進
當有定時器定時完成,把定時器優先級寫入軟件定時器就緒表中,并根據就緒表前后的值判斷時候發送信號量OSTmrSemSignal,以激活回調函數任務。任務OSTmr_Task的流程如圖1所示。
?
圖1 OSTmr_Task的流程
把定時器優先級寫入定時器就緒表的代碼如下所示:
if (OSTmrTime == ptmr?>OSTmrMatch) {
prio = ptmr?>OSTmrPrio;
pfnct =OSTmrCall[prio].OSTmrCallback;
if (pfnct != (OS_TMR_CALLBACK)0) { /*加入定時器回調函數就緒表*/
OSTmrRdyGrp|= (INT8U)(1 《 (INT8U)(prio 》 0x03));
OSTmrRdyTbl[prio >> 0x03]|= (INT8U)(1 《 (INT8U)(prio & 0x07));
}
}
2.2.3 對定時器停止函數OSTmrStop()的修改
函數OSTmrStop只需修改與回調函數執行相關的部分即可,例如,case OS_TMR_OPT_CALLBACK_ARG: 部分的代碼如下:
case OS_TMR_OPT_CALLBACK_ARG:
prio = ptmr?>OSTmrPrio;
pfnct = OSTmrCall[prio].OSTmrCallback;
if (pfnct != (OS_TMR_CALLBACK)0) {
……/*prio加入定時器就緒表*/
OSTmrCall[prio].OSTmrCallbackArg =(void *)callback_arg;
OSSemPost(OSTmrSemCallback); /*發送回調函數執行信號量*/
}else {
*perr = OS_ERR_TMR_NO_CALLBACK;
}
而case OS_TMR_OPT_CALLBACK:部分的代碼同上,只是回調函數的參數不需要重新賦值。
2.2.4 回調函數任務OSTmr_TaskCallback()
在源文件tmr.c中加入回調函數任務OSTmr_TaskCallback(),根據定時器就緒表中的優先級執行相應回調函數,回調函數任務的結構如下所示:
static voidOSTmr_TaskCallback(void *p_arg) {……/*變量定義*/
for (;;){//請求信號量OSTmrSemCallback
OSSemPend(OSTmrSemCallback, 0, &err);
OSTmr_Lock();/*定時器上鎖*/
while (OSTmrRdyGrp) {
……/*從定時器就緒表中得到最高優先級的定時器回調函數*/
……/*刪除就緒表中的占有位*/
OSTmr_Unlock(); /*定時器上鎖*/
pfnct = OSTmrCall[prio].OSTmrCallback;
(*pfnct)((void *)(OSTmrCall[prio].OSTmr),OSTmrCall[prio].OSTmrCallbackArg); /*執行回調函數*/
OSTmr_Lock(); /*定時器上鎖*/
}
OSTmr_Unlock();/*定時器解鎖*/
}
}
由以上代碼可知,訪問就緒表時定時器上鎖,而執行回調函數時處于定時器解鎖狀態。如果回調函數執行時間較長,在下一個軟件定時器節拍到來時,定時器掃描任務可以得到及時的執行,當前回調函數執行完成后,可以及時得執行就緒表中最高優先級定時器的回調函數。由此可以看出,高優先級定時器的回調函數得到及時執行,系統的實時性提高。
實驗測試發現,在回調函數任務OSTmr_TaskCallback中,使用任務調度上鎖與解鎖比使用定時器上鎖與解鎖(即信號量的請求)執行速度快一些。畢竟回調函數任務的優先級很高(一般僅次于定時器掃描任務OSTmr_Task的優先級),所以使用任務調度鎖定比定時器鎖定要好一些。當然,還可以使用開關中斷的方式對就緒表進行訪問,可以根據實際情況選擇使用哪種方式。
3 實驗測試
本次實驗使用軟件開發環境IAR 5.30,以基于CortexM3內核的路虎LPC1768開發板作為硬件實驗平臺[6],對實時操作系統μC/OSII 2.86進行改進。
對改進后的操作系統進行測試,在主函數中創建一個啟動任務,在啟動任務中創建4個周期定時器(系統中“時間輪”數設為4),賦予不同優先級與定時值,每個定時器控制一個LED的閃爍,啟動這4個定時器。在啟動函數中創建4個任務,每個任務也是控制一個LED燈的閃爍(利用任務延時),之后啟動任務掛起。利用μC/OSII CSPY插件觀察各定時器的運行情況,如圖2所示。
?
圖2 軟件定時器運行界面
經實驗測試,系統運行正常,定時器回調函數得到及時的執行,系統實時性得到很大的提高。
4 結語
軟件定時器改進后,定時器任務的執行時間確定,僅與同時完成定時的定時器數目有關,對處于就緒表中的定時器回調函數按優先級執行,使高優先級定時器的回調函數得到及時的執行,提高了系統的實時性。
評論
查看更多