保存现场几种场景
场景一:普通函数调用 (如 main调用fun)
触发机制:由
BL(Branch with Link)等跳转指令在软件层面主动触发。谁来完成:编译器自动生成的代码来完成。
保存了哪些寄存器:
R4-R11:如果被调用的函数(fun)内部使用了这些寄存器,它会在函数的开头(入口处)用PUSH指令将它们压栈,在返回前用POP恢复。LR:BL指令执行时,CPU硬件会自动将返回地址存入LR。但若fun内部还有嵌套调用,它也会把LR压栈保存。
原理:这确保了函数调用前后,调用者(main)的
R4-R11内容保持不变,保证了程序的确定性。
场景二:异常/硬件中断 (如外部中断、SysTick)
触发机制:由外部事件或内部外设在硬件层面异步触发。
谁来完成:CPU硬件自动完成一部分,程序员/编译器完成另一部分。
保存了哪些寄存器:分两步进行。
硬件自动压栈 (8个寄存器):当异常发生时,Cortex-M4内核会自动将以下8个寄存器压入当前使用的栈中(PSP或MSP),组成一个“栈帧”。
R0-R3,R12,LR,PC,xPSR这在硬件上保证了中断处理函数可以作为普通的C函数来编写。
软件可选保存:如果中断服务程序(ISR)需要用到
R4-R11,编译器会像普通函数一样,在ISR的入口处生成代码来保存它们。
特殊点:中断嵌套与返回。处理异常时,
LR寄存器会被赋一个特殊值EXC_RETURN,用于指示异常返回时使用哪个栈指针(MSP或PSP)以及返回到线程模式还是处理模式,从而支持嵌套中断。
场景三:FreeRTOS任务切换
触发机制:由SysTick时钟滴答或任务主动阻塞,在软件层面主动触发
PendSV(可挂起服务调用)异常来执行。谁来完成:CPU硬件自动完成一部分,FreeRTOS的移植层代码 (port.c)完成其余部分。
保存了哪些寄存器:这是保存最完整、最彻底的一种场景。
硬件自动压栈:触发
PendSV异常后,硬件首先自动保存了和场景二相同的R0-R3,R12,LR,PC,xPSR到当前任务的栈中。软件强制保存所有剩余寄存器:紧接着,在
PendSV的中断服务函数(通常是xPortPendSVHandler)中,FreeRTOS的汇编代码会主动将当前任务的所有其他通用寄存器,包括R4-R11全部压栈保存。
原理:因为任务切换是完全独立的上下文切换,当前任务的执行现场(所有寄存器的值)必须被完整地保存到它自己的任务栈中,等到下一次它获得CPU时才能精确地恢复现场,继续执行。
