【專題4:從0到1寫嵌入式操作系統】 之 【3.PendSV異常】

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寄存器保存到上一個任務的棧中,然後恢復的是下一個任務的棧,此時就實現了任務切換。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章