uCOS-II中的OS_CPU.h,OS_CPU_A.s,OS_CPU.c
μC/OS-Ⅱ的移植集中在OS_CPU.h,OS_CPU_A.s,OS_CPU.c這三個文件上,下面分別詳細介紹三個文件中的函數和需要修改或者編寫的代碼。
1. OS_CPU.h的移植
該文件定義了和處理器及編譯器相關的定義及一些全局函數聲明。由于ARM7 處理器字長為32位,半字長為16位,字節為8位,因此在OS_CPU.h文件修改與編譯器相關的定義如下:
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;
typedef signed char INT8S;
typedef unsigned short INT16U;
typedef signed short INT16S;
typedef unsigned long INT32U;
typedef signed long INT32S;
typedef float FP32;
typedef double FP64;
typedef unsigned long OS_STK;
?
#define OS_CRITICAL_METHOD 2
#define OS_ENTER_CRITICAL() ARMDisableInt()
#define OS_EXIT_CRITICAL() ARMEnableInt()
#define OS_STK_GROWTH 1
#define OS_TASK_SW OSCtxSw
?
extern void OSCtxSw(void);
extern void OSIntCtxSw(void);
extern void ARMDisableInt(void);
extern void ARMEnableInt(void);
extern void OSTickISR(void);
2. OS_CPU_C.C文件
移植OS_CPU_C.C文件時,需要編寫的是任務堆棧初始化函數OSTaskStkInit和時鐘節拍中斷服務鉤子函數OSTimeTickHook。
在μC/OS-II中,每一個任務都有自己的任務堆棧,當發生任務切換或者中斷時,其CPU使用權被剝脫,為了任務能被再次運行,那么這個被打斷的任務所用到的處理器的寄存器內容均應得到保存,按照ARM7 處理器的壓棧和入棧指令的特點,設計任務堆棧如下圖2:
CPSR
R0
R1
……
R12
LR(R14)
PC(R15)
圖2 任務堆棧的結構
根據任務堆棧結構示意圖,OS_STK函數編寫如下:
#define SVCMODE 0x13
OS_STK * OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt){
OS_STK *stk;
opt = opt;
stk = (OS_STK) ptos;
*--stk = (OS_STK) task;
*--stk = (OS_STK) task;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = (INT32U) pdata;
*--stk = (SVC32MODE|0x40);
return ((OS_STK *)stk);
}
說明:用戶創建任務時,OSTaskCreat()會調用OSTaskStkInit函數初始化該任務的堆棧,并把返回的堆棧指針保存到該任務的TCB結構中的最前面的參數OSTCBStkPtr中,當該任務要被恢復時,任務切換函數從其TCB塊中取得其任務堆棧指針,依次將堆棧內容彈到處理器對應的 CPSR、r0,r1,…,r12,lr,pc的寄存器中,完成現場的恢復和程序指針PC的返回。
另一個需要編寫的函數是OSTimeTickHook,該函數被時鐘節拍中斷服務函數OSTickISR中的OSTimeTick函數調用,用來清除時鐘節拍中斷發生設備的請求。本移植方案使用S3C44B0X處理器的RTC模塊的tick中斷作為時鐘節拍中斷,該函數編寫如下:
void OSTimeTickHook(void){
rI_ISPC =((INT32U)0x01) << 20;
}
注意:用戶也可不修改此函數,但是必須在OSTickISR中執行清除發生節拍中斷的設備的中斷請求標志,為便于說明,本文將利用內核提供給用戶的OSTimeTickHook函數來完成清中斷的任務。
另外幾個hook函數不必去改它們。至此,OS_CPU.C編寫完成。
3. OS_CPU_A.S文件的移植
該文件是移植過程中唯一需要用匯編語言來實現的文件,也是移植的重點和難點所在。在這個文件里,需要編寫的函數有OSStartHighRdy,OSCtxSW,OSIntCtxSW,OSTickISR,ARMDisableInt,ARMEnableInt幾個。
下面先結合us/os的任務切換的過程分析一下這幾個函數的作用。
1)OSStartHighRdy()函數
當程序執行內核的OSStart函數時,表示多任務系統開始啟動, OSStart函數將調用OSStartHighRdy函數從最高優先級任務的TCB塊中獲得該任務的堆棧指針,通過該指針,依次從該任務的任務堆棧中恢復CPU的現場。由于任務在堆棧初始化時,已經設定了彈出到程序指針寄存器PC的是該任務函數的入口地址,因此,OSStartHighRdy函數只需依次彈出任務棧內容到處理起寄存器,該任務便將得以運行。
2)OSCtxSw()函數
該函數是任務級的上下文切換函數,當任務被阻塞而主動請求CPU開始任務調度時執行,其過程是將當前任務的的CPU現場保存到該任務堆棧中去,然后從 OSTCBHighRdy中獲得更高優先級任務的堆棧指針,再從該指針指向的堆棧中恢復此任務的CPU現場,使之繼續執行,從而完成一次任務級別的切換。表2為OSCtxSw函數的偽代碼。
void OSCtxSw(void) {
保存處理器寄存器;
OSTCBCur->OSTCBStkPtr = sp;
OSTCBCur = OSTCBHighRdy;
SP = OSTCBHighRdy->OSTCBStkPtr;
恢復該任務的現場();
執行中斷返回指令;
}
表2 OSCtxSw函數的偽代碼
3) OSIntCtxSw() 函數
該函數用于中斷級的上下文切換。由于CPU響應時鐘節拍中斷后,處理器從svc進入了irq模式,并進入時鐘節拍中斷服務函數OSTickISR, OSTickISR函數發現若有高優先級任務需要運行,則系統不返回中斷前的任務,而直接調度就緒的高優先級任務使之盡快得到執行,以保證實時性能。但是由于OSTickISR函數一開始已經保存過任務中斷前的CPU現場,因此OSIntCtxSW()不需要再進行類似的操作。當OSTickISR調用 OSIntExit函數找出需要運行的更高優先級任務后,OSIntExit會將該任務的TCB指針放在OSTCBHighRdy中,然后 OSIntExit在最后調用OSIntCtxSW函數來從OSTCBHighRdy中獲取堆棧指針然后恢復該高優先級任務的現場,使得其繼續執行,并不再返回時鐘節拍中斷服務程序。顯然,OSIntCtxSW函數的過程和OSCtxSW函數的后半部分操作相同,因此,OSCtxSW可以借用 OSIntCtxSW的代碼。
4) OSTickISR()函數
在 CPU響應時鐘節拍中斷后,程序指針PC發生跳轉后進入該函數,由于OSTickISR調用OSTimeTick函數使得所有的延時節拍不為0的任務延時節拍數減1,并調用OSIntExit函數來找出就緒的高優先級任務,若需要切換,則最后由OSIntCtxSw來完成新任務的調度,否則仍然返回到被時鐘節拍中斷的任務。OSTickISR函數的偽碼和注釋見表3。
5) ARMDisableInt和ARMEnableInt函數
ARMDisableInt 是用來暫時禁止FIQ及IRQ中斷的函數,ARMEnableInt則是恢復ARMDisableInt執行前的中斷使能狀態,二者成對使用,用來保護臨界段代碼不被中斷破壞。本移植使用方式2,即在進入臨界段代碼前關中斷,完成后恢復先前的中斷使能狀態。
void OSTickISR(void) {
保存處理器寄存器;
調用OSIntEnter();
給產生中斷的設備清中斷;
調用OSTimeTick();
調用OSIntExit();
恢復處理器寄存器;
執行中斷返回指令;
}
表3 OSTickTime函數的偽碼
下面給出OS_CPU_A.S的全部內容和注釋。
; *****OS_CPU_A.S文件匯編代碼開始*****
AREA |subr|, CODE, READONLY ;聲明為代碼段
;***** OSStartHighRdy代碼開始*****
???? EXPORT OSStartHighRdy ;關鍵詞EXPORT表示聲明此函數被其他文件使用,下同
???? IMPORT OSTaskSwHook ;關鍵詞IMPORT聲明此函數/參量在其他文件中定義,下同
???? IMPORT OSTCBHighRdy
???? IMPORT OSRunning
OSStartHighRdy ; 使就緒表中任務最高的優先級的任務開始運行
???????? BL OSTaskSwHook ; 調用用戶的Hook函數,空函數
???????? LDR r4,=OSRunning ; 將OSRunning置1,聲明多任務OS開始運行
???????? MOV r5, #1
???????? STRB r5, [r4]
???????? LDR r4, =OSTCBHighRdy ; 偽指令,取得存儲OSTCBHighRdy的地址
???????? LDR r4, [r4] ; 得到最高優先級任務的任務堆棧地址
???????? LDR sp, [r4] ; 切換到新任務的堆棧
???????? LDMFD sp!, {r4} ;從新任務堆棧中讀取第一個參數(CPSR)到(r4)
???????? MSR cpsr_cxsf, r4 ;再傳給cpsr,堆棧中的CPSR彈出到CPU的cpsr寄存器
???????? LDMFD sp!, {r0-r12,lr,pc} ;依次恢復該任務r0~r12,lr,pc,切換到該任務
; *****下面開始OSCtxSw函數,完成任務級的任務切換*****
???? EXPORT OSCtxSw
???? IMPORT OSPrioCur
???? IMPORT OSPrioHighRdy
???? IMPORT OSTCBCur
???? IMPORT OSTaskSwHook
???? IMPORT OSTCBHighRdy;該變量指向任務切換后即將運行的任務的OS_TCB
OSCtxSw
???????? STMFD sp!, {lr} ; OSCtxSw是被調用的,lr的值就是調用前的PC值,入棧
???????? STMFD sp!, {r0-r12,lr} ; 將lr和其他寄存器入棧
???????? MRS r4, cpsr ;通過MRS指令將cpsr入棧
???????? STMFD sp!, {r4} ; 被掛起的當前任務的寄存器保存完畢,下面接著保存該
??????????????????????????????? ;任務的堆棧指針,以便下次恢復時,可以找到其堆棧指針,便可恢復其寄存器
???????? LDR r4, =OSTCBCur ; 得到當前TCB塊的地址,傳給r4
???????? LDR r5, [r4] ; 將OSTCBCur中的值傳給r5,注意OSTCBCur存的是指針
???????? STR sp, [r5] ; 將當前任務的sp傳到OSTCBCur存的指針中去
; *****下面OSCtxSw準備恢復優先級更高的就緒任務,這部分可共用OSIntCtxSw的代碼*****
; *****OSIntCtxSw函數開始*****
???? EXPORT OSIntCtxSw
???? IMPORT OSTaskSwHook
OSIntCtxSw ;準備任務切換
???????? BL OSTaskSwHook ;調用Hook函數,此為空函數
???????? LDR r4, =OSTCBHighRdy
???????? LDR r4, [r4] ;將高優先級的任務棧頂指針存到r4中
???????? LDR r5, =OSTCBCur
???????? STR r4, [r5] ; OSTCBCur = OSTCBHighRdy
???????? LDR r6, =OSPrioHighRdy;取出高優先級
???????? LDRB r6, [r6] ;優先級,字節傳送
???????? LDR r5, =OSPrioCur
???????? STRB r6, [r5] ; OSPrioCur = OSPrioHighRdy
???????? LDR sp, [r4] ;從r4中取得要恢復的任務的棧頂指針
???????? LDMFD sp!, {r4} ;彈出任務棧中的第一個參數,即cpsr
???????? MSR cpsr_cxsf, r4 ;首先開始恢復cpsr
???????? LDMFD sp!, {r0-r12,lr,pc} ;依次恢復r0~r12,lr,pc,任務切換
; *****OSTickISR開始*****
???? EXPORT OSTickISR
???? IMPORT OSIntEnter
???? IMPORT OSTimeTick
???? IMPORT OSIntExit
???? LINK_SAVE DCD 0 ;用來保存時鐘節拍中斷前的lr,以便計算出pc而使之入棧
???? PSR_SAVE DCD 0 ;用來保存中斷前的spsr,中斷產生時,svc模式下的cpsr存到spsr
OSTickISR ;時鐘節拍中斷服務程序入口,需要用戶在主函數中安裝
???????? STMFD sp!, {r4} ;因為r4下面要使用,故先保存r4到irq模式的堆棧中
???????? LDR r4, =LINK_SAVE ; 準備保存LR,SPSR,以便得到中斷前的pc和cpsr_svc
???????? STR lr, [r4] ; LINK_SAVE = lr_irq,此時lr=PC(中斷發生前)+4
???????? MRS lr, spsr ;lr已保存,用lr取得spsr(保存的是中斷前的cpsr)
???????? STR lr, [r4, #4] ; PSR_SAVE = spsr_irq
???????? LDMFD sp!, {r4} ;恢復r4
???????? ORR lr, lr, #0x80 ;在上下文切換前,屏蔽irq中斷。注意lr存的是中斷前的cpsr
???????? MSR cpsr_cxsf, lr ;中斷產生前是svc模式,故必須要切換到此模式下保存現場
???????? SUB sp, sp, #4 ;按任務棧結構,空一個空間預留給PC
???????? STMFD sp!, {r0-r12,lr} ; 依次保存lr、r12~r0
???????? LDR r4, =LINK_SAVE ;準備保存pc,取得存svc模式下發生中斷前lr的地址
???????? LDR lr, [r4, #0]
???????? SUB lr, lr, #4 ;中斷前的pc = LINK_SAVE - 4,此前lr為異常前pc+4的值
???????? STR lr, [sp, #(14*4)];保存pc到任務棧中預留的空間
???????? LDR r4, [r4, #4] ;開始保存cpsr,r4 = PSR_SAVE,即中斷前的cpsr_svc
???????? STMFD sp!, {r4} ;保存svc模式下任務的cpsr,寄存器保護完畢
???????? LDR r4, =OSTCBCur ;下面開始將該堆棧指針傳給OSTCBCur所指向的指針
???????? LDR r4, [r4] ;便于OSIntExit函數判斷是否當前任務優先級最高
???????? STR sp, [r4] ;在OSTCBCur->OSTCBstkptr保存被中斷的任務的棧頂指針
???????? BL OSIntEnter ;異常前的上下文保存好之后,開始準備中斷服務,將OSIntNesting++
???????? BL OSTimeTick ;將所有延時節拍不為1的任務的節拍數都減1,并清中斷標志
???????? BL OSIntExit ;將OSIntNesting--,并判斷是否有高優先級任務就緒,若有,則調
?????????????????????????? ;用OSIntCtxSw()調度該任務并不再返回; 若沒有則返回到這里
???????? LDMFD sp!, {r4} ;這里sp存的仍是調用OSIntEnter前的sp,即被中斷的任務棧頂指針
???????? MSR cpsr_cxsf, r4 ;從堆棧中恢復中斷前任務的cpsr,注意此時irq才被重新允許
???????? LDMFD sp!, {r0-r12,lr,pc} ;恢復中斷前任務的 r0-r12,lr和pc,返回被中斷的任務
;******OSTickISR函數代碼完成,下面是臨界段代碼前后開關中斷的函數******
???? EXPORT ARMDisableInt
ARMDisableInt
???????? MRS r0, cpsr ;由于任務和內核都運行在svc模式下,因此可方便地操作cpsr
???????? STMFD sp!, {r0} ; 保存當前的cpsr
???????? ORR r0, r0, #0xc0 ;屏蔽FIQ,IRQ中斷
???????? MSR cpsr_c, r0 ;回寫cpsr,只屏蔽IRQ中斷
???????? MOV pc, lr ;返回
???????? EXPORT ARMEnableInt
???????? ARMEnableInt ;必須和ARMDisableInt成對使用
???????? LDMFD sp!, {r0} ;彈出在ARMDisableInt中被保存的cpsr
???????? MSR cpsr_c, r0 ;恢復關中斷前的cpsr
???????? MOV pc, lr ;返回
???????? END ;匯編代碼結束
;*****OS_CPU_A.S文件結束******
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的uCOS-II中的OS_CPU.h,OS_CPU_A.s,OS_CPU.c的全部內容,希望文章能夠幫你解決所遇到的問題。