杂项知识——单片机启动流程(Cortex-M 内核)——个人笔记
一句话总结:
上电后硬件先清零寄存器→自动从向量表加载 MSP 和 Reset_Handler→执行汇编启动文件(关中断→调 SystemInit→调 __main)→__main 完成 .data/.bss 初始化→最终跳到 main()
启动流程:
① 硬件复位:所有寄存器清零
一句话:上电/复位电路先把 CPU 与片上外设的寄存器全部拉到已知默认值。
展开:
1、电源监控器检测到 VDD 过阈值后,内部产生 POR/BOR 脉冲;
2、所有寄存器(R0-R12、PSP、MSP、PRIMASK、NVIC、TIM、GPIO…)被强制置 0 或默认值;
3、复位完成后,CPU 放开总线,准备取第一条指令。
② 向量表读取:取 MSP + Reset 向量
一句话:CPU 自动从地址 0x0000 0000 读出 8 个字节,前 4 字节 → MSP,后 4 字节 → PC(一般指向复位)。
展开:
1、0x0000 0000 实际被重映射到 Flash(0x0800 0000)或 Boot ROM;
2、硬件级把 0x0000 0000 处的 32 位数写入 MSP;
3、把 0x0000 0004 处的 32 位数写入 PC(最低位必须为 1,表示 Thumb 状态)。
③ 汇编启动文件:Reset_Handler
一句话:汇编写的 Reset_Handler 先关中断、再调 C 函数 SystemInit()。
展开:
1、startup_stm32fxxx.s 内实现;
关键动作:
cpsid i 关全局中断;
可选:使能 FPU(M4/M7);
bl SystemInit → 配置时钟、Flash 等待周期、重设 VTOR;
bl __main(C 库入口,绝不返回)。
④ C 库入口:__main
一句话:__main 完成 C 运行环境:把 .data 从 Flash 搬到 RAM,把 .bss 清零,调构造函数。
展开:
由编译工具链提供(Keil 的 __main,GCC 的 _start);
步骤:
复制映像:.data 有初值的全局变量 → RAM;
清零段:.bss、.sbss 清 0;
C++ 构造:调用 __libc_init_array/__cxx_global_var_init;
最终 bl main 跳转。
⑤ 用户 main() 入口
一句话:至此 C 世界就绪,用户开始写外设初始化与业务逻辑。
展开:
栈指针 MSP 已指向正确 RAM 顶部;
时钟已跑到设定频率;
全局变量、静态对象已初始化;
NVIC 已可开中断;
main() 永不返回,返回将陷入死循环或 HardFault。
OS程序流程:
①流程
- 系统启动时,运行启动代码,此时使用的是主堆栈指针(MSP)
- 调用SVC中断(Supervisor Call),os将第一个任务进行切换,触发特权级切换(MSP)
- SVC中断完成后(MSP),加载首个任务的上下文,切换至进程堆栈指针(PSP)开始运行
- 用户级的线程模式开始执行,此时CPU运行于用户模式,使用PSP。
- 中断到来时,自动切换回MSP堆栈,处理中断(特权模式)中断必须在MSP中进行。
- 中断处理完成后,恢复PSP,继续用户任务执行。
- 当需要任务切换时(如tick中断或任务主动让出CPU),触发PendSV异常。
- 在PendSV中断处理函数中,保存旧任务的寄存器到旧任务的堆栈(PSP)。
- 从就绪列表选择新任务,加载新任务的寄存器状态,并切换PSP至新任务堆栈。
- 新任务开始执行,继续使用PSP堆栈。
② 注意事项
1、操作中断里的变量,一定用的是MSP指针。在OS中,进入任务中后,运行的就是PSP指针。
2、任务的切换是在PendSV中断中进行
更多推荐

所有评论(0)