在MC68HC908GP32上移植μC/OS-II
(2)代碼臨界區(qū)
μC/OS-II在進(jìn)入系統(tǒng)臨界代碼區(qū)之前要關(guān)閉中斷,等到退出臨界區(qū)后再打開,從而保護(hù)核心數(shù)據(jù)不被多任務(wù)環(huán)境下的其他任務(wù)或中斷破壞。在GP32中,開關(guān)中斷可以通過匯編指令CLI和SEI來實(shí)現(xiàn)。所以μC/OS-II中的宏OS_ENTER_CRITICAL()定義為指令SEI,OS_EXIT_CRITICAL()定義為指令CLI。
(3)堆棧增長方向
GP32的堆棧是由高地址向低地址方向增長的,所以常量OS_STK_GPOWTH必須設(shè)置為1。
(4)OS_TASK_SW()函數(shù)的定義
在μC/OS-II中,OS_TASK_SW()用來實(shí)現(xiàn)任務(wù)切換。就緒任務(wù)的堆棧初始化應(yīng)該模擬一次中斷發(fā)生后的樣子,堆棧中應(yīng)該按進(jìn)棧次序設(shè)置好各個(gè)寄存器的內(nèi)容。OS_TASK_SW()函數(shù)模擬一次中斷過程,在中斷返回的時(shí)候進(jìn)行任務(wù)切換。GP32中可采用軟中斷指令SWI實(shí)現(xiàn)任務(wù)切換。中斷服務(wù)程序的入口點(diǎn)必須指向匯編函數(shù)OSCtxSw()。
OS_TASK_SW()的定義:
#define OS_TASK_SW() asm swi
3.OS_CPU08.ASM文件
μC/OS-II的移植需要改寫OS_CPU08.ASM中的4個(gè)函數(shù):OSStartHighRdy()、OSCtxSw()、OSIntCtxSw()和OSTickISR()。
(1)OSStartHighRdy()函數(shù)
該函數(shù)由SStart()函數(shù)調(diào)用,功能是運(yùn)行優(yōu)先級(jí)最高的就緒任務(wù)。在調(diào)用 OSStart()之前,必須先調(diào)用OSInit(),并且已經(jīng)至少創(chuàng)建了一個(gè)任務(wù)。為了啟動(dòng)任務(wù),OSStartHighRdy()首先找到當(dāng)前就緒的優(yōu)先級(jí)最高的任務(wù)(OSTCBHighRdy中保存有優(yōu)先級(jí)最高任務(wù)的任務(wù)控制塊-TCB的地址),并從任務(wù)的任務(wù)控制塊(OS_TCB)中找到指向堆棧的指針,然后從堆棧中彈出全部寄存器的內(nèi)容,運(yùn)行RTE中斷返回。由于任務(wù)創(chuàng)建時(shí)堆棧的結(jié)構(gòu)就是按中斷后的堆棧結(jié)構(gòu)初始化的,執(zhí)行RET指令后就切換到新任務(wù)(有關(guān)μC/OS-II的任務(wù)切換機(jī)制,請(qǐng)參考系列講座的第2講)。對(duì)于OSStartHighRdy的代碼,我們采用在C中嵌入?yún)R編的方法編寫。需要說明的是,由于GP32中有512字節(jié)RAM,所以地址指針必須是16位的;而GP32中累加寄存器A為8位,所以用累加器A傳遞地址必須進(jìn)行兩次讀入、輸出操作。
Void OSStartHighRdy(void)
{asm
{
jsr OSTaskSwHook //調(diào)用用戶定義接口函數(shù)
lda OSRunning //設(shè)置OSRunning變量,標(biāo)志進(jìn)入多任務(wù)模式
inca
sta OSRunning
ldx OSTCBHighRdy //取得最高優(yōu)先級(jí)就緒任務(wù)TCB地址
stx OSTCBCur //保存到OSTCBCur中
pshx
ldx OSTCBHighRdy:1//保存地址的第二個(gè)字節(jié)
stx OSTCBCur:1
pulh
lda 0,X //載放就緒任務(wù)堆棧指針
psha
ldx 1,X //載入就緒任務(wù)堆棧指針第二個(gè)字節(jié)
pulh
txs
pulh //恢復(fù)索引寄存器內(nèi)容
rti //中斷返回,運(yùn)行新任務(wù)
}}
(2)OSCtxSw()函數(shù)
OSCtxSw()是一個(gè)任務(wù)級(jí)的任務(wù)切換函數(shù)(在任務(wù)中調(diào)用,區(qū)別于在中斷程序中調(diào)用的OSIntCtxSw())。在GP32上實(shí)現(xiàn),可通過執(zhí)行一條軟中斷指令SWI來實(shí)現(xiàn)任務(wù)切換。軟中斷向量指向OSCtxSw()。在 μC/OS-II中,如果任務(wù)調(diào)用了某個(gè)函數(shù),而該函數(shù)的執(zhí)行結(jié)果可能造成系統(tǒng)任務(wù)新調(diào)度(例如試圖喚醒一個(gè)優(yōu)先級(jí)更高的任務(wù)),則在函數(shù)的末尾會(huì)調(diào)用 OSSched();如果OSSched()將查找當(dāng)前就緒的優(yōu)先級(jí)最高的任務(wù),若不是當(dāng)前任務(wù),則判斷是否需要進(jìn)行任務(wù)調(diào)度,并找到該任務(wù)控制塊 OS_TCB的地址,將該地址拷貝到變量OSTCBHighRdy中,然后通過宏OS_TASK_SW()執(zhí)行軟中斷進(jìn)行任務(wù)切換。在此過程中,變量 OSTCBCur始終包含一個(gè)指向當(dāng)前運(yùn)行任務(wù)OS_TCB的指針。OSCtxSw()的匯編代碼如下:
Void OSCtxSw(void)
{asm
{pshh //保存X寄存器
tsx
pshx
pshh
dx OSTCBCur //載入當(dāng)前任務(wù)的TCB指針
pshx
ldx OSTCBCur:1 //載入TCB的第二個(gè)字節(jié)
pulh
pula
sta 0,x //保存當(dāng)前堆棧指針
pula
sta 1,x
jsr OSTaskSwHook //調(diào)用用戶定義的接口函數(shù)
lda OSPrioHighRdy //設(shè)置OSPrioCur=OSPrioHighRdy
sta OSPrioCur
pshx
ldx OSTCBHighRdy:1
stx OSTCBCur:1
pulh
lda 0,x //載入堆棧指針
psha
ldx,1,x
pulh
txs
pulh //恢復(fù)索引寄存器內(nèi)容
rti //中斷返回,切換任務(wù)
}}
(4)OSTickISR()函數(shù)
在μC/OS-II中,當(dāng)調(diào)用OSStart()啟動(dòng)多任務(wù)環(huán)境后,時(shí)鐘中斷的使用是非常重要的。在時(shí)鐘中斷程序中負(fù)責(zé)處理所有與定時(shí)相關(guān)的工作,如任務(wù)的延時(shí)、等待操作等等。在時(shí)鐘中斷中將查詢處于等待狀態(tài)的任務(wù),判斷是否延時(shí)結(jié)束,否則將重新進(jìn)行任務(wù)調(diào)度。
為GP32編寫的函數(shù)OSTickISR()的代碼如下:
void OSTickISR()void{
asm{
pshh
LDA T1SC
BCLR 7,T1SC //允許中斷嵌套
}
OsintEnter(); /*標(biāo)志進(jìn)入中斷*/
OSTimeTick(); /*調(diào)用時(shí)鐘節(jié)拍函數(shù)*/
OSlntExit(); /*標(biāo)志退出中斷*/
Asm{
Pulh
Rti
}}
和μC/OS-II中的其他中斷服務(wù)程序一樣,OSTickISR()首先在被中斷任務(wù)堆棧中保存CPU寄存器的值,然后調(diào)用OSIntEnter()。μC/OS-II要求在中斷服務(wù)程序開頭調(diào)用OSIntEnter(),其作用是將記錄中斷嵌套層數(shù)的全局變量OSIntNesting加1。如果不調(diào)用OSIntEnter(),直接將OSIntNesting加1也是允許的。隨后,OSTickISR()調(diào)用OSTimeTick(),檢查所有處于延時(shí)等待狀態(tài)的任務(wù),判斷是否有延時(shí)結(jié)束就緒的任務(wù)。在OSTickISR()的最后調(diào)用OSIntExit(),如果在中斷中(或其他嵌套的中斷)有更高優(yōu)先級(jí)的任務(wù)就緒,并且當(dāng)前中斷為中斷嵌套的最后一層,OSIntExit() 將進(jìn)行任務(wù)調(diào)度。注意:如果進(jìn)行了任務(wù)調(diào)度,OSIntExit()將不同志返回調(diào)用者,而是用新任務(wù)的堆棧中的寄存器數(shù)值恢復(fù)CPU現(xiàn)場,然后用 IRET實(shí)現(xiàn)任務(wù)切換。如果當(dāng)有中斷不是中斷嵌套的最后一層,或中斷中沒有改變?nèi)蝿?wù)的就緒狀態(tài),OSIntExit()將返回調(diào)用者 OSTickISR(),最后OSTickISR()返回被中斷的任務(wù)。
評(píng)論