PendSV典型使用場合是在上下文切換時(在不同任務之間切換)。 我們先簡單的寫幾段代碼實現PendSV的中斷觸發,當然也會涉及到CM3內核彙編指令,自從開始挑戰的那天起,你不如地獄誰入地獄!
如何觸發PendSV中斷呢?從Cortex-M3權威指南手冊上可以看到如下圖所示,控制ICSR的28位置1便可以懸起PendSV 觸發PendSV中斷。
如何設置PendSV優先級?同樣下表也來自Cortex-M3權威指南,設置地址0xE000ED22中的值既可以設置PendSV的優先級。暫且將PendSV的優先級設置爲最低0xff;
main.c加入如下代碼用於觸發PendSV中斷異常,主函數中調用trigger_PendSV() 便可以觸發。
#include "delay.h"
#include "sys.h"
#define NVIC_INT_CTRL 0xE000Ed04 //PendSV中斷控制器地址
#define NVIC_PENDSV_SET 0x10000000 //PendSV觸發的值
#define NVIC_SYSPRI2 0xE000Ed22 //PendSV優先級控制地址
#define NVIC_PENDSV_PRI 0x000000ff //PendSV設置爲最低優先值
#define MEM32(addr) *(volatile unsigned long *)(addr)
#define MEM8(addr) *(volatile unsigned char *)(addr)
typedef struct _BlockType_t
{
unsigned long *stackPtr;
}BlockType_t;
BlockType_t *blockPtr;
// 觸發PendSV中斷異常
void trigger_PendSV(void)
{
MEM32(NVIC_INT_CTRL) = NVIC_PENDSV_SET; //觸發PendSV
MEM8(NVIC_SYSPRI2) = NVIC_PENDSV_PRI; //設置PendSV優先級
}
u8 flag;
unsigned long stackBuffer[1024];
BlockType_t block;
int main(void)
{
delay_init(); //延時函數初始化
block.stackPtr=&stackBuffer[1024];
blockPtr = █
while(1)
{
flag = 1;
delay_ms(50);
flag = 0;
delay_ms(50);
trigger_PendSV();
}
}
真實的情況是,我們看不到任何現象,我們沒有在 PendSV_Hander()做任何處理,一般的PendSV異常處理都是使用匯編代碼,我們先嚐試使用C文件採用文件內嵌套匯編的形式來在C文件中加入彙編,添加switch.c文件,將其添加到工程中,用來存放PendSV觸發後中斷處理的函數。__asm__ 表明要在C文件中嵌套匯編,該函數主要是將R4~R11寄存器的值保存到block結構體內定義的指針所指向的數據緩衝區buffer中。人爲改變R4 R5寄存器的值,最後再從buffer中恢復原來R4~R11的值,看下是否發生改變。
__asm void PendSV_Handler(void)
{
IMPORT blockPtr //IMPORT 相當於c 語言中的 extern
LDR R0, =blockPtr //加載blockPtr變量的地址
LDR R0,[R0] //取出blockPtr變量的地址的值,也就是unsigned long *stackPtr;
LDR R0,[R0] //取出stackPtr地址對應的值
STMDB R0!,{R4-R11} //批量把寄存器R4~R11的值寫入內存中,最後一個寄存器的值寫入R0
//後一個任務開始
LDR R1,=blockPtr //加載blockPtr變量的地址
LDR R1,[R1]
STR R0,[R1] //將R0的值寫入到R1的地址中
ADD R4,R4,#1 //改變R4的值
ADD R5,R5,#1
LDMIA R0!,{R4-R11} //從內存中恢復R4-R11的值
BX LR //退出異常
}
這裏用到了STMDB和LDMIA指令,這裏先簡單介紹一下。
stmdb:db(decrease before)表示先減後存。
指令 stmdb sp!, { fp, ip, lr, pc} %% "!”表示sp等於最終被修改的sp的值。
假設 sp=4096,此條指令的執行過程如下:
1.先減:sp=sp-4=4092;
2.後存:4092-4095處存放pc的值;
3.先減:sp=sp-4=4088;
4.後存:4088-4091處存放lr寄存器的值;
以此類推,..........。
ldmia:ia(increase after)表示先讀後增。
指令ldmia sp, {fp,sp, pc}
假設 sp=4080,此條指令的執行過程如下:
1.先讀:fp位於4080-4083處存放原來保存的fp;
2.後增:sp=sp+4=4084;
3.先讀:sp位於4084-4087處存放原來保存的ip;
4.後增:sp=sp+4;
以此類推,..........。
進行調試,單步執行到STMDB R0!,{R4-R11} 將R4~R11寄存器的值寫入到Stack_buffer緩衝區(內存)。
通過LDMIA R0!,{R4-R11} 來從Stack_buffer緩衝區恢復R4~R11的值。
注意:R0-R3的值由內核自動恢復。