第一:void vPortSVCHandler( void );
__asm void prvStartFirstTask( void )
{
/* *INDENT-OFF* */
PRESERVE8/* Use the NVIC offset register to locate the stack. */
ldr r0, =0xE000ED08
ldr r0, [ r0 ]
ldr r0, [ r0 ]/* Set the msp back to the start of the stack. */
msr msp, r0
/* Globally enable interrupts. */
cpsie i
cpsie f
dsb
isb
/* Call SVC to start the first task. */
svc 0
nop
nop
/* *INDENT-ON* */
__asm void vPortSVCHandler( void )
{
/* *INDENT-OFF* */
PRESERVE8ldr r3, = pxCurrentTCB /* Restore the context. */
ldr r1, [ r3 ] /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */
ldr r0, [ r1 ] /* The first item in pxCurrentTCB is the task top of stack. */
ldmia r0 !, { r4 - r11 } /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */
msr psp, r0 /* Restore the task stack pointer. */
isb
mov r0, # 0
msr basepri, r0
orr r14, # 0xd
bx r14
/* *INDENT-ON* */
}
主要是用来启动第一个任务的的,启动的代码是 SVC 0,进入系统调用中断,之后这个中断函数就不再被调用了,函数体主要实现了第一个任务的现场恢复,核心代码就是最后两句,切换到线程模式;
/* Make PendSV and SysTick the lowest priority interrupts. */
portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI;portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI;
/* Start the timer that generates the tick ISR. Interrupts are disabled
* here already. */
vPortSetupTimerInterrupt();/* Initialise the critical nesting count ready for the first task. */
uxCriticalNesting = 0;
启动第一个任务之前,设置了定时器中断,所以接下来的运行就交给了systick了
第二:void xPortSysTickHandler( void );
void xPortSysTickHandler( void )
{
/* The SysTick runs at the lowest interrupt priority, so when this interrupt
* executes all interrupts must be unmasked. There is therefore no need to
* save and then restore the interrupt mask value as its value is already
* known - therefore the slightly faster vPortRaiseBASEPRI() function is used
* in place of portSET_INTERRUPT_MASK_FROM_ISR(). */
vPortRaiseBASEPRI();
{
/* Increment the RTOS tick. */
if( xTaskIncrementTick() != pdFALSE )
{
/* A context switch is required. Context switching is performed in
* the PendSV interrupt. Pend the PendSV interrupt. */
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}
}vPortClearBASEPRIFromISR();
}
这个函数的核心是 xTaskIncrementTick(),如果返回值是pdture,代表需要切换任务,这设置portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; 打开pendsv中断,这样就是在pendsv中实现任务的切换了;
所以这个中断服务函数的任务就是判断是否需要切换任务,如果不需要,就继续执行当前任务,如果需要,就到pendsv中去切换;
第三:void xPortPendSVHandler( void );
__asm void xPortPendSVHandler( void )
{
extern uxCriticalNesting;
extern pxCurrentTCB;
extern vTaskSwitchContext;/* *INDENT-OFF* */
PRESERVE8mrs r0, psp
isbldr r3, =pxCurrentTCB /* Get the location of the current TCB. */
ldr r2, [ r3 ]stmdb r0 !, { r4 - r11 } /* Save the remaining registers. */
str r0, [ r2 ] /* Save the new top of stack into the first member of the TCB. */stmdb sp !, { r3, r14 }
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
msr basepri, r0
dsb
isb
bl vTaskSwitchContext
mov r0, #0
msr basepri, r0
ldmia sp !, { r3, r14 }ldr r1, [ r3 ]
ldr r0, [ r1 ] /* The first item in pxCurrentTCB is the task top of stack. */
ldmia r0 !, { r4 - r11 } /* Pop the registers and the critical nesting count. */
msr psp, r0
isb
bx r14
nop
/* *INDENT-ON* */
}
这个中断服务函数,是任务切换的核心,里面包含了三部分:
1.保存旧任务的现场
2.修改xpcurrentTCB实现上下文的切换
3.恢复新任务的现场
具体的在上一篇博文中已经讲过了