2.PendSV的触发

        PendSV典型使用场合是在上下文切换时(在不同任务之间切换)。 我们先简单的写几段代码实现PendSV的中断触发,当然也会涉及到CM3内核汇编指令,自从开始挑战的那天起,你不如地狱谁入地狱!

      如何触发PendSV中断呢?从Cortex-M3权威指南手册上可以看到如下图所示,控制ICSR的28位置1便可以悬起PendSV 触发PendSV中断。

2.PendSV的触发_第1张图片

        如何设置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缓冲区(内存)。

2.PendSV的触发_第2张图片2.PendSV的触发_第3张图片

 通过LDMIA R0!,{R4-R11} 来从Stack_buffer缓冲区恢复R4~R11的值。

注意:R0-R3的值由内核自动恢复。

你可能感兴趣的:(嵌入式操作系统)