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