当前位置: 首页 > news >正文

STM32启动流程全面解析:从上电复位到进入应用程序main函数

目录

1 STM32启动总览

2 从 Boot ROM 到 Boot Loader

3 启动文件

3.1 启动文件到底是什么?

3.2 硬件自动执行(上电复位后):

3.3 启动文件的初始化工作

3.4 一图流总结

4 中断向量表

4.1 BOOT 配置:决定 “程序从哪里读”

4.2 向量表的 “唯一性” 与 “偏移”:

4.3 复位向量指向的复位服务函数

4.4 中断向量表里存的是什么?

4.5 函数的地址(入口地址)是固定的吗?

4.6 中断向量表:“程序入口” 的索引表

5 复位向量、Boot Loader 与启动文件详解

5.1 STM32启动代码与Bootloader的关系

两者的区别:

5.2 用户自定义Bootloader的执行流程

典型的双程序区布局:

Bootloader的启动执行过程:

阶段1:硬件自动执行

阶段2:Bootloader的启动代码执行

阶段3:Bootloader主逻辑

5.3 关键技术:从Bootloader跳转到应用程序

跳转函数的实现:

5.4 应用程序的特殊配置

链接脚本修改 (.ld文件):

应用程序中设置向量表偏移并开启中断:

需要注意

5.5 完整的启动序列

5.6 总结:


1 STM32启动总览

上电复位  ↓  
硬件复位初始化:复位寄存器值、外设恢复默认状态(如关闭外设时钟)  ↓  
BootROM层:
执行BootROM(厂商芯片流片时固化的一段代码,固化在芯片内部)  ├─→ 初始化基础时钟(如切换到HSI内部时钟)  ├─→ 初始化必要外设(如Flash控制器、调试串口)  ├─→ 检测启动模式(通过BOOT引脚或选项字节,启动Bootloader)  │     ├─→ 从内部Flash(0×08000000)启动:直接跳转至用户程序起始地址  │     └─→ 从系统存储器(System Memory)启动:执行厂商Bootloader(如STM32的USB DFU) │     └─→ 从RAM启动(0x20000000) ↓  
Bootloader层:
假设从内部Flash启动:加载用户Bootloader(若有)  ├─→ 从内部Flash/外部存储介质读取用户Bootloader到RAM或直接执行  ├─→ 执行Bootloader的启动代码(.s文件)  ├─→ 用户Bootloader执行:  │     ├─→ 高级时钟配置(如PLL倍频到主频)  │     ├─→ 外设深度初始化(如以太网、文件系统)  │     ├─→ 固件更新检测(OTA、USB/UART通信)  │     └─→ 跳转至应用程序复位向量(即main()的上级入口)  ↓  
Application层:
执行应用程序的启动代码(startup_xxx.s)  ├─→ 初始化C语言环境(如复制.data段、清零.bss段)  ├─→ 调用库初始化(如ARM的__main)  └─→ 最终跳转至main()函数↓
main()函数

2 从 Boot ROM 到 Boot Loader

单片机如何从boot rom到boot loader的部分我的一篇博客已经讲过了:

STM32启动流程解析:从BootROM到BootLoader

3 启动文件

参考阅读:

stm32--启动文件(.s)与启动过程_stm32 .s-CSDN博客

STM32学习笔记(6): 启动代码(Startup Code)_怎么找stm32的运行代码-CSDN博客

3.1 启动文件到底是什么?

启动文件(通常是汇编文件,比如 STM32 的startup_stm32f103xe.s)是由芯片厂商提供、直接与硬件内核(如 Cortex-M3/M4)对接的底层代码,它的作用是:

  1. 定义中断向量表(包括复位向量);

  2. 完成 CPU 上电后最基础的初始化(栈、堆、全局变量等);

  3. 最终 “接力” 跳转到用户代码的main函数。

具体作用:

3.2 硬件自动执行(上电复位后):

  • CPU从向量表的第一个条目(0x00000000)读取初始栈指针(MSP)

  • 从向量表的第二个条目(0x00000004)读取复位向量(Reset_Handler的地址)

  • CPU跳转到Reset_Handler函数

