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