taskYIELD()任务切换
#define taskYIELD() portYIELD()
#define portYIELD() \
{ \
/* 向中断控制寄存器bit28位写入1,将PendSV中断设置为挂起状态,
等到优先级高于PendSV的中断执行完成后,PendSV中断服务函数将被执行,进行任务切换工作 */ \
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \
__dsb( portSY_FULL_READ_WRITE ); \
__isb( portSY_FULL_READ_WRITE ); \
}
xPortPendSVHandler( )中断服务函数
__asm void xPortPendSVHandler( void )
{
extern uxCriticalNesting; /*临界段嵌套计数器,初始化为0xaaaaaaaa,调度器启动后初始化为0,每进入一次临界段+1*/
extern pxCurrentTCB; /* 指向当前激活的任务 */
extern vTaskSwitchContext;
PRESERVE8
mrs r0, psp /* PSP内容存入R0 */
isb /* 指令同步隔离,清流水线 */
ldr r3, =pxCurrentTCB /* 将当前指向的TCB的地址赋给R3 */
ldr r2, [r3] /* 将TCB赋给R2 */
stmdb r0!, {r4-r11} /* 以r0为基址,指针先递减再操作,保存r4-r11寄存器入栈,并更新r0的值,r0最终指向r4*/
str r0, [r2] /* 将栈顶指针赋给r0 */
stmdb sp!, {r3, r14} /* 将R3和R14临时压入堆栈,因为即将调用函数vTaskSwitchContext,调用函数时,返回地址自动保存到R14中,所以一旦调用发生,
R14的值会被覆盖,因此需要入栈保护; R3保存的当前激活的任务TCB指针(pxCurrentTCB)地址,函数调用后会用到,因此也要入栈保护*/
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
msr basepri, r0 /* 屏蔽中断,进入临界区 */
dsb /* 数据和指令同步隔离 */
isb
bl vTaskSwitchContext /* 调用函数,寻找新的任务运行,通过使变量pxCurrentTCB指向新的任务来实现任务切换 */
mov r0, #0 /* 开中断,退出临界区*/
msr basepri, r0
ldmia sp!, {r3, r14} /* 恢复R3和R14*/
ldr r1, [r3] /* 将TCB赋给r1*/
ldr r0, [r1] /* 将当前激活的任务TCB的栈顶指针存入R0*/
ldmia r0!, {r4-r11} /* r4-r11出栈*/
msr psp, r0 /*更新psp指针的值,将最新的栈顶指针赋给psp*/
isb
bx r14 /* 异常发生时,R14中保存异常返回标志,包括返回后进入线程模式还是处理器模式、使用PSP堆栈指针还是MSP堆栈指针,
当调用 bx r14指令后,硬件会知道要从异常返回,然后出栈,这个时候堆栈指针PSP已经指向了新任务堆栈的正确位置,
当新任务的运行地址被出栈到PC寄存器后,新的任务也会被执行。*/
nop
}
taskSELECT_HIGHEST_PRIORITY_TASK()寻找最高优先级任务
vTaskSwitchContext()函数实际上是调用
taskSELECT_HIGHEST_PRIORITY_TASK()这个函数
#define taskSELECT_HIGHEST_PRIORITY_TASK() \
{ \
UBaseType_t uxTopPriority; \
\
/* 寻找最高优先级并加入就绪列表中 */ \
portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \
/* 就绪列表中找到最高优先级任务的TCB,然后更新到 pxCurrentTCB */
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \
}