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