Arm ContxM FreeRTOS MSP之间PSP相互切换

Arm ContxM FreeRTOS MSP之间PSP相互切换

代码从main()函数开始执行的时候,栈默认是MSP开始.当创建线程后从main函数切换到线程的时候,栈从MSP->PSP. 这个过程如下.

简单概况:
FreeRTOS 创建线程的时候,事先往堆栈中保存EXC_TURN值,然后做线程切换的时候,先把保存的EXC_TURN值反映到CPU寄存器,这样执行线程的时候就变成了PSP.也就是说线程的时候使用PSP是人为设置的.相反MSP是硬件自己完成的,比如当发生中断后,执行中断函数的时候硬件吧SP改成MSP.
然后把执行中断函数之前的SP状态保存成EXC_TURN,这样退出中断函数的时候可以知道需不需要转成PSP
图1: 中断函数中退出
在这里插入图片描述
图2: 中断发生过程与说明
在这里插入图片描述

详细流程:

1. FreeRTOS 中创建线程的时候,堆栈中保存EXC_TURN

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
	//创建线程的时候,先填充栈数据
	//填充:xPSR 值
	pxTopOfStack--;
	*pxTopOfStack = portINITIAL_XPSR;	/* xPSR */
	//填充:PC 值(线程入口的函数地址)
	pxTopOfStack--;
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;	/* PC */
	//填充:LR 值
	pxTopOfStack--;
	*pxTopOfStack = ( StackType_t ) prvTaskExitError;	/* LR */

	//保留 R12, R3, R2  R1寄存器的地方
	pxTopOfStack -= 5;	/* R12, R3, R2 and R1. */
	//填充:R0 线程入口函数的 参数.
	*pxTopOfStack = ( StackType_t ) pvParameters;	/* R0 */

	//填充:EXC_TURN. (0xFFFF_FFFD(即返回异常时进入线程模式,使用PSP堆栈)
	pxTopOfStack--;
	*pxTopOfStack = portINITIAL_EXC_RETURN; //#define portINITIAL_EXC_RETURN( 0xfffffffd )

    //保留  R11, R10, R9, R8, R7, R6, R5  R4寄存器的地方
	pxTopOfStack -= 8;	/* R11, R10, R9, R8, R7, R6, R5 and R4. */

	return pxTopOfStack;
}

1.1 EXC_RETURN 说明
根据Cortex-M3的异常处理流程,当发生异常时,CPU先将核心寄存器压入当前堆栈(如果当前是线程模式,则压入PSP堆栈,如果当前是Handler模式,则压入MSP堆栈),然后CPU会将LR设置为一个特殊的值,比如0xFFFFFFFD,然后切换到Handler模式,切换成MSP堆栈,最后进入异常处理例程(异常处理例程总是使用MSP堆栈)。在异常处理例程完成后需要从中断返回时,就将LR的值载入到PC中(通常是BX LR指令,也可以是MOV PC,LR指令,或者POP {…, PC}等指令,只要能将LR赋给PC即可),由于LR的值是0xFFFFFFFD,CPU检测到向PC中载入的是这个特殊值时,就知道是中断返回,于是做中断返回的动作(与压入动作相反:从堆栈中弹出核心寄存器的值,恢复到线程模式或Handler模式等)。

这里这个特殊的值(0xFFFFFFFD)就是EXC_RETURN,它的特点是高28位全部是1,只有低4位可变化,不同的低4位表示不同的中断返回动作。
在这里插入图片描述
2.步骤如下

1.从函数main()开始.这个时候默认是MSP

2 . 启动线程前,根据线程中预存的数据重新设置寄存器.
执行汇编"svc 0" -> 应的中断函数

__asm void vPortSVCHandler( void )
{
	PRESERVE8

	/* 当前线程的TCB地址 */
	ldr	r3, =pxCurrentTCB
	//获取具体的当前线程地址
	ldr r1, [r3]
	//TCB中的第一个数据是 线程堆栈的地址.
	ldr r0, [r1]
	//从线程堆栈中 Pop 数据 
	//r14:portINITIAL_EXC_RETURN( 0xfffffffd )
	ldmia r0!, {r4-r11, r14}
	//获取当前的堆栈位置,设置成PSP
	msr psp, r0
	isb
	mov r0, #0
	msr	basepri, r0
	//执行 bx 0xfffffffd 的结果就是,把SP切换到PSP.
	bx r14
}

3.过上面的函数,已经重新设置了SP, 且退出的时候还会把存储的R0~R3, R12, R14, 返回地址(PC)、xPSR 恢复给寄存器.

4.执行线程的代码,除非线程结束,不然不会再继续main()函数

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值