我们看一下SVC中断服务函数:
__asm void vPortSVCHandler( void )
{
PRESERVE8
ldr r3, =pxCurrentTCB /* pxCurrentTCB指向处于最高优先级的就绪任务TCB */
ldr r1, [r3] /* 获取任务TCB地址 */
ldr r0, [r1] /* 获取任务TCB的第一个成员,即当前堆栈栈顶pxTopOfStack */
ldmia r0!, {r4-r11} /* 出栈,将寄存器r4~r11出栈 */
msr psp, r0 /* 最新的栈顶指针赋给线程堆栈指针PSP */
isb
mov r0, #0
msr basepri, r0
orrr14, #0xd /* 这里0x0d表示:返回后进入线程模式,从进程堆栈中做出栈操作,返回Thumb状态*/
bx r14
}
通过上一篇介绍任务创建的**,我们已经认识了指针pxCurrentTCB。这是定义在tasks.c中的唯一一个全局变量,指向处于最高优先级的就绪任务TCB。我们知道FreeRTOS的核心功能是确保处于最高优先级的就绪任务获得CPU权限,因此可以说这个指针指向的任务要么正在运行中,要么即将运行(调度器关闭),所以这个变量才被命名为pxCurrentTCB。 根据《FreeRTOS高级篇2---FreeRTOS任务创建分析》第三节我们可以知道,一个任务创建时,会将它的任务堆栈初始化的像是经过一次任务切换一样,如图1-1所示。对于Cortex-M3架构,需要依次入栈xPSR、PC、LR、R12、R3~R0、R11~R4,其中r11~R4需要人为入栈,其它寄存器由硬件自动入栈。寄存器PC被初始化为任务函数指针vTask_A,这样当某次任务切换后,任务A获得CPU控制权,任务函数vTask_A被出栈到PC寄存器,之后会执行任务A的代码;LR寄存器初始化为函数指针prvTaskExitError,这是由移植层提供的一个出错处理函数。 任务TCB结构体成员pxTopOfStack表示当前堆栈的栈顶,它指向最后一个入栈的项目,所以在图中它指向R4,TCB结构体另外一个成员pxStack表示堆栈的起始位置,所以在图中它指向堆栈的最开始处。
图1-1:任务创建后任务堆栈分布情况 所以,SVC中断服务函数一开始就使用全局指针pxCurrentTCB获得第一个要启动的任务TCB,从而获得任务的当前堆栈栈顶指针。先将人为入栈的寄存器R4~R11出栈,将最新的堆栈栈顶指针赋值给线程堆栈指针PSP,再取消中断掩蔽。到这里,只要发生中断,就都能够被响应了。
|