嵌入式:单片机启动流程与MSP/PC指针原理
·
单片机上电后并不会直接进入 main() 函数,而是首先执行启动文件 startup_stm32f40_41xxx.s 中定义的启动流程,包含读取中断向量表、PC指针的复位等。其中包含两个重要的角色:
- MSP(栈指针):指向内存中栈区(一种特殊的数据结构)的当前位置。栈用于存储函数调用时的返回地址、局部变量、寄存器上下文等。
-
PC(程序计数器):指示CPU接下来要执行下一条指令。它就像一个指针,始终指向内存中将要执行的下一条指令的地址。CPU每执行完一条指令,PC会自动增加指向下一条,除非遇到跳转指令(如函数调用、条件分支)改变它。
下面通过一个实例介绍MCU其中程序中MSP、PC指针的赋值与变化
int main(void)
{
int a = 1;
int b = a;
SysTick_Config(16000000);
while (1)
{
// 主循环
}
}
主程序启动分为两个阶段:
-
读取向量表 → 初始化 MSP / PC

复位时,MSP与PC指针的变化如下:
//地址 内容
0x08000000 0x20020000 ← 栈顶地址
0x08000004 0x08000101 ← Reset_Handler 地址
//CPU复位后
MSP = 0x20020000
SP = MSP = 0x20020000
PC = 0x08000101
-
Reset_Handler → main()→ 局部变量 a、b 入栈 → SysTick_Config()
初始化时钟配置
systemInit()后才进入主程序main()
main()转化为汇编指令下的MSP、PC变化过程:
地址 汇编指令 PC变化 MSP/SP变化
------------------------------------------------------------------------------------------------
0x08000100 PUSH {LR} PC: 0x08000100 → 0x08000102 SP: 0x20020000 → 0x2001FFFC
0x08000102 SUB SP, SP, #8 PC: 0x08000102 → 0x08000104 SP: 0x2001FFFC → 0x2001FFF4
0x08000104 MOVS R0, #1 PC: 0x08000104 → 0x08000106 SP不变
0x08000106 STR R0, [SP, #4] PC: 0x08000106 → 0x08000108 SP不变
; a = 1
0x08000108 LDR R0, [SP, #4] PC: 0x08000108 → 0x0800010A SP不变
; 读取 a
0x0800010A STR R0, [SP, #0] PC: 0x0800010A → 0x0800010C SP不变
; b = a
0x0800010C LDR R0, =16000000 PC: 0x0800010C → 0x08000110 SP不变
0x08000110 BL SysTick_Config PC: 0x08000110 → SysTick_Config SP本身不一定变
; LR = 0x08000115 ; 函数内部可能继续用栈
0x08000114 B loop PC: 0x08000114 → 0x08000114 SP不变
分析:
- 进入
main()函数后,若函数中定义了局部变量,编译器通常会通过调整 SP/MSP 为当前函数建立栈帧,从而在栈中为局部变量分配空间。例如,两个int类型局部变量通常需要 8 字节栈空间,因此可能通过SUB SP, SP, #8使栈指针向低地址移动。 - 对局部变量的读写并不是移动栈顶,而是通过
SP加偏移地址的方式访问,例如[SP, #4]表示访问栈帧中某个局部变量的位置。函数返回或栈帧释放时,SP/MSP 再恢复到进入函数前的位置
总结:单片机上电后,CPU 根据中断向量表初始化 MSP,并将 PC 设置为
Reset_Handler的入口地址,从而开始执行启动代码。PC 在顺序执行时通过自增自动指向下一条指令。进入main()后,局部变量通过调整 MSP 的栈顶的位置来分配存储空间;变量的读写则通过基于 MSP 的偏移寻址完成。
更多推荐

所有评论(0)