2.PendSV的觸發

        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的值由內核自動恢復。

發佈了43 篇原創文章 · 獲贊 28 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章