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中记录了中断返回的地址,跳转执行

相关推荐
青鱼2918 小时前
时间片在FreeRTOS中的含义解析
freertos·时间片轮转
济6171 天前
FreeRTOS 控制任务设计 (2)--- 运动学逆解 + PID 闭环 + PWM 驱动全流程实现
stm32·单片机·嵌入式·freertos
济6171 天前
FreeRTOS 控制任务设计 (1)--- 双模式闭环控制:IDLE/RUN 状态机与任务通知机制
stm32·单片机·嵌入式·freertos
W.W.H.5 天前
FreeRTOS移植(保姆级教程)
经验分享·单片机·操作系统·freertos·rtos
波特率1152008 天前
FreeRTOS当中的Mail Queue使用教程(CMSIS_v1)
单片机·操作系统·freertos
济61710 天前
FreeRTOS 通信任务设计(3)---基于状态机的串口协议帧解析
stm32·嵌入式·freertos
济61710 天前
FreeRTOS 通信任务设计(4终)----从字节流到有效帧的完美闭环
stm32·嵌入式·freertos
济61713 天前
FreeRTOS 通信任务设计(2)----UART+DMA 环形缓冲 + 空闲中断+ 流缓冲区--- 高效接收方案详解
stm32·单片机·嵌入式·freertos
济61713 天前
FreeRTOS 通信任务设计(1)---STM32 串口 DMA + 协议帧解析 + CRC 校验全流程详解
stm32·嵌入式·freertos
Wave84515 天前
FreeRTOS软件定时器详解
stm32·单片机·freertos