FREERTOS学习笔记,初始化第一个任务相关

注意:LR与R14表同一个寄存器。

FREERTOS任务切换过程

系统在进入中断前会先依次把xPSR, PC, LR, R12以及R3‐R0由硬件自动压入适当的堆栈中,之后在进入中断函数执行响应中断内容。当中断执行完后再将上述入栈数据依次出栈。

关于上述寄存器:
xPSR:状态字寄存器。

PC:表被打断前任务指令即将执行指令的的地址,用于返回原来地址继续执行。

LR:表示本次任务执行完退出后该执行的地址,举例如下:若函数A调用了函数B,则在进入函数B之前PC内存了函数A下次将执行指令的地址,LR存储了函数A结束时该执行的地址。此时要进入B执行,在进入B执行前会将包括LR(但不包括PC)在内的部分寄存器入栈,然后进入函数B,此时LR更新为进入函数前的PC需要指的下一个指令地址,PC更新为函数B起始指令,LR会一直保持下去直到在调用其他函数,从而再次进栈,然后更新为B函数若不调用其他函数的话,下一个该执行的指令地址。当函数退出时,LR内存的指令地址会传给PC,从而PC接着调用函数B之前的指令执行。然后LR的值出栈。
对于中断则有所不同,当函数A正在执行时,中断来临,此时会类似于调用函数B,会将寄存器入栈,但此时入栈的寄存器不仅包含LR还包含了PC,进入中断不同于调用函数B,LR会更新为一个表示中断结束后返回用户模式还是特权模式的等等的值,当任务结束后向LR跳转就会根据LR的值到响应模式运行,其对应的值类型如下:
在这里插入图片描述
然后中断结束,原来入栈的寄存器返回,PC的值从入栈时PC的值获取,不再通过LR来获取。

代码详解

任务堆栈初始化:

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
	/* Simulate the stack frame as it would be created by a context switch
	interrupt. */

	/* Offset added to account for the way the MCU uses the stack on entry/exit
	of interrupts, and to ensure alignment. */
	pxTopOfStack--;

	*pxTopOfStack = portINITIAL_XPSR;	/* xPSR */
	pxTopOfStack--;
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;	/* PC */
	pxTopOfStack--;
	*pxTopOfStack = ( StackType_t ) prvTaskExitError;	/* LR */

	/* Save code space by skipping register initialisation. */
	pxTopOfStack -= 5;	/* R12, R3, R2 and R1. */
	*pxTopOfStack = ( StackType_t ) pvParameters;	/* R0 */

	/* A save method is being used that requires each task to maintain its
	own exec return value. */
	pxTopOfStack--;
	*pxTopOfStack = portINITIAL_EXC_RETURN;

	pxTopOfStack -= 8;	/* R11, R10, R9, R8, R7, R6, R5 and R4. */

	return pxTopOfStack;
}

首先是模拟在任务中产生中断需要入栈的寄存器来入栈。主要由xPSR,PC,LR,R12,R3,R2,R1,R0。其中PC为任务入口地址,LR根据上面讲的表任务中LR原有值,它为若任务中含有RETURN(正常不该有),则任务执行完返回后执行的地址。在内核中prvTaskExitError地址为:

static void prvTaskExitError( void )
{
	/* A function that implements a task must not exit or attempt to return to
	its caller as there is nothing to return to.  If a task wants to exit it
	should instead call vTaskDelete( NULL ).

	Artificially force an assert() to be triggered if configASSERT() is
	defined, then stop here so application writers can catch the error. */
	configASSERT( uxCriticalNesting == ~0UL );
	portDISABLE_INTERRUPTS();
	for( ;; );
}

即直接执行错误函数。

R12,R0-R3,这些为通用寄存器。

然后再次将R14入栈,这个R14用于在中断中确定中断结束后调用用户栈还是主栈。portINITIAL_EXC_RETURN对应的值为:0xfffffffd,结合上面的表表示返回线程模式。

然后将R11-R4入栈,之后返回现在的栈顶地址。

综合考虑

结合https://blog.csdn.net/yuchendoudou/article/details/102639190中任务切换章节,一个任务A进入PENDSV中断后,先将xPSR,PC,LR,R12,R3,R2,R1,R0入栈,然后进入PENDSV中断处理函数,此时R14即LR会自动更新为任务A对应的portINITIAL_EXC_RETURN,然后将该值入栈,再将R11-R4入栈,之后就将下一个任务相关寄存器依次出栈。

你可能感兴趣的:(FREERTOS学习笔记,初始化第一个任务相关)