STM32启动流程详解:轻松理解完整启动流程分析
STM32启动流程一般也不用管,但是如果涉及到时钟配置,RTOS移植,堆栈大小的修改,就要了解下了。
搞清楚 STM32 的启动流程,不仅能明白芯片怎么从上电到运行程序,还能为调试和优化打下基础。简单来说,启动流程就是芯片从"睡梦中醒来"到"开始干活"的全过程。
这篇文章将用通俗易懂的语言,带你一步步拆解 STM32 的启动流程,从上电复位到进入 main 函数,揭开它的神秘面纱。
STM32 的启动流程可以分成三个大步骤:
1.上电复位:芯片一上电或被复位,CPU 就从一个固定的地址(通常是 0x00000000)开始跑代码。
2.系统初始化:包括设置堆栈、准备数据、配置时钟等,为程序运行搭好舞台。
3.跳转到 main 函数:一切就绪后,跳到用户写的 main 函数,开始执行你的程序逻辑。
这些步骤主要靠一个叫“启动文件”(startup file)的家伙完成。它通常是用汇编语言写的,负责在 C 语言环境准备好之前,把硬件初始化搞定。接下来,我们就来细细拆解这三大阶段。
STM32启动流程一般也不用管,但是如果涉及到时钟配置,RTOS移植,堆栈大小的修改,就要了解下了。
搞清楚 STM32 的启动流程,不仅能明白芯片怎么从上电到运行程序,还能为调试和优化打下基础。简单来说,启动流程就是芯片从"睡梦中醒来"到"开始干活"的全过程。
这篇文章将用通俗易懂的语言,带你一步步拆解 STM32 的启动流程,从上电复位到进入 main 函数,揭开它的神秘面纱。
STM32 的启动流程可以分成三个大步骤:
1.上电复位:芯片一上电或被复位,CPU 就从一个固定的地址(通常是 0x00000000)开始跑代码。
2.系统初始化:包括设置堆栈、准备数据、配置时钟等,为程序运行搭好舞台。
3.跳转到 main 函数:一切就绪后,跳到用户写的 main 函数,开始执行你的程序逻辑。
这些步骤主要靠一个叫“启动文件”(startup file)的家伙完成。它通常是用汇编语言写的,负责在 C 语言环境准备好之前,把硬件初始化搞定。接下来,我们就来细细拆解这三大阶段。
一、复位和启动文件
1.复位是怎么回事?
STM32 的启动流程从“复位”开始。复位就像按下芯片的“重启键”,可以由几种情况触发:
上电复位:刚插上电源,芯片自动复位。
外部复位:通过复位引脚(比如按下开发板上的 RESET 键)。
软件复位:程序里故意触发复位。
不管哪种复位,结果都一样:CPU 被清零,然后从一个固定地址 0x00000000 开始执行代码。这个地址存的是复位处理程序(Reset_Handler)的入口。
不过,这个 0x00000000 的实际位置会变,取决于启动模式。
STM32 有三种启动模式,通过 BOOT0 和 BOOT1 引脚设置:
从闪存(Flash)启动:最常见,程序从内部 Flash(通常是 0x08000000
)跑。
从系统内存启动:用来跑 bootloader 或更新固件。
从 SRAM 启动:调试时可能会用到。
大多数时候,我们选 Flash 启动,所以复位后,0x00000000
会映射到 0x08000000
,这就是程序的起点。
2.启动文件干啥用的?
启动文件(比如 startup_stm32f10x_md.s)是用汇编语言写的,它的作用就像单片机的“开机助手”,负责在用户代码运行前完成一些关键准备工作。具体来说,它干了以下几件事:
定义中断向量表:这是一个地址表,告诉 CPU 当发生复位、中断等事件时,应该跳转到哪里执行代码。比如,复位时要跳到哪个地址。
设置堆栈:为程序分配一块内存区域(栈),用来存放临时数据,比如函数调用时的变量。
初始化系统:调用一些函数(比如 SystemInit),配置时钟、外设等硬件,确保单片机能正常运行。
跳转到 C 环境:最后跳转到 C 语言的 main 函数,让你的用户代码开始执行。
总结:启动文件是硬件和用户程序之间的桥梁,保证单片机从“刚睡醒”到“开始干活”能顺利过渡。
二、单片机刚上电,第一步执行哪一部分代码?
单片机上电或复位后,CPU 会自动从一个固定的内存地址 0x00000000 开始执行代码。这个地址存放的是中断向量表的起始位置。
中断向量表在哪里?
__Vectors 是 STM32 启动文件中定义的中断向量表(Interrupt Vector Table)。它是一个数据表,通常位于 Flash 的起始地址(例如 0x08000000),用于存储一些关键信息,包括堆栈顶部,复位函数,中断回调函数的地址。
这里的 DCD 表示“定义常量数据”(Define Constant Data),也就是说,__Vectors 存储的不是可执行的指令,而是供 CPU 读取的数据。
在 STM32 中,通常通过设置STM32单片机BOOT 引脚,配置启动模式。如果选择从 Flash 启动(最常见的情况),0x00000000 会映射到 Flash 的起始地址 0x08000000。所以,CPU 实际上是从 0x08000000 开始执行代码。
第一步具体做什么? CPU 从 0x08000000 读取第一个字(32 位数据),这个字是栈顶地址(__initial_sp),用来设置堆栈指针(SP)。然后从下一个地址(0x08000004)读取复位处理程序的地址(Reset_Handler),并跳转到那里执行。
我们来看启动文件中中断向量表的一个典型例子:
DCD:汇编指令,表示“定义一个字”(32 位数据)。
__initial_sp:栈顶地址,复位时 CPU 会把这个值加载到堆栈指针 SP,告诉程序临时数据存哪里。
ok,这里我们插一句,很多人可能会问,那__initial_sp在哪里?到底做了哪些事?
这段代码,通常是在头文件的开头。
下面对每段代码进行解释:
Stack_Size EQU 0x00000800;0x00000400 这里定义了堆栈的大小为 0x00000800,也就是 2048 字节(十六进制 0x800 等于十进制的 2048)。
AREA STACK, NOINIT, READWRITE, ALIGN=3
这行声明了一个名为 STACK 的内存区域:
NOINIT 表示该区域不需要初始化(即留空,不填充默认值)。
READWRITE 表示该区域可读可写,适合用作堆栈。
ALIGN=3 表示按 2^3(即 8 字节)对齐,以满足 STM32 CPU 的内存对齐要求。
Stack_Mem SPACE Stack_Size 这行在 STACK 区域中为堆栈分配了 Stack_Size 大小的空间,也就是 2048 字节。这块内存将用于存储堆栈数据。
Stack_Mem:
表示堆栈内存的起始地址(栈底)。
是堆栈的底部边界,程序运行时 SP 不会低于这个地址,否则会发生堆栈溢出。
__initial_sp: 这是一个标号,表示堆栈内存的结束地址(栈顶),程序启动时,CPU 会将这个地址加载到 SP 寄存器,作为堆栈的初始指针,随着数据压栈,SP 会从这个地址开始向下(地址减小)移动。
在汇编中,标号的位置由其定义处决定,因此 __initial_sp 的地址是 Stack_Mem 的起始地址加上 Stack_Size,也就是堆栈的最高地址。
ok,继续回到中断向量部分:
Reset_Handler:复位处理程序的地址,CPU 会跳转到这里开始执行后续初始化代码。
Reset_Handler 是启动文件中的一个函数,通常会:
调用 SystemInit:配置系统时钟等硬件。
跳转到 __main:进入 C 运行时环境。
SystemInit 是 ST 库提供的函数,负责初始化时钟、外设等,比如设置系统时钟为 72MHz。
__main 是 C 运行时库的入口,它会初始化全局变量(复制 .data 段到 SRAM,清零 .bss 段),然后跳转到用户写的 main 函数,至此头文件初始化完成。
可以把启动流程想象成“起床去上班”:
上电/复位:闹钟响了,你醒了。
设置堆栈__initial_sp:整理床铺,准备临时放东西的地方。
跳转 Reset_Handler:开始起床流程。
调用 SystemInit:洗漱、穿衣服(准备硬件)。
跳转 __main:吃早餐(准备软件环境)。
调用 main:出门上班(运行你的任务)。
STM32 的启动流程虽然复杂,但拆开看就很简单,弄懂这个过程,你就掌握了芯片的“开机密码”了。
最近很多粉丝问我单片机怎么学,我根据自己从业十年经验,累积耗时一个月,精心整理一份「单
片机最佳学习路径+单片机入门到高级教程+工具包」,全部无偿分享给铁粉!!!
除此以外,再含泪分享我压箱底的22个热门开源项目,包含源码+原理图+PCB+说明文档,让你迅速进阶成高手!
教程资料包和详细的学习路径可以看我下面这篇文章的开头。
《单片机入门到高级开挂学习路径(附教程+工具)》
《单片机入门到高级开挂学习路径(附教程+工具)》
《单片机入门到高级开挂学习路径(附教程+工具)》
作者:无际单片机编程