3.3 启动文件的初始化工作

启动文件(如startup_stm32xxxx.s)是一段汇编代码,主要完成以下任务:

startup_stm32xxxx.s 文件:
(下面的代码仅为演示,不代表实际代码); ...                        ; 定义初始堆栈大小
; ...
; ...                        ; 建立中断向量表
DCD     __initial_sp         ; 初始化中断向量表,第一个表项是栈顶地址
DCD     Reset_Handler        ; 初始化中断向量表,第二个表项是复位中断服务函数地址
DCD     NMIException         ; 初始化中断向量表
; ...                        ; 初始化中断向量表...Reset_Handler  ; 复位中断服务函数(上电复位后执行)BL      SystemInit           ; 调用SystemInit函数(初始化系统时钟等)BL      __main               ; 调用__main函数(由编译器提供,负责初始化全局变量)BX      LR                   ; 跳转返回
  • 初始化栈和堆:在 SRAM 中划分栈空间(用于函数调用、局部变量)和堆空间(用于malloc等动态内存分配)。

  • 初始化中断向量表。

  • 初始化全局变量:

    • 将 Flash 中.rwdata段的 “初始化了特定值的变量” 复制到 SRAM 的.data段。

    • 将 SRAM 中.bss段的 “未初始化或初始化为 0 的变量” 清零。

  • 初始化系统外设:如配置系统时钟(SystemInit 函数,部分启动文件会调用)。

  • 启动文件完成所有初始化后,通过汇编指令跳转到用户编写的main函数,此时才真正开始执行你写的第一行用户代码。

3.4 一图流总结

4 中断向量表

通过上文的Boot ROM与Boot Loader我们知道:Boot ROM会读取Boot引脚的电平配置来选择启动方式。

4.1 BOOT 配置:决定 “程序从哪里读”

单片机通过BOOT 引脚(如 STM32 的 BOOT0、BOOT1)配置程序的启动介质,这一步决定了 “中断向量表和代码存在哪里”:

  • BOOT0=0:从主 Flash启动(最常用,用户程序存放在这里)。

  • BOOT0=1,BOOT1=0:从系统存储器启动(用于出厂引导程序(厂家的bootloader)或 ISP 在线编程)。

  • BOOT0=1,BOOT1=1:从SRAM启动(多用于调试场景)。

4.2 向量表的 “唯一性” 与 “偏移”

  • 系统中只有一份 “逻辑上的中断向量表,但它的 “物理存储位置” 可以通过SCB->VTOR偏移到 Flash 或 SRAM。

  • 也就是说,向量表的 “内容(各中断的服务函数地址)” 是固定的,但 “存储这张表的物理地址” 可以切换 —— 这就是 “偏移” 的本质。

4.3 复位向量指向的复位服务函数

(如Reset_Handler)是唯一的,但它的 “存储地址” 会随向量表的偏移而变化:

  • 场景 1:向量表在 Flash(SCB->VTOR = 0x08000000),复位向量(0x08000004)指向的是Flash 中存储的Reset_Handler函数的地址

  • 场景 2:向量表在 SRAMSCB->VTOR = 0x20000000此时需要先将Flash 中的向量表复制到 SRAM(包括Reset_Handler的地址),然后SCB->VTOR指向 SRAM 起始地址。复位向量(0x20000004)指向的是Flash 中存储的Reset_Handler函数的地址。(与 Flash 中原始地址一致,因为向量表是复制过来的)。

4.4 中断向量表里存的是什么?

中断向量表的每一个条目(比如第0条是初始栈指针MSP,第1条是复位向量),存储的并不是指令代码本身,而是一个地址值(32位指针)。

  • 对于复位向量,这个地址值就是 Reset_Handler 函数在内存中的入口地址。

4.5 函数的地址(入口地址)是固定的吗?

