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一般是满减栈,freertos中,有portSTACK_GROWTH宏控制,portSTACK_GROWTH 小于0为向下增长,大于0向上增长

二、xPortPendSVHandler源代码:

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已经是更新之后的了,即pxCurrentTCB是个指针,本身地址没变,但指向的内容发生了变化。

9、

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

10、

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

11、

恢复FPU相关寄存器

12、

 将栈顶的位置给psp

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

13、

 

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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KiranWang

一起努力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值