电子产业一站式赋能平台

PCB联盟网

搜索
查看: 136|回复: 0
收起左侧

最通俗易懂的STM32完整启动流程分析

[复制链接]

310

主题

310

帖子

2764

积分

三级会员

Rank: 3Rank: 3

积分
2764
发表于 2025-3-3 12:30:00 | 显示全部楼层 |阅读模式
关注公众号,回复“入门资料”获取单片机入门到高级开挂教程
开发板带你入门,我们带你飞

文 | 无际(微信:2777492857)
全文约2454字,阅读大约需要 10 分钟
STM32启动流程一般也不用管,但是如果涉及到时钟配置,RTOS移植,堆栈大小的修改,就要了解下了。
           
搞清楚 STM32 的启动流程,不仅能明白芯片怎么从上电到运行程序,还能为调试和优化打下基础。简单来说,启动流程就是芯片从"睡梦中醒来"到"开始干活"的全过程。
           
这篇文章将用通俗易懂的语言,带你一步步拆解 STM32 的启动流程,从上电复位到进入 main 函数,揭开它的神秘面纱。
           
STM32 的启动流程可以分成三个大步骤:
           
1.上电复位:芯片一上电或被复位,CPU 就从一个固定的地址(通常是 0x00000000)开始跑代码。
2.系统初始化:包括设置堆栈、准备数据、配置时钟等,为程序运行搭好舞台。
3.跳转到 main 函数:一切就绪后,跳到用户写的 main 函数,开始执行你的程序逻辑。
           
这些步骤主要靠一个叫“启动文件”(startup file)的家伙完成。它通常是用汇编语言写的,负责在 C 语言环境准备好之前,把硬件初始化搞定。接下来,我们就来细细拆解这三大阶段。   

twetmj4k1u3640519943.png

twetmj4k1u3640519943.png

           
一、复位和启动文件
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 开始执行代码。这个地址存放的是中断向量表的起始位置。
           
中断向量表在哪里?

5ll3pfrjj31640520043.png

5ll3pfrjj31640520043.png

__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),并跳转到那里执行。
           
我们来看启动文件中中断向量表的一个典型例子:

jsponkskeiz640520143.png

jsponkskeiz640520143.png

           
           
DCD:汇编指令,表示“定义一个字”(32 位数据)。
__initial_sp:栈顶地址,复位时 CPU 会把这个值加载到堆栈指针 SP,告诉程序临时数据存哪里。
           
ok,这里我们插一句,很多人可能会问,那__initial_sp在哪里?到底做了哪些事?
           
这段代码,通常是在头文件的开头。   

kvsl35bvwfd640520243.png

kvsl35bvwfd640520243.png

下面对每段代码进行解释:
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 运行时环境。

ezvh0fun3py640520343.png

ezvh0fun3py640520343.png

         
SystemInit 是 ST 库提供的函数,负责初始化时钟、外设等,比如设置系统时钟为 72MHz。   
         
__main 是 C 运行时库的入口,它会初始化全局变量(复制 .data 段到 SRAM,清零 .bss 段),然后跳转到用户写的 main 函数,至此头文件初始化完成。
           
可以把启动流程想象成“起床去上班”:
           
上电/复位:闹钟响了,你醒了。
设置堆栈__initial_sp:整理床铺,准备临时放东西的地方。
跳转 Reset_Handler:开始起床流程。
调用 SystemInit:洗漱、穿衣服(准备硬件)。
跳转 __main:吃早餐(准备软件环境)。
调用 main:出门上班(运行你的任务)。
           
STM32 的启动流程虽然复杂,但拆开看就很简单,弄懂这个过程,你就掌握了芯片的“开机密码”了。   

end

nl0fsw4untd640520443.jpg

nl0fsw4untd640520443.jpg


下面是更多无际原创的个人成长经历、行业经验、技术干货。
1.电子工程师是怎样的成长之路?10年5000字总结
2.如何快速看懂别人的代码和思维
3.单片机开发项目全局变量太多怎么管理?
4.C语言开发单片机为什么大多数都采用全局变量的形式
5.单片机怎么实现模块化编程?实用程度让人发指!
6.c语言回调函数的使用及实际作用详解

7.手把手教你c语言队列实现代码,通俗易懂超详细!

8.c语言指针用法详解,通俗易懂超详细!
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


联系客服 关注微信 下载APP 返回顶部 返回列表