在编译和链接阶段,链接器会根据链接脚本(Linker Script)为每一个函数分配一个固定的逻辑地址。假设Reset_Handler 函数被链接器定位到了 Flash 区域的 0x08000C00 这个地址。

那么,无论中断向量表放在哪里(向量表偏移),Reset_Handler 这个函数的本体,其物理位置就在 Flash 的 0x08000C00 处,不会移动。而向量表中的复位向量就永远指向0x08000C00 这个地址。

4.6 中断向量表:“程序入口” 的索引表

上文说过,中断向量表是一个地址列表,存储了复位向量、所有中断服务函数的入口地址。它位于启动介质的起始地址区域(如 Flash 的0x0800 0000处)。

它的结构(以 Cortex-M 内核为例):

5 复位向量、Boot Loader 与启动文件详解

概要:Bootloader 和 APP 是两个工程,两个工程都有自己的启动文件。bootloader如果存在,就会先进bootloader的启动文件,然后到bootloader的main()函数,执行完bootloader的流程,然后跳转到APP的Reset_Handler,执行APP的启动文件,再进APP的main()函数。

5.1 STM32启动代码与Bootloader的关系

启动代码不是Bootloader的一部分,但Bootloader会包含自己的启动代码。

两者的区别:

特性

启动代码 (Startup Code)

Bootloader

目的

芯片基础初始化

应用程序管理、固件更新

位置

任何STM32程序都有

可选的独立程序

功能

栈初始化、变量初始化、时钟配置

跳转判断、固件验证、烧录逻辑

关系:Bootloader本身也是一个程序,它包含了自己的启动代码,用于初始化Bootloader运行所需的环境。Bootloader 和 APP 是两个工程。

5.2 用户自定义Bootloader的执行流程

典型的双程序区布局:

Flash布局 (0x08000000)
├── Bootloader区 (0x08000000 - 0x08007FFF)
│   ├── Bootloader的向量表
│   ├── Bootloader的启动代码
│   └── Bootloader的主逻辑
└── 应用程序区 (0x08008000 - ...)├── 应用程序的向量表(已偏移)├── 应用程序的启动代码└── 应用程序的主逻辑

Bootloader的启动执行过程:

阶段1:硬件自动执行
// 上电后硬件自动完成:
1. 从0x08000004读取复位向量 → Bootloader的Reset_Handler地址
2. 跳转到Bootloader的Reset_Handler

阶段2:Bootloader的启动代码执行
// startup_bootloader.s (Bootloader的启动文件)
Reset_Handler:/* 初始化栈和变量 */bl SystemInit           // 时钟配置bl main_bootloader      // 进入Bootloader主逻辑

阶段3:Bootloader主逻辑
// bootloader.c
int main_bootloader(void)
{/* 1. 硬件初始化 */init_uart();           // 串口通信init_flash();          // Flash驱动init_buttons();        // 按键检测/* 2. 检查是否需要进入升级模式 */if (check_update_condition()) {// 进入固件升级模式firmware_update_mode();}/* 3. 验证应用程序完整性 */if (validate_application()) {// 跳转到应用程序jump_to_application();} else {// 应用程序无效,停留在Bootloaderstay_in_bootloader();}
}

5.3 关键技术:从Bootloader跳转到应用程序

跳转函数的实现:

void jump_to_application(void)
{// 应用程序的起始地址(比如0x08008000)uint32_t app_address = 0x08008000;// 1. 获取应用程序的栈指针和复位向量uint32_t *app_vector_table = (uint32_t*)app_address;uint32_t app_sp = app_vector_table[0];    // 应用程序的栈指针uint32_t app_reset = app_vector_table[1]; // 应用程序的复位向量// 2. 重新初始化外设(避免Bootloader的影响)HAL_RCC_DeInit();HAL_DeInit();// 3. 关闭所有中断__disable_irq();// 4. 设置应用程序的向量表偏移SCB->VTOR = app_address;// 5. 设置应用程序的栈指针__set_MSP(app_sp);// 6. 跳转到应用程序的复位处理函数((void (*)(void))app_reset)();// 不会执行到这里
}

