1.PendSV異常
PendSV是系統異常,具有可編程的優先級,主要用在OS的任務切換中。可以將它配置爲最低優先級(比普通任務的優先級要高,在運行任務期間發生了PendSV異常,CPU可以正常響應該異常)。
2.實現案例
系統啓動之後手動觸發一個PendSV異常,然後在PendSV異常處理函數中保存R4 - R11的值到指定的棧中。
main.c
#define NVIC_INT_CTRL 0xE000ED04 // 中斷控制及狀態寄存器
#define NVIC_PENDSVSET 0x10000000 // 觸發軟件中斷的值
#define NVIC_SYSPRI2 0xE000ED22 // 系統優先級寄存器
#define NVIC_PENDSV_PRI 0x000000FF // 配置優先級
#define MEM32(addr) *(volatile unsigned long *)(addr)
#define MEM8(addr) *(volatile unsigned char *)(addr)
void triggerPendSVC (void)
{
MEM8(NVIC_SYSPRI2) = NVIC_PENDSV_PRI; // 向NVIC_SYSPRI2寫NVIC_PENDSV_PRI,設置其爲最低優先級
MEM32(NVIC_INT_CTRL) = NVIC_PENDSVSET; // 向NVIC_INT_CTRL寫NVIC_PENDSVSET,用於PendSV
}
typedef struct _BlockType_t
{
unsigned long * stackPtr;
}BlockType_t;
BlockType_t * blockPtr;
void delay (int count)
{
while (--count > 0);
}
int flag;
unsigned long stackBuffer[1024];
BlockType_t block;
int main ()
{
block.stackPtr = &stackBuffer[1024];//從這裏可以體會到ARM使用的是滿減棧。
blockPtr = █
for (;;) {
flag = 0;
delay(100);
flag = 1;
delay(100);
triggerPendSVC();
}
}
switch.c
__asm void PendSV_Handler ()
{
//導入變量
IMPORT blockPtr
// 加載寄存器存儲地址
LDR R0, =blockPtr
LDR R0, [R0]
LDR R0, [R0]
// 保存寄存器
//爲什麼只保存R4-R11,因爲其他的寄存器的值硬件會自動保存到棧中(這裏的棧指MSP)
//STM的作用:批量將寄存器的數據寫到R0地址處的空間中,
//D表示地址遞減,也就是一開始R0的值是棧頂,棧頂是高地址
//B表示在加載數據之前,先將R0減4
//RO!中的!表示當所有寄存器都加載完畢之後,將此時R0的值/0x20000FF0(此時棧的地址)寫回R0
STMDB R0!, {R4-R11}
// 將最後的地址寫入到blockPtr中,一開始棧指針變量的值是數組最後一個元素的地址,
//將寄存器中的數據寫到了棧裏面之後,此時棧肯定是在向下移動的,STMDB能自動移動棧指針(R0/block.stackPtr),
//當所有寄存器的數據都寫到了棧中,此時的棧地址必須更新到棧指針變量中。
// 即,將此時的棧地址寫到block.stackPtr中,該變量的值爲0x20000FF0
LDR R1, =blockPtr
LDR R1, [R1]
STR R0, [R1]
// 修改部分寄存器,用於測試
ADD R4, R4, #1
ADD R5, R5, #1
// 恢復寄存器,這裏的R0的值等於0x20000FF0,壓棧和彈棧的順序相反
LDMIA R0!, {R4-R11}
// 異常返回
BX LR
NOP
}
總結:
當發生任務切換/PendSV異常時,如果把保存R4-R11寄存器保存到上一個任務的棧中,然後恢復的是下一個任務的棧,此時就實現了任務切換。