STM32F103 使用 ARM Cortex-M3 内核,与 STM32F013(Cortex-M0)相比,其压栈行为有所不同,主要体现在异常自动压栈和**手动压栈(函数调用)**两方面。
1. 进入异常/中断时的自动压栈
当 STM32F103 发生 中断或异常 时,Cortex-M3 内核会 自动 将以下 8 个寄存器 按固定顺序 压入栈中:
寄存器 | 说明 |
---|---|
R0 | 通用寄存器 R0 (函数参数) |
R1 | 通用寄存器 R1 |
R2 | 通用寄存器 R2 |
R3 | 通用寄存器 R3 |
R12 | 通用寄存器 R12 |
LR (R14) | 链接寄存器(异常返回地址) |
PC | 发生异常时的程序计数器 |
xPSR | 程序状态寄存器 |
压栈顺序(从低地址到高地址)
plaintext
低地址
↓
[ xPSR ] --> 最高位 (最先压入栈)
[ PC ]
[ LR ]
[ R12 ]
[ R3 ]
[ R2 ]
[ R1 ]
[ R0 ] --> 最低位 (最后压入栈)
↑
高地址
说明:
- 进入中断/异常时,处理器自动保存这些寄存器,以便中断返回时恢复现场。
PC
记录异常发生时的地址,返回时用于恢复执行。xPSR
记录状态信息(如中断号、Thumb 状态等)。- 退出中断时,处理器会自动弹出这些寄存器并恢复 CPU 状态。
比 Cortex-M0 额外支持
Cortex-M3 支持硬件堆栈对齐 (8 字节对齐),同时具备 可选的浮点扩展(如果有 FPU) ,但 STM32F103 没有 FPU,因此不会涉及浮点寄存器的压栈。
2. 函数调用时的手动压栈
当 C 语言函数调用时,ARM 编译器根据 ARM AAPCS(ARM 过程调用标准),会自动进行手动压栈:
-
函数参数传递
-
前 4 个参数 传递给
R0-R3
,超过 4 个的参数需要压入栈。 -
例如:
cvoid test(int a, int b, int c, int d, int e); // e 需要压栈
-
-
局部变量存放
- 若寄存器不够用,局部变量会被压入栈中。
-
保护非易失性寄存器
- 非易失性寄存器 (
R4-R11
和LR
) 必须在函数调用前保存,否则返回时数据会丢失。 - 编译器可能会在函数入口
push
这些寄存器,函数结束时pop
恢复。
- 非易失性寄存器 (
示例
assembly
push {r4-r7, lr} ; 进入函数时,保存 r4-r7 和 lr
...
pop {r4-r7, lr} ; 离开函数时,恢复寄存器
bx lr ; 返回调用方
说明:
push {r4-r7, lr}
:手动压栈,保护R4-R7
和LR
(返回地址)。pop {r4-r7, lr}
:弹出栈,恢复寄存器。bx lr
:返回调用方。
3. STM32F103(Cortex-M3) vs. STM32F013(Cortex-M0)
对比项 | STM32F013(Cortex-M0) | STM32F103(Cortex-M3) |
---|---|---|
自动压栈寄存器 | R0-R3, R12, LR, PC, xPSR |
R0-R3, R12, LR, PC, xPSR |
异常返回自动弹栈 | ✅ | ✅ |
支持 8 字节堆栈对齐 | ❌ | ✅ |
支持可选浮点寄存器压栈 | ❌ | ❌(无 FPU) |
函数调用手动压栈 | R4-R7, LR |
R4-R11, LR (更多可用寄存器) |
总结
-
异常/中断发生时,Cortex-M3 会自动压栈:
R0-R3, R12, LR, PC, xPSR
这 8 个寄存器。- 返回时自动弹栈恢复。
-
普通函数调用时,手动压栈:
push {r4-r7, lr}
保护调用者的寄存器。- 超过 4 个参数的函数参数和局部变量会存入栈中。
-
与 Cortex-M0(STM32F013)相比:
- Cortex-M3 支持更多的寄存器 (
R4-R11
),函数调用时可能会压栈更多寄存器。 - Cortex-M3 支持 8 字节对齐,优化堆栈访问效率。
- Cortex-M3 支持更多的寄存器 (
这样,STM32F103 在不同场景下的压栈行为就清楚了!如果你有更具体的应用场景,可以进一步探讨 😃