最近学习白问网韦东山老师在B站开源的freeRTOS课程,网址:韦东山直播公开课:RTOS实战项目之实现多任务系统 第1节:裸机程序框架和缺陷_哔哩哔哩_bilibili和7天物联网训练营【第2期】7天物联网智能家居实战训练营
在学习过程中按照韦老师的方法分析了下freeRTOS源码,如果有不对的地方请指证。
PendSVC和上下文切换业务流程源码分析,基于cubemx生成的freeRTOS工程。
__asm void xPortPendSVHandler( void )
{
extern uxCriticalNesting;
extern pxCurrentTCB;
extern vTaskSwitchContext;
/ *八字节对齐*/
PRESERVE8
/* 当进入PendSVC Handler时,上一个任务运行的环境即:
xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0(任务的形参)
这些CPU寄存器的值会自动保存到任务的栈中,剩下的r4~r11需要手动保存 */
/*获取任务的堆栈指针*/
mrs r0, psp
isb
ldr r3, =pxCurrentTCB/*加载pxCurrentTCB的地址到r3*/
ldr r2, [r3] /*加载pxCurrentTCB到r2*/
/* 浮点数处理,如果使能浮点数,就需要入栈 */
tst r14, #0x10
it eq
vstmdbeq r0!, {s16-s31}
/* 保存内核寄存器到任务堆栈 */
stmdb r0!, {r4-r11, r14}
str r0, [r2] * 将任务栈的新的栈顶指针存储到当前任务TCB的第一个成员,即栈顶指针 */
stmdb sp!, {r3} /* 将R3临时压入堆栈,因为即将调用函数vTaskSwitchContext,
调用函数时,返回地址自动保存到R14中,所以一旦调用发生,R14的值会被覆盖,因此需要入栈保护;
R3保存的当前激活的任务TCB指针(pxCurrentTCB)地址,函数调用后会用到,因此也要入栈保护 */
/* 进入临界段 */
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
msr basepri, r0
dsb
isb
/* 调用函数vTaskSwitchContext,寻找新的任务运行,通过使变量pxCurrentTCB指向新的任务来实现任务切换 */
bl vTaskSwitchContext
/*退出临界段*/
mov r0, #0
msr basepri, r0
/*恢复r3*/
ldmia sp!, {r3}
/* 当前激活的任务TCB第一项保存了任务堆栈的栈顶,现在栈顶值存入R0*/
ldr r1, [r3]
ldr r0, [r1]
/* 出栈. */
ldmia r0!, {r4-r11, r14}
/* 浮点数寄存器出栈 */
tst r14, #0x10
it eq
vldmiaeq r0!, {s16-s31}
/* 异常发生时,R14中保存异常返回标志,包括返回后进入线程模式还是处理器模式、
使用PSP堆栈指针还是MSP堆栈指针,当调用 bx r14指令后,硬件会知道要从异常返回,
然后出栈,这个时候堆栈指针PSP已经指向了新任务堆栈的正确位置,
当新任务的运行地址被出栈到PC寄存器后,新的任务也会被执行。*/
msr psp, r0
isb
bx r14
nop
nop
}
上下文切换
void vTaskSwitchContext( void )
{
if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
{
/* 标记调度器状态*/
xYieldPending = pdTRUE;
}
else
{
/* 标记调度器状态*/
xYieldPending = pdFALSE;
/* 检查任务栈是否溢出 */
taskCHECK_FOR_STACK_OVERFLOW();
/* 选择优先级最高的任务,把当前的任务控制块进行赋值 */
taskSELECT_HIGHEST_PRIORITY_TASK();
}
}