但是uC/OSIII在Cortex-M3平台中,任务切换函数却是使用的同一函数,确切的说是使用了同一样的一个宏定义,如下:
#define OS_TASK_SW() NVIC_INT_CTRL = NVIC_PENDSVSET
#define OSIntCtxSw() NVIC_INT_CTRL = NVIC_PENDSVSET
(这个宏的作用是调用PendSV中断。)
为什么和书上说的不一致呢?两种完全不同的场景居然能使用同一个处理过程?
机关就在调用PendSV中断这里。uC/OSIII在Cortex-M3平台下使用PendSV中断进行任务的调度切换。而Cortex-M3的中断进入前会自动保存8个寄存器 :xPSR, PC, LR, R12, R0-R3。
也就是说,无论是从任务中调用的任务切换还是从中断退出中调用的任务切换,都是调用的PendSV中断。这一点很重要,因此只要是任务切换,结果就是:在进入PendSV处理函数后,肯定有8个寄存器已经保存到原 thread的PSP中了(注意这个thread,uC/OSIII普通任务都是thread模式的,使用PSP。中断进程叫handler模式,使用MSP。这也是Cortex-M3架构决定的)。
那从任务中调用任务切换和从中断退出中调用的任务切换就没有一点区别吗?还是有一点的,从任务中调用任务切换是直接进入的PendSV中断;从中断退出中调用的任务切换是执行的咬尾中断,先退出原中断,然后进入PendSV中断,具体区别就是:从任务中进入PendSV处理函数,那8个寄存器是PendSV中断自己保存的;从中断退出后进入PendSV处理函数那8个寄存器是原中断保存的,不是PendSV保存的。但对PendSV处理函数而言,两者并无区别,反正给我保存了就行了,不管到底是我自己保存的还是你替我保存的。
所以uC/OSIII在PendSV处理函数中的过程就是:一进来就保存剩下的8个寄存器:R4-R11。当然,要保存到PSP下面,这是原任务使用的堆栈,保存后就保证了原任务自己堆栈内容的完整,注意千万别弄错了保存到了MSP下面,MSP是中断函数使用的。
然后就进行任务优先级查找,出栈全部寄存器,返回thread模式,执行新任务。
附PendSV处理函数的注释:
Note(s) : 1) PendSV is used to cause a context switch. This is a recommended method for performing context switches with Cortex-M3. This is because the Cortex-M3 auto-saves half of the processor context on any exception, and restores same on return from exception. So only saving of R4-R11 is required and fixing up the stack pointers. Using the PendSV exception this way means that context saving and restoring is identical whether it is initiated from a thread or occurs due to an interrupt or exception.