移植μC/OS-Ⅱ
8.05.01OSTaskStkInt()
OSTaskCreate()和OSTaskCreateExt()通過調(diào)用OSTaskStkInt()來初始化任務(wù)的堆棧結(jié)構(gòu),因此,堆棧看起來就像剛發(fā)生過中斷并將所有的寄存器保存到堆棧中的情形一樣。圖8.3顯示了OSTaskStkInt()放到正被建立的任務(wù)堆棧中的東西。注意,在這里我假定了堆棧是從上往下長(zhǎng)的。下面的討論同樣適用于從下往上長(zhǎng)的堆棧。
在用戶建立任務(wù)的時(shí)候,用戶會(huì)傳遞任務(wù)的地址,pdata指針,任務(wù)的堆棧棧頂和任務(wù)的優(yōu)先級(jí)給OSTaskCreate()和OSTaskCreateExt()。雖然OSTaskCreateExt()還要求有其它的參數(shù),但這些參數(shù)在討論OSTaskStkInt()的時(shí)候是無關(guān)緊要的。為了正確初始化堆棧結(jié)構(gòu),OSTaskStkInt()只要求剛才提到的前三個(gè)參數(shù)和一個(gè)附加的選項(xiàng),這個(gè)選項(xiàng)只能在OSTaskCreateExt()中得到。
圖 8.3 堆棧初始化(pdata通過堆棧傳遞)

回顧一下,在μC/OS-Ⅱ中,無限循環(huán)的任務(wù)看起來就像其它的C函數(shù)一樣。當(dāng)任務(wù)開始被μC/OS-Ⅱ執(zhí)行時(shí),任務(wù)就會(huì)收到一個(gè)參數(shù),好像它被其它的任務(wù)調(diào)用一樣。
voidMyTask(void*pdata)
{
/* 對(duì)'pdata'做某些操作 */
for(;;){
/* 任務(wù)代碼 */
}
}
如果我想從其它的函數(shù)中調(diào)用MyTask(),C編譯器就會(huì)先將調(diào)用MyTask()的函數(shù)的返回地址保存到堆棧中,再將參數(shù)保存到堆棧中。實(shí)際上有些編譯器會(huì)將pdata參數(shù)傳至一個(gè)或多個(gè)寄存器中。在后面我會(huì)討論這類情況。假定pdata會(huì)被編譯器保存到堆棧中,OSTaskStkInit()就會(huì)簡(jiǎn)單的模仿編譯器的這種動(dòng)作,將pdata保存到堆棧中[F8.3(1)]。但是結(jié)果表明,與C函數(shù)調(diào)用不一樣,調(diào)用者的返回地址是未知的。用戶所擁有的是任務(wù)的開始地址,而不是調(diào)用該函數(shù)(任務(wù))的函數(shù)的返回地址!事實(shí)上用戶不必太在意這點(diǎn),因?yàn)槿蝿?wù)并不希望返回到其它函數(shù)中。
這時(shí),用戶需要將寄存器保存到堆棧中,當(dāng)處理器發(fā)現(xiàn)并開始執(zhí)行中斷的時(shí)候,它會(huì)自動(dòng)地完成該過程的。一些處理器會(huì)將所有的寄存器存入堆棧,而其它一些處理器只將部分寄存器存入堆棧。一般而言,處理器至少得將程序計(jì)數(shù)器的值(中斷返回地址)和處理器的狀態(tài)字存入堆棧[F8.3(2)]。很明顯,處理器是按一定的順序?qū)⒓拇嫫鞔嫒攵褩5模脩粼趯⒓拇嫫鞔嫒攵褩5臅r(shí)候也就必須依照這一順序。
接著,用戶需要將剩下的處理器寄存器保存到堆棧中[F8.3(3)]。保存的命令依賴于用戶的處理器是否允許用戶保存它們。 有些處理器用一個(gè)或多個(gè)指令就可以馬上將許多寄存器都保存起來。用戶必須用特定的指令來完成這一過程。例如,Intel80x86使用PUSHA指令將8個(gè)寄存器保存到堆棧中。對(duì)Motorola68HC11處理器而言,在中斷響應(yīng)期間,所有的寄存器都會(huì)按一定順序自動(dòng)的保存到堆棧中,所以在用戶將寄存器存入堆棧的時(shí)候,也必須依照這一順序。
現(xiàn)在是時(shí)候討論這個(gè)問題了: 如果用戶的C編譯器將pdata參數(shù)傳遞到寄存器中而不是堆棧中該作些什么?用戶需要從編譯器的文檔中找到pdata儲(chǔ)存在哪個(gè)寄存器中。pdata的內(nèi)容就會(huì)隨著這個(gè)寄存器的儲(chǔ)存被放置在堆棧中。
圖 8.4 堆棧初始化(pdata通過寄存器傳遞)

