先了解下如何使用PendSV异常。(为何要使用PendSV而不是其他的异常,请参考《cortex-M3权威指南》)
PendSV异常
PendSV,即可悬起的系统调用,OS可以利用它缓期执行一个异常,直到其它重要的任务完成后才执行操作。触发PendSV只需往NVIC的PendSV悬起寄存器的第28为置1即可;其典型用于上下文切换。
1,如何设定PendSV优先级?
NVIC_PENDSV_PRI EQU 0xFF
LDR R0, =NVIC_SYSPRI14 LDR R1, =NVIC_PENDSV_PRI
STRB R1, [R0]
2,如何触发PendSV异常?
往ICSR第28位写1,即可将PendSV异常挂起。若是当前没有高优先级中断产生,那么程序将会进入PendSV handler
NVIC_INT_CTRL EQU 0xE000ED04
NVIC_PENDSVSET EQU 0x10000000
LDR R0, =NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
触发PendSV中断异常。
#defineNVIC_INT_CTRL 0xE000Ed04 //PendSV中断控制器地址 #defineNVIC_PENDSV_SET 0x10000000 //PendSV触发的值 #defineNVIC_SYSPRI2 0xE000Ed22 //PendSV优先级控制地址 #defineNVIC_PENDSV_PRI 0x000000ff //PendSV设置为最低优先值 #defineMEM32(addr) *(volatile unsigned long *)(addr) #defineMEM8(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优先级 }
CPU何时响应PendSV异常
我们都知道,「高优先级的中断会打断低优先级的中断」,这也是系统实时性的一个重要保障,所以就引入了一个问题:
相比起GPIO中断、定时器中断、串口中断这些外部中断,PendSV异常的优先级更高呢?还是更低呢?
想象这样一种情况:
① CPU正在开心的运行着任务1……
② 此时你按下了按键,产生了一个GPIO中断,CPU收到后马上跑去执行中断处理函数……
③ 处理过程中,此时系统产生了一个PendSV异常,CPU收到后,嘲讽了一句:“我就是从普通任务跑来处理中断的,还没处理完,现在又让我执行下一个普通任务,脑子抽风了?”,说完继续处理中断……
所以说,无论任务的优先级有多高,它都没有中断高,「系统的PendSV异常优先级必须设为最低的」,以避免在外部中断服务函数中产生任务切换。
设置PendSV异常优先级的寄存器如下,值可以为0-255:
tos中在启动调度时设定pendsv异常的优先级,源码如下:
NVIC_SYSPRI14 EQU 0xE000ED22
NVIC_PENDSV_PRI EQU 0xFF
同样,设置pendSV异常优先级为最低的汇编代码如下:
; set pendsv priority lowest
; otherwise trigger pendsv in port_irq_context_switch will cause a context switch in irq
; that would be a disaster
MOV32 R0, NVIC_SYSPRI14
MOV32 R1, NVIC_PENDSV_PRI
STRB R1, [R0]
进入PendSV ISR时,cortex-M3做了什么?
1,入栈。会有8个寄存器自动入栈。入栈内容及顺序如下:
在步骤一中,我们已经设置了PSP,那这8个寄存器就会自动入栈到PSP所指地址处。
2,取向量。找到PendSV ISR的入口地址,这样就能跳到ISR了。,
3,更新寄存器内容。
做完这三步后,程序就进入ISR了。
进入ISR前,我们已经完成了步骤一,cortex-M3已经帮我们完成了步骤二的一部分,剩下的需要我们手动完成。
进入OS前的两步之PendSV(任务切换)
RTOS内功修炼记(二)—— 优先级抢占式调度到底是怎么回事?