PendSV典型使用场合是在上下文切换时(在不同任务之间切换)。 我们先简单的写几段代码实现PendSV的中断触发,当然也会涉及到CM3内核汇编指令,自从开始挑战的那天起,你不如地狱谁入地狱!
如何触发PendSV中断呢?从Cortex-M3权威指南手册上可以看到如下图所示,控制ICSR的28位置1便可以悬起PendSV 触发PendSV中断。
如何设置PendSV优先级?同样下表也来自Cortex-M3权威指南,设置地址0xE000ED22中的值既可以设置PendSV的优先级。暂且将PendSV的优先级设置为最低0xff;
main.c加入如下代码用于触发PendSV中断异常。
#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)
// 触发PendSV中断异常
void trigger_PendSV(void)
{
MEM32(NVIC_INT_CTRL) = NVIC_PENDSV_SET; //触发PendSV
MEM8(NVIC_SYSPRI2) = NVIC_PENDSV_PRI; //设置PendSV优先级
}
主函数中调用trigger_PendSV() 便可以触发。
真实的情况是,我们看不到任何现象,我们没有在 PendSV_Hander()做任何处理,一般的PendSV异常处理都是使用汇编代码,我们先尝试使用C文件采用文件内嵌套汇编的形式来在C文件中加入汇编,添加switch.c文件,将其添加到工程中,用来存放PendSV触发后中断处理的函数。__asm__ 表明要在C文件中嵌套汇编,该函数主要是将R4~R11寄存器的值保存到block结构体内定义的指针所指向的数据缓冲区buffer中。人为改变R4 R5寄存器的值,最后再从buffer中恢复原来R4~R11的值,看下是否发生改变。
这里用到了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;
以此类推,..........。
开启debud调试功能,按F10 单步执行到trigger_PendSV函数,接着按F11进入trigger_PendSV函数内部执行。
trigger_PendSV函数执行完毕会触发PendSv事件中断,进入PendSV_Handler函数中。
按F10 单步执行到STMDB R0!,{R4-R11} 将R4~R11寄存器的值写入到Stack_buffer缓冲区(内存)。
执行改变ADD R4, R4, #10 ADD R5, R5, #10 指令来改变R4 R5寄存器的值。
通过LDMIA R0!,{R4-R11} 来从Stack_buffer缓冲区恢复R4~R11的值。