uCOS-II的移植步驟
(二)函數(shù) void OSStartHighRdy(void)
OSStartHighRdy()由OSStart()調(diào)用,用來(lái)啟動(dòng)最高優(yōu)先級(jí)任務(wù),當(dāng)然任務(wù)必須在OSStart()前已被創(chuàng)建
- PendSV中斷的優(yōu)先級(jí)應(yīng)該為最低優(yōu)先級(jí),原因在<>的7.6節(jié)已有說(shuō)明
- .PSP設(shè)置為0,是告訴具體的任務(wù)切換程序(OS_CPU_PendSVHandler()),這是第一次任務(wù)切換。做過(guò)切換后PSP就不會(huì)為0了,后面會(huì)看到。
- 往中斷控制及狀態(tài)寄存器ICSR(0xE000ED04)第28位寫(xiě)1即可產(chǎn)生PendSV中斷。這個(gè)<>8.4.5 其它異常的配置寄存器有說(shuō)明
OSStartHighRdy ;設(shè)置PendSV中斷的優(yōu)先級(jí)
LDR R0, =NVIC_SYSPRI14 ;Set the PendSV exception priority
LDR R1, =NVIC_PENDSV_PRI
STRB R1, [R0] ;*(uint8_t *)NVIC_SYSPRI14 = NVIC_PENDSV_PRI
MOVS R0, #0 ;初始化進(jìn)程堆棧指針 Set the PSP to 0 for initial context switch call
MSR PSP, R0 ;初始化PSP為0 初始化上下文切換調(diào)用
LDR R0, =OSRunning ; OSRunning = TRUE
MOVS R1, #1 ;設(shè)置OSRunning = TRUE
STRB R1, [R0]
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET ;觸發(fā)PendSV中斷
STR R1, [R0] ;*(uint32_t *)NVIC_INT_CTRL = NVIC_PENDSVSET
CPSIE I ; Enable interrupts at processor level開(kāi)啟中斷
OSStartHang
B OSStartHang ; Should never get here 死循環(huán) which(1);
(三)用戶(hù)任務(wù)切換函數(shù)和中斷任務(wù)的切換函數(shù)。
官方的移植代碼里面,這兩個(gè)函數(shù)的處理函數(shù)是一樣的,而關(guān)于這個(gè)問(wèn)題的討論,網(wǎng)上也有很多,我查了下官方關(guān)于在Cortex-M3各個(gè)廠家處理器上的移植,都是一樣的,就連現(xiàn)在最新推出的M4系列(飛思卡爾 K60),他們也是這么做,所以我決定也這么干吧。
這兩個(gè)函數(shù)就做了一件事,就是觸發(fā)PendSV中斷。如果沒(méi)有比PendSV優(yōu)先級(jí)高的中斷觸發(fā),那么用戶(hù)的切換任務(wù)和中斷的切換任務(wù)就會(huì)得到及時(shí)的執(zhí)行。
OSIntCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
OSCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0] ;*(uint32_t *)NVIC_INT_CTRL = NVIC_PENDSVSET
BX LR
(四)這個(gè)函數(shù)是真正實(shí)現(xiàn)任務(wù)切換的函數(shù),理解這個(gè)函數(shù)很重要。
首先我們要明白一點(diǎn),那就是這個(gè)函數(shù)說(shuō)白了就是一個(gè)中斷函數(shù),而它不同于一般的中斷函數(shù),在KEIL里面我們寫(xiě)好C的程序之后,已經(jīng)將中斷的入棧和出棧的工作做好了,在這里我們要自己寫(xiě)入棧和出棧的匯編指令,而且這里的關(guān)鍵就在,中斷函數(shù)出棧的時(shí)候恢復(fù)的堆棧指針是指向別的任務(wù)的,理解了這個(gè),下面的函數(shù)就很好的理解了。
附上一幅任務(wù)出棧和入棧的圖
// | .... |
;// |-----------------|
;// | .... |
;// |-----------------|
;// | .... |
;// |-----------------| |---- 任務(wù)切換時(shí)PSP
;// Low Memory | .... | |
;// |-----------------| | |---------------| |----------------|
;// ^ | R4 | <----|----|--OSTCBStkPtr |<-----| (OS_TCB *) |
;// ^ |-----------------| |---------------| |----------------|
;// ^ | R5 | | | OSTCBHighRdy
;// | |-----------------| |---------------|
;// | | R6 | | |
;// | |-----------------| |---------------|
;// | | R7 | | |
;// | |-----------------| |---------------|
;// | | R8 | Tasks
;// | |-----------------| OS_TCB
;// | | R9 |
;// | |-----------------|
;// | | R10 |
;// Stack |-----------------|
;// Growth | R11 |
;// = 1 |-----------------|
;// | | R0 = p_arg | <-------- 異常時(shí)的PSP (向上生長(zhǎng)的滿棧)
;// | |-----------------|
;// | | R1 |
;// | |-----------------|
;// | | R2 |
;// | |-----------------|
;// | | R3 |
;// | |-----------------|
;// | | R12 |
;// | |-----------------|
;// | | LR |
;// | |-----------------|
;// | | PC = task |
;// | |-----------------|
;// | | xPSR |
;// High Memory |-----------------|
這個(gè)里面有個(gè)PSP進(jìn)程堆棧指針,關(guān)于這個(gè)指針和主堆棧指針的區(qū)別需要好好的看看權(quán)威指南
OS_CPU_PendSVHandler ;xPSR, PC, LR, R12, R0-R3已自動(dòng)保存
CPSID I ;任務(wù)切換期間需要關(guān)中斷 Prevent interruption during context switch
MRS R0, PSP ;R0 = PSP PSP is process stack pointer 線程堆棧指針
CBZ R0, OS_CPU_PendSVHandler_nosave ;如果PSP==0跳轉(zhuǎn)到OS_CPU_PendSVHandler_nosave去執(zhí)行 在多任務(wù)的初始化時(shí)PSP被初始化為0 Skip register save the first time
;若果PSP如果是0,標(biāo)示任務(wù)沒(méi)有運(yùn)行過(guò),那么不需要壓棧
SUBS R0, R0, #0x20 ;R0 -= 0x20 保存R4-R11到任務(wù)堆棧 共32個(gè)字節(jié)
STM R0, {R4-R11} ;壓棧R4-R11, 其他8個(gè)寄存器是在異常時(shí)自動(dòng)壓棧的
LDR R1, =OSTCBCur ;獲取OSTCBCur->OSTCBStkPtr
LDR R1, [R1] ;R1 = *R1 (R1 = OSTCBCur)
STR R0, [R1] ;*R1 = R0 (*OSTCBCur = SP) R0 is SP of process being switched out
;將當(dāng)前任務(wù)的堆棧保存到自己的任務(wù)控制塊
;OSTCBCur->OSTCBStkPtr = PSP
;程序運(yùn)行此位置,已經(jīng)保存了當(dāng)前任務(wù)的context了
; At this point, entire context of process has been saved
OS_CPU_PendSVHandler_nosave
PUSH {R14} ; Save LR exc_return value 保存R14,因?yàn)楹竺嬉{(diào)用函數(shù)
LDR R0, =OSTaskSwHook ; OSTaskSwHook(); R0 = &OSTaskSwHook
BLX R0 ; 調(diào)用OSTaskSwHook()
POP {R14} ; 恢復(fù)R14
LDR R0, =OSPrioCur ; OSPrioCur = OSPrioHighRdy; R0 = &OSPrioCur
LDR R1, =OSPrioHighRdy ; R1 = &OSPrioHighRdy
LDRB R2, [R1] ; R2 = *R1 (R2 = OSPrioHighRdy)
STRB R2, [R0] ; *R0 = R2 (OSPrioCur = OSPrioHighRdy)
LDR R0, =OSTCBCur ; OSTCBCur = OSTCBHighRdy;;R0 = &OSTCBCur
LDR R1, =OSTCBHighRdy ; R1 = &OSTCBHighRdy
LDR R2, [R1] ; R2 = *R1 (R2 = OSTCBHighRdy)
STR R2, [R0] ; *R0 = R2 (OSTCBCur = OSTCBHighRdy) 此時(shí) [R2] = 新任務(wù)的PSP
LDR R0, [R2] ;R0 = *R2 (R0 = OSTCBHighRdy), 此時(shí)R0是新任務(wù)的SP R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
LDM R0, {R4-R11} ; 從任務(wù)堆棧SP恢復(fù)R4-R11 Restore r4-11 from new process stack
ADDS R0, R0, #0x20 ; 調(diào)整PSP R0 += 0x20
MSR PSP, R0 ; Load PSP with new process SP PSP = R0, 用新任務(wù)的SP加載PSP
ORR LR, LR, #0x04 ; Ensure exception return uses process stack 確保LR位2為1,返回后使用進(jìn)程堆棧PSP
CPSIE I ;開(kāi)中斷
BX LR ; Exception return will restore remaining context 中斷返回
END
由于是第一次的學(xué)習(xí)關(guān)于操作系統(tǒng)的知識(shí),里面的理解估計(jì)有很多不對(duì)的地方。隨著自己的理解就會(huì)有所修正。
評(píng)論