一旦用戶初始化了堆棧,OSTaskStkInit()就需要返回堆棧指針?biāo)傅牡刂穂F8.3(4)]。
OSTaskCreate()和OSTaskCreateExt()會(huì)獲得該地址并將它保存到任務(wù)控制塊(OS_TCB)中。
處理器文檔會(huì)告訴用戶堆棧指針會(huì)指向下一個(gè)堆棧空閑位置, 還是會(huì)指向最后存入數(shù)據(jù)的堆棧單元位置。例如,對(duì)Intel80x86處理器而言,堆棧指針會(huì)指向最后存入數(shù)據(jù)的堆棧單元位置,而對(duì)Motorola68HC11處理器而言,堆棧指針會(huì)指向下一個(gè)空閑的位置。
8.05.02OSTaskCreateHook()
當(dāng)用OSTaskCreate()或OSTaskCreateExt()建立任務(wù)的時(shí)候就會(huì)調(diào)用OSTaskCreateHook()。該函數(shù)允許用戶或使用用戶的移植實(shí)例的用戶擴(kuò)展μC/OS-Ⅱ的功能。
當(dāng)μC/OS-Ⅱ設(shè)置完了自己的內(nèi)部結(jié)構(gòu)后,會(huì)在調(diào)用任務(wù)調(diào)度程序之前調(diào)用OSTaskCreateHook()。該函數(shù)被調(diào)用的時(shí)候中斷是禁止的。因此用戶應(yīng)盡量減少該函數(shù)中的代碼以縮短中斷的響應(yīng)時(shí)間。
當(dāng)OSTaskCreateHook()被調(diào)用的時(shí)候,它會(huì)收到指向已建立任務(wù)的OS_TCB的指針,這樣它就可以訪問所有的結(jié)構(gòu)成員了。當(dāng)使用OSTaskCreate()建立任務(wù)時(shí),OSTaskCreateHook()的功能是有限的。但當(dāng)用戶使用OSTaskCreateExt()建立任務(wù)時(shí),用戶會(huì)得到OS_TCB中的擴(kuò)展指針OSTCBExtPtr),該指針可用來訪問任務(wù)的附加數(shù)據(jù),如浮點(diǎn)寄存器,MMU寄存器,任務(wù)計(jì)數(shù)器的內(nèi)容,以及調(diào)試信息。
只用當(dāng)OS_CFG.H中的OS_CPU_HOOKS_EN被置為1時(shí)才會(huì)產(chǎn)生OSTaskCreateHook()的代碼。這樣,使用用戶的移植實(shí)例的用戶可以在其它的文件中重新定義hook函數(shù)。
8.05.03OSTaskDelHook()
當(dāng)任務(wù)被刪除的時(shí)候就會(huì)調(diào)用OSTaskDelHook()。 該函數(shù)在把任務(wù)從μC/OS-Ⅱ的內(nèi)部任務(wù)鏈表中解開之前被調(diào)用。當(dāng)OSTaskDelHook()被調(diào)用的時(shí)候,它會(huì)收到指向正被刪除任務(wù)的OS_TCB的指針, 這樣它就可以訪問所有的結(jié)構(gòu)成員了。 OSTaskDelHook()可以用來檢驗(yàn)TCB擴(kuò)展是否被建立了(一個(gè)非空指針)并進(jìn)行一些清除操作。OSTaskDelHook()不返回任何值。
只用當(dāng)OS_CFG.H中的OS_CPU_HOOKS_EN被置為1時(shí)才會(huì)產(chǎn)生OSTaskDelHook()的代碼。
8.05.04OSTaskSwHook()
當(dāng)發(fā)生任務(wù)切換的時(shí)候調(diào)用OSTaskSwHook()。不管任務(wù)切換是通過OSCtxSw()還是OSIntCtxSw()來執(zhí)行的都會(huì)調(diào)用該函數(shù)。OSTaskSwHook()可以直接訪問OSTCBCur和OSTCBHighRdy,因?yàn)樗鼈兪侨肿兞俊STCBCur指向被切換出去的任務(wù)的OS_TCB,而OSTCBHighRdy指向新任務(wù)的OS_TCB。 注意在調(diào)用OSTaskSwHook()期間中斷一直是被禁止的。
因?yàn)榇a的多少會(huì)影響到中斷的響應(yīng)時(shí)間,所以用戶應(yīng)盡量使代碼簡(jiǎn)化。OSTaskSwHook()沒有任何參數(shù),也不返回任何值。
評(píng)論