移植μC/OS-Ⅱ
8.05.01OSTaskStkInt()
OSTaskCreate()和OSTaskCreateExt()通過調(diào)用OSTaskStkInt()來初始化任務(wù)的堆棧結(jié)構(gòu),因此,堆??雌饋砭拖駝偘l(fā)生過中斷并將所有的寄存器保存到堆棧中的情形一樣。圖8.3顯示了OSTaskStkInt()放到正被建立的任務(wù)堆棧中的東西。注意,在這里我假定了堆棧是從上往下長的。下面的討論同樣適用于從下往上長的堆棧。
在用戶建立任務(wù)的時候,用戶會傳遞任務(wù)的地址,pdata指針,任務(wù)的堆棧棧頂和任務(wù)的優(yōu)先級給OSTaskCreate()和OSTaskCreateExt()。雖然OSTaskCreateExt()還要求有其它的參數(shù),但這些參數(shù)在討論OSTaskStkInt()的時候是無關(guān)緊要的。為了正確初始化堆棧結(jié)構(gòu),OSTaskStkInt()只要求剛才提到的前三個參數(shù)和一個附加的選項,這個選項只能在OSTaskCreateExt()中得到。
圖 8.3 堆棧初始化(pdata通過堆棧傳遞)
回顧一下,在μC/OS-Ⅱ中,無限循環(huán)的任務(wù)看起來就像其它的C函數(shù)一樣。當(dāng)任務(wù)開始被μC/OS-Ⅱ執(zhí)行時,任務(wù)就會收到一個參數(shù),好像它被其它的任務(wù)調(diào)用一樣。
voidMyTask(void*pdata)
{
/* 對'pdata'做某些操作 */
for(;;){
/* 任務(wù)代碼 */
}
}
如果我想從其它的函數(shù)中調(diào)用MyTask(),C編譯器就會先將調(diào)用MyTask()的函數(shù)的返回地址保存到堆棧中,再將參數(shù)保存到堆棧中。實際上有些編譯器會將pdata參數(shù)傳至一個或多個寄存器中。在后面我會討論這類情況。假定pdata會被編譯器保存到堆棧中,OSTaskStkInit()就會簡單的模仿編譯器的這種動作,將pdata保存到堆棧中[F8.3(1)]。但是結(jié)果表明,與C函數(shù)調(diào)用不一樣,調(diào)用者的返回地址是未知的。用戶所擁有的是任務(wù)的開始地址,而不是調(diào)用該函數(shù)(任務(wù))的函數(shù)的返回地址!事實上用戶不必太在意這點,因為任務(wù)并不希望返回到其它函數(shù)中。
這時,用戶需要將寄存器保存到堆棧中,當(dāng)處理器發(fā)現(xiàn)并開始執(zhí)行中斷的時候,它會自動地完成該過程的。一些處理器會將所有的寄存器存入堆棧,而其它一些處理器只將部分寄存器存入堆棧。一般而言,處理器至少得將程序計數(shù)器的值(中斷返回地址)和處理器的狀態(tài)字存入堆棧[F8.3(2)]。很明顯,處理器是按一定的順序?qū)⒓拇嫫鞔嫒攵褩5模脩粼趯⒓拇嫫鞔嫒攵褩5臅r候也就必須依照這一順序。
接著,用戶需要將剩下的處理器寄存器保存到堆棧中[F8.3(3)]。保存的命令依賴于用戶的處理器是否允許用戶保存它們。 有些處理器用一個或多個指令就可以馬上將許多寄存器都保存起來。用戶必須用特定的指令來完成這一過程。例如,Intel80x86使用PUSHA指令將8個寄存器保存到堆棧中。對Motorola68HC11處理器而言,在中斷響應(yīng)期間,所有的寄存器都會按一定順序自動的保存到堆棧中,所以在用戶將寄存器存入堆棧的時候,也必須依照這一順序。
現(xiàn)在是時候討論這個問題了: 如果用戶的C編譯器將pdata參數(shù)傳遞到寄存器中而不是堆棧中該作些什么?用戶需要從編譯器的文檔中找到pdata儲存在哪個寄存器中。pdata的內(nèi)容就會隨著這個寄存器的儲存被放置在堆棧中。
圖 8.4 堆棧初始化(pdata通過寄存器傳遞)
一旦用戶初始化了堆棧,OSTaskStkInit()就需要返回堆棧指針?biāo)傅牡刂穂F8.3(4)]。
OSTaskCreate()和OSTaskCreateExt()會獲得該地址并將它保存到任務(wù)控制塊(OS_TCB)中。
處理器文檔會告訴用戶堆棧指針會指向下一個堆??臻e位置, 還是會指向最后存入數(shù)據(jù)的堆棧單元位置。例如,對Intel80x86處理器而言,堆棧指針會指向最后存入數(shù)據(jù)的堆棧單元位置,而對Motorola68HC11處理器而言,堆棧指針會指向下一個空閑的位置。
8.05.02OSTaskCreateHook()
當(dāng)用OSTaskCreate()或OSTaskCreateExt()建立任務(wù)的時候就會調(diào)用OSTaskCreateHook()。該函數(shù)允許用戶或使用用戶的移植實例的用戶擴展μC/OS-Ⅱ的功能。
當(dāng)μC/OS-Ⅱ設(shè)置完了自己的內(nèi)部結(jié)構(gòu)后,會在調(diào)用任務(wù)調(diào)度程序之前調(diào)用OSTaskCreateHook()。該函數(shù)被調(diào)用的時候中斷是禁止的。因此用戶應(yīng)盡量減少該函數(shù)中的代碼以縮短中斷的響應(yīng)時間。
當(dāng)OSTaskCreateHook()被調(diào)用的時候,它會收到指向已建立任務(wù)的OS_TCB的指針,這樣它就可以訪問所有的結(jié)構(gòu)成員了。當(dāng)使用OSTaskCreate()建立任務(wù)時,OSTaskCreateHook()的功能是有限的。但當(dāng)用戶使用OSTaskCreateExt()建立任務(wù)時,用戶會得到OS_TCB中的擴展指針OSTCBExtPtr),該指針可用來訪問任務(wù)的附加數(shù)據(jù),如浮點寄存器,MMU寄存器,任務(wù)計數(shù)器的內(nèi)容,以及調(diào)試信息。
只用當(dāng)OS_CFG.H中的OS_CPU_HOOKS_EN被置為1時才會產(chǎn)生OSTaskCreateHook()的代碼。這樣,使用用戶的移植實例的用戶可以在其它的文件中重新定義hook函數(shù)。
8.05.03OSTaskDelHook()
當(dāng)任務(wù)被刪除的時候就會調(diào)用OSTaskDelHook()。 該函數(shù)在把任務(wù)從μC/OS-Ⅱ的內(nèi)部任務(wù)鏈表中解開之前被調(diào)用。當(dāng)OSTaskDelHook()被調(diào)用的時候,它會收到指向正被刪除任務(wù)的OS_TCB的指針, 這樣它就可以訪問所有的結(jié)構(gòu)成員了。 OSTaskDelHook()可以用來檢驗TCB擴展是否被建立了(一個非空指針)并進(jìn)行一些清除操作。OSTaskDelHook()不返回任何值。
只用當(dāng)OS_CFG.H中的OS_CPU_HOOKS_EN被置為1時才會產(chǎn)生OSTaskDelHook()的代碼。
8.05.04OSTaskSwHook()
當(dāng)發(fā)生任務(wù)切換的時候調(diào)用OSTaskSwHook()。不管任務(wù)切換是通過OSCtxSw()還是OSIntCtxSw()來執(zhí)行的都會調(diào)用該函數(shù)。OSTaskSwHook()可以直接訪問OSTCBCur和OSTCBHighRdy,因為它們是全局變量。OSTCBCur指向被切換出去的任務(wù)的OS_TCB,而OSTCBHighRdy指向新任務(wù)的OS_TCB。 注意在調(diào)用OSTaskSwHook()期間中斷一直是被禁止的。
因為代碼的多少會影響到中斷的響應(yīng)時間,所以用戶應(yīng)盡量使代碼簡化。OSTaskSwHook()沒有任何參數(shù),也不返回任何值。
評論