移植μC/OS-Ⅱ
圖 8.2 在ISR執(zhí)行過程中的堆棧內(nèi)容.
接著,CPU會調(diào)用正確的ISR。μC/OS-Ⅱ要求用戶的ISR在開始時要保存剩下的處理器寄存器[F8.2(2)]。一旦寄存器保存好了,μC/OS-Ⅱ就要求用戶或者調(diào)用OSIntEnter(),或者將變量OSIntNesting加1。在這個時候,被中斷任務(wù)的堆棧中只包含了被中斷任務(wù)的寄存器內(nèi)容?,F(xiàn)在,ISR可以執(zhí)行中斷服務(wù)了。并且如果ISR發(fā)消息給任務(wù)(通過調(diào)用OSMboxPost()或OSQPost()), 恢復(fù)任務(wù)(通過調(diào)用OSTaskResume()), 或者調(diào)用OSTimeTick()或OSTimeDlyResume()的話,有可能使更高優(yōu)先級的任務(wù)處于就緒狀態(tài)。
假設(shè)有一個更高優(yōu)先級的任務(wù)處于就緒狀態(tài)。μC/OS-Ⅱ要求用戶的ISR在完成中斷服務(wù)的時候調(diào)用OSIntExit()。OSIntExit()會告訴μC/OS-Ⅱ到了返回任務(wù)級代碼的時間了。
調(diào)用OSIntExit()會導(dǎo)致調(diào)用者的返回地址被保存到被中斷的任務(wù)的堆棧中[F8.2(3)]。
OSIntExit()剛開始時會禁止中斷,因為它需要執(zhí)行臨界段的代碼。根據(jù)OS_ENTER_CRITICAL()的不同執(zhí)行過程(參看8.03.02),處理器的狀態(tài)寄存器會被保存到被中斷的任務(wù)的堆棧中[F8.2(4)]。OSIntExit()注意到由于有更高優(yōu)先級的任務(wù)處于就緒狀態(tài),被中斷的任務(wù)已經(jīng)不再是要繼續(xù)執(zhí)行的任務(wù)了。在這種情況下,指針OSTCBHighRdy會被指向新任務(wù)的OS_TCB,并且OSIntExit()會調(diào)用OSIntCtxSw()來執(zhí)行任務(wù)切換。調(diào)用OSIntCtxSw()也同樣使返回地址被保存到被中斷的任務(wù)的堆棧中[F8.2(5)]。
在用戶切換任務(wù)的時候,用戶只想將某些項([F8.2(1)]和[F8.2(2)])保留在堆棧中,并忽略其它項(F8.2(3),(4)和(5)) 。這是通過調(diào)整堆棧指針(加一個數(shù)在堆棧指針上)來完成的[F8.2(6)]。加在堆棧指針上的數(shù)必須是明確的,而這個數(shù)主要依賴于移植的目標(biāo)處理器(地址空間可能是16,32或64位),所用的編譯器,編譯器選項,內(nèi)存模式等等。另外,處理器狀態(tài)字可能是8,16,32甚至64位寬,并且OSIntExit()可能會分配局部變量。有些處理器允許用戶直接增加常量到堆棧指針中,而有些則不允許。在后一種情況下,可以通過簡單的執(zhí)行一定數(shù)量的pop(出棧)指令來實現(xiàn)相同的功能。一旦堆棧指針完成調(diào)整,新的堆棧指針會被保存到被切換出去的任務(wù)的OS_TCB中[F8.2(7)]。
OSIntCtxSw()的原型如程序清單L8.3所示。這些代碼必須寫在匯編語言中,因為用戶不能直接從C語言中訪問CPU寄存器。如果用戶的編譯器支持插入?yún)R編語言代碼的話,用戶就可以將OSIntCtxSw()代碼放到OS_CPU_C.C文件中,而不放到OS_CPU_A.ASM文件中。正如用戶所看到的那樣,除了第一行以外,OSIntCtxSw()的代碼與OSCtxSw()是一樣的。這樣在移植實例中,用戶可以通過“跳轉(zhuǎn)”到OSCtxSw()中來減少 OSIntCtxSw()代碼量。
程序清單 L8.3 OSIntCtxSw()的原型
voidOSIntCtxSw(void)
{
調(diào)整堆棧指針來去掉在調(diào)用:
OSIntExit(),
OSIntCtxSw()過程中壓入堆棧的多余內(nèi)容;
將當(dāng)前任務(wù)堆棧指針保存到當(dāng)前任務(wù)的OS_TCB中:
OSTCBCur->OSTCBStkPtr= 堆棧指針;
調(diào)用用戶定義的OSTaskSwHook();
OSTCBCur=OSTCBHighRdy;
OSPrioCur=OSPrioHighRdy;
得到需要恢復(fù)的任務(wù)的堆棧指針:
堆棧指針 =OSTCBHighRdy->OSTCBStkPtr;
將所有處理器寄存器從新任務(wù)的堆棧中恢復(fù)出來;
執(zhí)行中斷返回指令;
}
OSIntCtxSw()是μC/OS-Ⅱ(和μC/OS)中唯一的與編譯器相關(guān)的函數(shù);在我收到的e-mail中,關(guān)于該函數(shù)的e-mail明顯多于關(guān)于μC/OS其它方面的。如果在多次任務(wù)切換后用戶的系統(tǒng)崩潰了,用戶應(yīng)該懷疑堆棧指針在OSIntCtxSw()中是否被正確地調(diào)整了。
8.04.04OSTickISR()
μC/OS-Ⅱ要求用戶提供一個時鐘資源來實現(xiàn)時間的延時和期滿功能。時鐘節(jié)拍應(yīng)該每秒鐘發(fā)生10-100次。 為了完成該任務(wù), 可以使用硬件時鐘, 也可以從交流電中獲得50/60Hz的時鐘頻率。
用戶必須在開始多任務(wù)調(diào)度后(即調(diào)用OSStart()后)允許時鐘節(jié)拍中斷。換句話說,就是用戶應(yīng)該在OSStart()運行后,μC/OS-Ⅱ啟動運行的第一個任務(wù)中初始化節(jié)拍中斷。通常所犯的錯誤是在調(diào)用OSInit()和OSStart()之間允許時鐘節(jié)拍中斷(如程序清單L8.4所示)。
程序清單 L8.4 在不正確的位置啟動時鐘節(jié)拍中斷
voidmain(void)
{
.
.
OSInit();/* 初始化 ? μC/OS-II*/
.
.
/* 應(yīng)用程序初始化代碼 ...*/
/*... 調(diào)用OSTaskCreate()建立至少一個任務(wù) */
.
.
允許時鐘節(jié)拍中斷;/* 千萬不要在這里允許!!! */
.
.
OSStart();/* 開始多任務(wù)調(diào)度 */
}
有可能在μC/OS-Ⅱ開始執(zhí)行第一個任務(wù)前時鐘節(jié)拍中斷就發(fā)生了。在這種情況下,μC/OS-Ⅱ的運行狀態(tài)不確定,用戶的應(yīng)用程序也可能會崩潰。
時鐘節(jié)拍ISR的原型如程序清單L8.5所示。這些代碼必須寫在匯編語言中,因為用戶不能直接從C語言中訪問CPU寄存器。如果用戶的處理器可以通過單條指令來增加OSIntNesting,那么用戶就沒必要調(diào)用 OSIntEnter()了。增加OSIntNesting要比通過函數(shù)調(diào)用和返回快得多。OSIntEnter()只增加OSIntNesting,并且作為臨界段代碼中受到保護(hù)。
程序清單 L8.5 時鐘節(jié)拍ISR的原型
voidOSTickISR(void)
{
保存處理器寄存器;
調(diào)用OSIntEnter()或者直接將 OSIntNesting加1;
調(diào)用OSTimeTick();
調(diào)用OSIntExit();
恢復(fù)處理器寄存器;
執(zhí)行中斷返回指令;
}
8.05OS_CPU_C.C
μC/OS-Ⅱ的移植實例要求用戶編寫六個簡單的C函數(shù):
OSTaskStkInit()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskSwHook()
OSTaskStatHook()
OSTimeTickHook()
唯一必要的函數(shù)是OSTaskStkInit(),其它五個函數(shù)必須得聲明但沒必要包含代碼。
評論