STM32F103栈指针(sp)工作原理及地址配置疑问
为什么MCU从Flash执行代码,栈指针指向RAM却能正常运行?
核心原理:Flash与RAM的功能分工
MCU的存储区域按功能明确划分:
- Flash(0x08000000起始):属于只读存储区(仅烧写时可写,运行时只读),专门用来存放程序指令、常量数据——硬件设计为复位后从Flash加载执行指令,这就是程序从这里启动的原因。
- RAM(0x20000000起始):是可快速读写的随机存取区,用于存储运行时的变量、栈、堆等需要动态修改的数据。栈需要频繁执行压栈/出栈操作(存储临时数据、函数返回地址等),必须放在可读写的RAM中,Flash的只读特性完全无法满足这个需求。
复位流程与栈指针的初始化
针对ST主流的Cortex-M架构:
- 复位后硬件自动完成第一步:从Flash向量表的第一个字(0x08000000地址)读取栈顶地址(即链接脚本里的
_estack),加载到SP寄存器。 - 接着从向量表第二个字读取
reset_handler的地址,跳转到该函数执行。
你提供的汇编代码里在reset_handler中再次设置SP,属于冗余但安全的操作——和硬件自动加载的地址完全一致,不会产生冲突,只是为了明确展示栈初始化的步骤。
_estack设为RAM末尾的原因
链接脚本中RAM长度为20K(0x5000字节),起始地址0x20000000,因此末尾地址是0x20000000 + 0x5000 = 0x20005000:
- Cortex-M的栈是向下生长的:压栈时SP值递减,从RAM末尾向起始地址方向使用空间。
- 将栈顶设为RAM末尾,能最大化利用RAM空间,避免栈与全局变量、静态变量等其他RAM区域重叠。
代码片段细节说明
链接脚本中的_estack
_estack = 0x20005000; MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K RAM (rxw) : ORIGIN = 0x20000000, LENGTH = 20K }
_estack是链接器定义的符号,用来标记栈顶地址,会被写入Flash向量表的第一个位置,同时供启动代码引用。
汇编中的SP设置
.type reset_handler, %function reset_handler: LDR r0, =_estack MOV sp, r0
这行代码显式初始化SP,虽然硬件已经自动完成,但保留它的好处包括:
- 兼容非标准启动流程
- 让新手直观理解栈的初始化逻辑
- 后续修改栈顶地址时,只需调整链接脚本的
_estack即可
内容的提问来源于stack exchange,提问作者Rynxie
相关产品推荐
相关产品推荐

