FreeRtos 任务切换深入分析

一、背景知识:

1、任务切换包含三个基本流程:保护现场、更新TCB、恢复现场并跳转

2、freertos的任务切换是在xPortPendSVHandler 中断函数中完成的

3、中断函数在调用之前,硬件已经保存了r0,r1,r2,r3,r12,r14(LR),r15(pc),恢复现场的时候由硬件自动恢复r0,r1,r2,r3,r12,r14(LR),r15(pc),即r0~r3在中断服务函数中是可以随便使用的,剩下的r4~r11需要在中断函数中手动保存。

4、ARM中的栈有两个,msp和psp,主程序和中断中用msp,线程中是psp

5、ARM一般是满减栈

**二、**xPortPendSVHandler源代码:

cpp 复制代码
void xPortPendSVHandler( void )
{
    /* This is a naked function. */

    __asm volatile
        (
         "	mrs r0, psp							\n"
         "	isb									\n"
         "										\n"
         "	ldr	r3, pxCurrentTCBConst			\n" /* Get the location of the current TCB. */
         "	ldr	r2, [r3]						\n"
         "										\n"
         "	tst r14, #0x10						\n" /* Is the task using the FPU context?  If so, push high vfp registers. */
         "	it eq								\n"
         "	vstmdbeq r0!, {s16-s31}				\n"
         "										\n"
         "	stmdb r0!, {r4-r11, r14}			\n" /* Save the core registers. */
         "	str r0, [r2]						\n" /* Save the new top of stack into the first member of the TCB. */
         "										\n"
         "	stmdb sp!, {r0, r3}					\n"
         "	mov r0, %0 							\n"
         "	cpsid i								\n" /* Errata workaround. */
         "	msr basepri, r0						\n"
         "	dsb									\n"
         "	isb									\n"
         "	cpsie i								\n" /* Errata workaround. */
         "	bl vTaskSwitchContext				\n"
         "	mov r0, #0							\n"
         "	msr basepri, r0						\n"
         "	ldmia sp!, {r0, r3}					\n"
         "										\n"
         "	ldr r1, [r3]						\n" /* The first item in pxCurrentTCB is the task top of stack. */
         "	ldr r0, [r1]						\n"
         "										\n"
         "	ldmia r0!, {r4-r11, r14}			\n" /* Pop the core registers. */
         "										\n"
         "	tst r14, #0x10						\n" /* Is the task using the FPU context?  If so, pop the high vfp registers too. */
         "	it eq								\n"
         "	vldmiaeq r0!, {s16-s31}				\n"
         "										\n"
         "	msr psp, r0							\n"
         "	isb									\n"
         "										\n"
#ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */
#if WORKAROUND_PMU_CM001 == 1
         "			push { r14 }				\n"
         "			pop { pc }					\n"
#endif
#endif
         "										\n"
         "	bx r14								\n"
         "										\n"
         "	.align 4							\n"
         "pxCurrentTCBConst: .word pxCurrentTCB	\n"
         ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY)
         );
}

三、xPortPendSVHandler分析:

1、先在xPortPendSVHandler中把pxCurrentTCB打印出来:

2、

将psp给r0

3、

这两行,将r3保存了pxCurrentTCB ,r2指向了上一个task的栈顶位置:pxTopOfStack

4、

这是对FPU寄存器的判断,可以先不看

5、

将r4-r11,r14保存到任务栈psp中。并将栈顶位置写入到r2中,即 pxTopOfStack这个变量

6、

此时的sp为msp,即将r3、r0保存到msp中。

至此,现场保护已经完成。

7、

跳转到vTaskSwitchContext这个C函数中执行,这个函数中就将下一个要执行的任务的TCP更新到**pxCurrentTCB中,**主要代码如下:

这里要时刻注意,不要迷糊,更新pxCurrentTCB的同时,r3也会指向这个新的pxCurrentTCB。

8、

将r3和r0从msp中恢复出来,注意:此时的r3中的pxCurrentTCB已经是更新之后的了。

9、

这两句,将新的pxCurrentTCB中的pxTopOfStack给了r0,即r0中记录了新task的栈顶位置

10、

从新task中恢复现场即恢复r4~r11,r14

11、

恢复FPU相关寄存器

12、

将栈顶的位置给psp

至此,恢复现场已经全部完成

13、

 

r14即LR中记录了中断返回的地址,跳转执行

相关推荐
La Pulga9 小时前
【FreeRTOS源码学习笔记】任务切换
单片机·mcu·学习·嵌入式·freertos·实时操作系统
La Pulga2 天前
【FreeRTOS源码学习笔记】链表的实现
数据结构·笔记·stm32·mcu·链表·嵌入式·freertos
La Pulga3 天前
【FreeRTOS源码学习笔记】任务创建
笔记·stm32·mcu·学习·嵌入式·freertos·实时操作系统
天吾cc8 天前
FreeRTOS 事件标志组
freertos
编码追梦人9 天前
基于 ESP32-S3 的便携式多参数环境监护仪设计:从硬件选型到 FreeRTOS 多任务落地
数据库·单片机·嵌入式硬件·mongodb·esp32·freertos·低功耗设计
天吾cc13 天前
FreeRTOS 队列集
freertos
硅基手札13 天前
操作系统设计思想系列 06 · FreeRTOS 深度 — 极简可移植范本
架构·操作系统·freertos·实时操作系统·嵌入式开发·设计思想
天吾cc17 天前
FreeRTOS 列表和列表项
freertos
qq_4017004118 天前
RTOS址与值 void*任务传参的坑 走void*万能转的天坑
freertos
天吾cc18 天前
FreeRTOS任务创建和删除
freertos