之前在系统滴答定时器中断服务函数中调用API函数xPortSysTickHandler(),xPortSysTickHandler()函数中通过向中断和状态寄存器的bit28写入1来启动PendSV中断,具体PendSV中断服务函数是 PendSV_Handler,并且任务切换的具体任务是在PendSV中断服务函数中完成的,都是拿汇编写的,简单看一下。
PendSV中断服务函数
#define xPortPendSVHandler PendSV_Handler
_asm void xPortPendSVHandler( void )
{
extern uxCriticalNesting;
extern pxCurrentTCB;
extern vTaskSwitchContext;
PRESERVE8
mrs r0, psp
isb
/* Get the location of the current TCB. */
ldr r3, =pxCurrentTCB
ldr r2, [r3]
/* Is the task using the FPU context? If so, push high vfp registers. */
tst r14, #0x10
it eq
vstmdbeq r0!, {s16-s31}
/* Save the core registers. */
stmdb r0!, {r4-r11, r14}
/* Save the new top of stack into the first member of the TCB. */
str r0, [r2]
stmdb sp!, {r3}
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
msr basepri, r0
dsb
isb
bl vTaskSwitchContext //在此处 调用函数vTaskSwitchContext() ,该函数用来获取下一个要运行的任务,并将 pxCurrentTCB更新为这个要运行的任务。
mov r0, #0
msr basepri, r0
ldmia sp!, {r3}
/* The first item in pxCurrentTCB is the task top of stack. */
ldr r1, [r3]
ldr r0, [r1]
/* Pop the core registers. */
ldmia r0!, {r4-r11, r14}
/* Is the task using the FPU context? If so, pop the high vfp registers
too. */
tst r14, #0x10
it eq
vldmiaeq r0!, {s16-s31}
msr psp, r0
isb
#ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata */
#if WORKAROUND_PMU_CM001 == 1
push { r14 }
pop { pc }
nop
#endif
#endif
bx r14 //至此,任务切换成功。
}
查找下一个要运行的任务
在PendSV中断服务函数中调用了函数vTaskSwitchContext()来获取下一个要运行的任务,也就是查找已经就绪了的优先级最高的任务,缩减后(去掉条件编译)后的函数源码:
void vTaskSwitchContext( void )
{
if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE ) //如果调度器被挂起
{
/* The scheduler is currently suspended - do not allow a context
switch. */
//调度器被挂起就不能进行任务切换
xYieldPending = pdTRUE; //当xYieldPending为pdTRUE时代表不进行任务切换;
}
else
{ //调度器没有被挂起,
xYieldPending = pdFALSE; //xYieldPending赋值为pdFALSE时进行任务切换;
traceTASK_SWITCHED_OUT();//在选择运行任务之前调用。pxcurrenttcb保存一个指针,指向正在关闭的任务的任务控制块;
/* Check for stack overflow, if configured. */
taskCHECK_FOR_STACK_OVERFLOW(); //检查堆栈溢出(如果已配置)
/* Select a new task to run using either the generic C or port
optimised asm code. */
taskSELECT_HIGHEST_PRIORITY_TASK();//调用该宏来获取下一个要运行的任务,即获取就绪列表的最高优先级任务列表中的下一个列表项,将列表项所对应的任务控制块赋值给pxcurrenttcb,就确定了下一个运行的任务;
traceTASK_SWITCHED_IN();//在选择要运行的任务后调用。pxcurrenttcb保存指针到选定任务的任务控制块;
}
}