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