首先貼出今天要與大家分享的內容源碼(位於內核源碼的 os_cpu_a.asm 中):PendSV_Handler CPSID I MRS R0, PSP CBZ R0, PendSV_Handler_Nosave SUBS R0, R0, #0x20 STM R0, {R4-R11} LDR R1, ...
首先貼出今天要與大家分享的內容源碼(位於內核源碼的 os_cpu_a.asm 中):
PendSV_Handler
CPSID I
MRS R0, PSP
CBZ R0, PendSV_Handler_Nosave
SUBS R0, R0, #0x20
STM R0, {R4-R11}
LDR R1, =OSTCBCur
LDR R1, [R1]
STR R0, [R1]
PendSV_Handler_Nosave
PUSH {R14}
LDR R0, =OSTaskSwHook
BLX R0
POP {R14}
LDR R0, =OSPrioCur
LDR R1, =OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
LDR R0, =OSTCBCur
LDR R1, =OSTCBHighRdy
LDR R2, [R1]
STR R2, [R0]
LDR R0, [R2]
LDM R0, {R4-R11}
ADDS R0, R0, #0x20
MSR PSP, R0
ORR LR, LR, #0x04
CPSIE I
BX LR
這兩段代碼尤為重要,內核中任務的切換主要就是由它們實現的。接下來,我將逐行為大家解析其中的奧秘。
這兩段代碼是中斷服務程式(ISR),那麼由誰來觸發中斷呢?我們以OSCtxSw()這個函數為入手點。
它其實就是C程式中的OS_TASK_SW()
#define OS_TASK_SW() OSCtxSw()
開始分析OSCtxSw()
OSCtxSw
PUSH {R4, R5}
LDR R4, =NVIC_INT_CTRL
LDR R5, =NVIC_PENDSVSET
STR R5, [R4]
POP {R4, R5}
BX LR
1.首先進入函數,將R4,R5入棧,保護寄存器。
2.給R4,R5分別賦值,文件中這樣定義:
NVIC_INT_CTRL EQU 0xE000ED04
NVIC_PENDSVSET EQU 0x10000000
NVIC_INT_CTRL為中斷控制寄存器的地址,NVIC_PENDSVSET為PendSV中斷的觸發值
3.將觸發值寫入控制寄存器
4.彈出R4,R5
因為我們一般調用任務切換的時候,都是在臨界區調用(禁止中斷),所以不會產生中斷。之後調用OS_EXIT_CRITICAL()函數退出臨界區後,中斷才會發生,這樣就產生了PendSV異常。
接下來才是我們的重點,開始分析ISR!
CPSID I
#關中斷,防止切換任務期間被打擾
MRS R0, PSP
#取出PSP(程式棧指針)賦值給R0
CBZ R0, PendSV_Handler_Nosave
#若R0為0,則跳轉到PendSV_Handler_Nosave函數繼續執行,這裡我們講述的就是由延時導致的任務切換,PSP都是有值的,所以繼續執行
SUBS R0, R0, #0x20
#將R0 - 0x20 則R0與PSP之間空出8個單位(每個單位4個位元組)
STM R0, {R4-R11}
#將寄存器的R4-R11存入空出的8個單位
LDR R1, =OSTCBCur
#將當前TCB的地址賦給R1
LDR R1, [R1]
#取出地址處的數據(即OSTCBCur->SP的地址,因為OSTCB結構體的第一個數據就是SP)賦給R1
STR R0, [R1]
#將R0(棧頂數據的地址)賦值給OSTCBCur->SP,則OSTCBCur->SP與R0指向的位置相同
!!這段程式的OSTCBCur指的都是old_task的指針
這段程式的圖解:
PUSH {R14}
LDR R0, =OSTaskSwHook
BLX R0
POP {R14}
#這小段代碼就是執行OSTaskSwHook這個C的函數
LDR R0, =OSPrioCur
LDR R1, =OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
#這小段代碼就將OSPrioHighRdy賦給OSPrioCur
LDR R0, =OSTCBCur
LDR R1, =OSTCBHighRdy
LDR R2, [R1]
STR R2, [R0]
#這小段代碼就將OSTCBHighRdy->SP賦給OSPrioCur->SP
LDR R0, [R2]
#將OSTCBHighRdy->SP的值(指向新任務棧中的地址,也就是新任務的棧頂)賦值給R0
LDM R0, {R4-R11}
#以R0為基址,讀出8個單位的數據給CPU的寄存器
ADDS R0, R0, #0x20
#R0地址 + 0x20,指向幾個重要的寄存器值(當前棧頂)
MSR PSP, R0
#將當前R0的值賦值給PSP
ORR LR, LR, #0x04
#確保異常返回後,新任務使用PSP指針
CPSIE I
#開啟中斷
!!這段程式的OSTCBCur指的都是new_task的指針
這段程式的圖解:
這樣也就完成了old_task到new_task的切換! 0.0
可能我理解不周,請謹慎參考,我會後期完善,謝謝支持!