5.4 应用程序的特殊配置

为了让应用程序能在偏移地址运行,需要进行特殊配置:

链接脚本修改 (.ld文件):

MEMORY
{FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 224K  /* 假设要将应用程序放在0x8008000 */RAM (xrw)  : ORIGIN = 0x20000000, LENGTH = 64K
}

应用程序中设置向量表偏移并开启中断:

// 在SystemInit或main函数开始处
SCB->VTOR = FLASH_BASE | 0x8000;  // 设置向量表偏移到0x08008000
__disable_irq();                  // 开启中断

需要注意

  1. SCB->VTOR的配置必须放在HAL_Init()之前(如代码中USER CODE BEGIN 1处)。因为HAL_Init()会初始化 SysTick 定时器,若此时向量表未重定向,会使用 Bootloader 的 SysTick 中断服务函数,导致异常。

  2. Bootloader 跳转前可能会部分外设(如 RTC),APP 若需使用这些外设,需在MX_XXX_Init()后重新启用(如__HAL_RCC_RTC_ENABLE())。

5.5 完整的启动序列

上电复位↓
硬件自动从0x08000004读取向量 → Bootloader的Reset_Handler↓
执行Bootloader启动代码↓
执行Bootloader主逻辑↓
判断是否需要更新固件↓
验证应用程序完整性↓
跳转到应用程序的Reset_Handler (0x08008004)↓
执行应用程序启动代码↓
执行应用程序main函数

5.6 总结:

  1. 启动代码是基础:每个STM32程序(包括Bootloader)都有自己的启动代码

  2. Bootloader是特殊程序:它运行在Flash起始位置,负责管理应用程序

  3. 跳转机制关键:通过修改VTOR寄存器实现从Bootloader到应用程序的平滑切换

  4. 应用程序需要适配:应用程序的链接地址和向量表都需要相应偏移

这种设计使得STM32可以实现IAP(在应用编程),支持固件远程升级等功能。

http://www.dtcms.com/a/414678.html

相关文章:

  • QQ可以在网站做临时会话么ysl免费网站建设
  • 做网站的女生多么镇江市住房城乡建设局网站
  • 后端两个接口需分开写,前端需不串行并同时刷新调用但数据不同步NOTE
  • 华北建设集团有限公司oa网站上海旅游网站建设情况
  • Appinventor笔记4-数字块与文本块
  • 龙溪营销型网站制作上海官网
  • 支付方式大升级!AI代理将进入购物环节
  • 前端实验(序)——前端开发基础
  • reset saved-configuration 概念及题目
  • 口腔病变识别分割数据集labelme格式1317张1类别
  • 做网站的qq兼职网易企业邮箱登录登录入口
  • 【开题答辩全过程】以 springboot毕业设计管理系统为例,包含答辩的问题和答案
  • 越南网站建设青岛做网络直播的网站
  • 【愚公系列】《人工智能70年》045-生成式Al的辉煌与难题(ChatGPT一鸣惊人)
  • 学习嵌入式的第四十二天——ARM——UART
  • MCP协议深度解析(理论篇):AI工具生态的统一语言
  • 沧州网站建设哪家专业微娱网络小程序代理
  • git-filter-repo - 强大的Git历史重写工具
  • 阿里云wordpress在哪里设置密码网站建设相关优化
  • 常州专业网站建设公司咨询做家具有那个网站好
  • Vim复制粘贴剪切命令详解
  • STM32H743-ARM例程8-EXTI外部中断
  • ARM(IMX6ULL)——通信(UART)
  • 网站 开发逻辑开发app开发公司
  • Kong Gateway 实操实例:代理上游服务并配置限流插件
  • 陕西西安网站设计公司重庆住房建设工程信息网官网
  • 【鸿蒙心迹】 我和新人的鸿蒙应用上架之路
  • 鸿蒙NEXT开发浅进阶到精通14:鸿蒙开发项目中遇到的需求问题及解决笔记05
  • 做网站申请多少类商标天津优化代理
  • 学前端视频课程笔记