1.当uC/OS-III需要切换到另外一个任务时,它将保存当前任务的现场到当前任务的堆栈中,主要是CPU相关的寄存器,然后恢复新任务的现场并执行新任务。这个过程叫做任务切换
2.由任务调度可知,任务级调度OSSched()中调用OS_TASK_SW()实现任务的切换,而中断级调度OSIntExit()中调用OSIntCtxSw()实现任务的切换。
在os_cpu.h文件中有如下定义
// 0xE000ED04是中断控制及状态寄存器的地址(它的说明位于《Cortex-M3权威指南》P131)
#ifndef NVIC_INT_CTRL
#define NVIC_INT_CTRL *((CPU_REG32 *)0xE000ED04)
#endif
// 在中断控制及状态寄存器的第28位写1表示产生一个可悬起系统调用(PendSV)的中断
#ifndef NVIC_PENDSVSET
#define NVIC_PENDSVSET 0x10000000
#endif
// 由以下可知不管是任务级调度还是中断级调度,最后都是通过软件直接产生一个可悬起系统调用(PendSV)的中断实现的
#define OS_TASK_SW() NVIC_INT_CTRL = NVIC_PENDSVSET
#define OSIntCtxSw() NVIC_INT_CTRL = NVIC_PENDSVSET
当通过软件产生一个可悬起系统调用的中断后,就会调用中断服务程序OS_PendSV_Handler(),下面是它的源码,位于os_cpu_a.asm,134行 - 171行
OS_CPU_PendSVHandler
CPSID I // 关中断
MRS R0, PSP // R0 = PSP(进程堆栈指针)
CBZ R0, OS_CPU_PendSVHandler_nosave // R0为0,跳转到OS_CPU_PendSVHandler_nosave,作用是跳过第一次寄存器保存
SUBS R0, R0, #0x20 // R0 = R0 - 0x20
STM R0, {R4-R11} // 保存R4-R11到堆栈
LDR R1, =OSTCBCurPtr // 把栈顶指针放入当前任务控制块OS_TCB中的变量StrPtr中
LDR R1, [R1]
STR R0, [R1] // R0指向的是栈顶
// 到这里,当前任务的运行环境已经保存完成
OS_CPU_PendSVHandler_nosave
PUSH {R14} // LR(R14)寄存器入栈
LDR R0, =OSTaskSwHook // 调用函数OSTaskSwHook()
BLX R0
POP {R14} // LR(R14)寄存器出栈
LDR R0, =OSPrioCur // OSPrioCur = OSPrioHighRdy;
LDR R1, =OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
LDR R0, =OSTCBCurPtr // OSTCBCurPtr = OSTCBHighRdyPtr;
LDR R1, =OSTCBHighRdyPtr
LDR R2, [R1]
STR R2, [R0]
LDR R0, [R2] // R0 = OSTCBHighRdyPtr->StkPtr;
LDM R0, {R4-R11} // R4 - R11从新任务的栈中出栈
ADDS R0, R0, #0x20 // R0 = R0 + 0x20
MSR PSP, R0 // PSP(进程堆栈指针) = R0
ORR LR, LR, #0x04 // 保证异常能返回到任务栈
CPSIE I // 使能中断
BX LR // 返回
END
注:要想明白任务切换,需要详细了解《Cortex-M3权威指南》中有关寄存器和中断相关的知识。