先了解下如何使用PendSV異常。(為何要使用PendSV而不是其他的異常,請參考《cortex-M3權威指南》)
PendSV異常
PendSV,即可懸起的系統調用,OS可以利用它緩期執行一個異常,直到其它重要的任務完成后才執行操作。觸發PendSV只需往NVIC的PendSV懸起寄存器的第28為置1即可;其典型用於上下文切換。
1,如何設定PendSV優先級?
NVIC_PENDSV_PRI EQU 0xFF
LDR R0, =NVIC_SYSPRI14 LDR R1, =NVIC_PENDSV_PRI
STRB R1, [R0]
2,如何觸發PendSV異常?
往ICSR第28位寫1,即可將PendSV異常掛起。若是當前沒有高優先級中斷產生,那么程序將會進入PendSV handler
NVIC_INT_CTRL EQU 0xE000ED04
NVIC_PENDSVSET EQU 0x10000000
LDR R0, =NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
觸發PendSV中斷異常。
#defineNVIC_INT_CTRL 0xE000Ed04 //PendSV中斷控制器地址 #defineNVIC_PENDSV_SET 0x10000000 //PendSV觸發的值 #defineNVIC_SYSPRI2 0xE000Ed22 //PendSV優先級控制地址 #defineNVIC_PENDSV_PRI 0x000000ff //PendSV設置為最低優先值 #defineMEM32(addr) *(volatile unsigned long *)(addr) #defineMEM8(addr) *(volatile unsigned char *)(addr) // 觸發PendSV中斷異常 void trigger_PendSV(void) { MEM32(NVIC_INT_CTRL) = NVIC_PENDSV_SET;//觸發PendSV MEM8(NVIC_SYSPRI2) =NVIC_PENDSV_PRI; //設置PendSV優先級 }
CPU何時響應PendSV異常
我們都知道,「高優先級的中斷會打斷低優先級的中斷」,這也是系統實時性的一個重要保障,所以就引入了一個問題:
相比起GPIO中斷、定時器中斷、串口中斷這些外部中斷,PendSV異常的優先級更高呢?還是更低呢?
想象這樣一種情況:
① CPU正在開心的運行着任務1……
② 此時你按下了按鍵,產生了一個GPIO中斷,CPU收到后馬上跑去執行中斷處理函數……
③ 處理過程中,此時系統產生了一個PendSV異常,CPU收到后,嘲諷了一句:“我就是從普通任務跑來處理中斷的,還沒處理完,現在又讓我執行下一個普通任務,腦子抽風了?”,說完繼續處理中斷……
所以說,無論任務的優先級有多高,它都沒有中斷高,「系統的PendSV異常優先級必須設為最低的」,以避免在外部中斷服務函數中產生任務切換。
設置PendSV異常優先級的寄存器如下,值可以為0-255:

tos中在啟動調度時設定pendsv異常的優先級,源碼如下:
NVIC_SYSPRI14 EQU 0xE000ED22 NVIC_PENDSV_PRI EQU 0xFF
同樣,設置pendSV異常優先級為最低的匯編代碼如下:
; set pendsv priority lowest ; otherwise trigger pendsv in port_irq_context_switch will cause a context switch in irq ; that would be a disaster MOV32 R0, NVIC_SYSPRI14 MOV32 R1, NVIC_PENDSV_PRI STRB R1, [R0]
進入PendSV ISR時,cortex-M3做了什么?
1,入棧。會有8個寄存器自動入棧。入棧內容及順序如下:
在步驟一中,我們已經設置了PSP,那這8個寄存器就會自動入棧到PSP所指地址處。
2,取向量。找到PendSV ISR的入口地址,這樣就能跳到ISR了。,
3,更新寄存器內容。
做完這三步后,程序就進入ISR了。
進入ISR前,我們已經完成了步驟一,cortex-M3已經幫我們完成了步驟二的一部分,剩下的需要我們手動完成。
進入OS前的兩步之PendSV(任務切換)
RTOS內功修煉記(二)—— 優先級搶占式調度到底是怎么回事?