STM32 程序升级学习笔记:Bootloader、IAP 与串口升级流程

1. 前言

简单说明为什么要学习 STM32 程序升级。

可以写:

平时开发 STM32 时,通常使用 ST-LINK 通过 SWD 接口下载程序,这种方式在开发调试阶段很方便。但是如果设备已经安装到现场,后期需要更新功能或者修复 Bug,每次都拆机接下载器就不现实。因此,需要一种可以通过通信接口升级程序的方法,例如串口升级、CAN 升级、USB 升级等。

本文主要记录 STM32 程序升级的基础知识,重点梳理 Bootloader、IAP、Flash 分区、串口接收程序、Flash 写入以及 Bootloader 跳转 App 的基本流程。


2. 学习 STM32 程序升级前需要先搞清楚的问题

在真正理解 STM32 程序升级之前,需要先搞明白下面几个问题:

复制代码
1. Bootloader 是什么?
2. ISP 和 IAP 有什么区别?
3. STM32 Flash 地址为什么从 0x08000000 开始?
4. Bootloader 和 App 为什么要分区?
5. App 为什么要改起始地址?
6. Bootloader 怎么跳转到 App?
7. bin 文件怎么通过串口写进 Flash?

这几个问题是理解 STM32 串口升级的基础。后面的内容基本就是围绕这些问题展开。


3. Bootloader 是什么?

Bootloader 可以理解为 STM32 上电后最先运行的一段程序。

它一般放在 STM32 用户 Flash 的起始位置,也就是:

复制代码
0x08000000

Bootloader 本身不是业务程序,它主要负责启动管理和程序升级。

Bootloader 的主要作用包括:

  1. 判断是否需要升级;

  2. 通过串口接收新的 App 程序;

  3. 擦除 App 所在的 Flash 区域;

  4. 将新程序写入 App 区;

  5. 升级完成后跳转到 App 运行。

可以简单理解为:

复制代码
Bootloader = 程序更新器
App        = 真正的业务程序

4. ICP,ISP,IAP 有什么区别?

STM32 程序升级里经常会看到 ISP 和 IAP,这两个概念很容易混。

4.1 ISP:使用芯片内部 Bootloader 升级

ISP 的意思是系统内编程。

STM32 芯片内部本身就带有一段系统 Bootloader。通过 BOOT0、BOOT1 启动配置,可以让芯片进入系统 Bootloader,然后通过串口、USB 等方式下载程序。

常见理解:

复制代码
BOOT0 = 0:从用户 Flash 启动
BOOT0 = 1:进入系统 Bootloader

ISP 的特点:

  • 不需要自己写 Bootloader;

  • 可以通过串口等接口下载程序;

  • 需要控制 BOOT0 引脚;

  • 升级流程受 STM32 内部 Bootloader 限制。

4.2 IAP:自己写 Bootloader 升级

IAP 的意思是应用内编程。

它的核心是:

复制代码
用户自己写 Bootloader
Bootloader 自己接收程序
Bootloader 自己擦写 Flash
Bootloader 自己跳转 App

IAP 的特点:

  • 灵活性高;

  • 可以自己定义升级协议;

  • 可以通过串口、CAN、USB 等接口升级;

  • 适合实际产品中的程序升级功能。

4.3 ICP:通过下载器烧录程序

ICP(In-Circuit Programming)可以理解为 在线电路编程 ,也就是通过外部下载器,直接把程序烧录到 STM32 芯片内部 Flash 中。

在 STM32 开发中,最常见的方式就是使用 ST-LINKJ-LINK ,通过 SWDJTAG 接口下载程序。

这种方式一般用于开发调试阶段。

例如我们平时在 Keil、STM32CubeIDE 中点击下载,本质上就是通过下载器把编译好的程序写入到 STM32 的 Flash 中。

4.4 简单对比

ICP、ISP、IAP 都可以实现 STM32 程序下载或升级,但它们的使用场景不一样。

对比项 ICP ISP IAP
全称 In-Circuit Programming In-System Programming In-Application Programming
中文理解 在线电路编程 系统内编程 应用内编程
是否需要下载器 需要 ST-LINK / J-LINK 不需要专用下载器 不需要专用下载器
是否需要自己写 Bootloader 不需要 不需要,使用芯片内部 Bootloader 需要,用户自己写 Bootloader
常用接口 SWD / JTAG 串口 / USB 等 串口 / CAN / USB 等
是否需要控制 BOOT 引脚 不需要 通常需要控制 BOOT0 一般不需要
灵活性 一般 一般
适合场景 开发调试、量产烧录 简单程序下载 产品后期升级、自定义升级

简单理解:

复制代码
ICP:用下载器直接烧程序
ISP:用芯片内部 Bootloader 烧程序
IAP:自己写 Bootloader,让程序自己升级程序

实际开发中可以这样区分:

  • 开发调试阶段:一般用 ICP,也就是 ST-LINK 下载;
  • 简单下载程序:可以用 ISP,进入 STM32 内部 Bootloader;
  • 产品升级功能:更适合用 IAP,自己写 Bootloader,通过串口等接口升级 App。

5. STM32 Flash 地址为什么从 0x08000000 开始?

STM32 的用户程序一般存放在内部 Flash 中,而 STM32 用户 Flash 的映射地址通常从:

复制代码
0x08000000

开始。

也就是说,普通情况下,我们写的程序会被下载到这个地址开始的位置。

如果没有 Bootloader,App 程序一般就是这样存放的:

复制代码
0x08000000  → App 程序

STM32 上电后,会从启动地址读取中断向量表,然后执行复位中断入口,最终进入 main 函数。

所以 0x08000000 是理解 STM32 程序启动过程的关键地址。


6. Bootloader 和 App 为什么要分区?

加入 Bootloader 后,Flash 里面就不能只放一个 App 程序了。

因为 Bootloader 要先运行,App 也要存在,所以必须把 Flash 分成两个区域:

复制代码
0x08000000  → Bootloader 区
0x08004000  → App 区

这里假设 Bootloader 占用 16KB 空间,所以 App 从 0x08004000 开始。

这样分区的原因是:

  1. STM32 上电后先执行 Bootloader;

  2. Bootloader 不能被 App 覆盖;

  3. App 放在 Bootloader 后面的独立区域;

  4. Bootloader 需要升级时,只擦除和写入 App 区;

  5. 不需要升级时,Bootloader 直接跳转 App。

简单来说:

复制代码
Bootloader 管升级
App 管业务功能

两者分开放,程序结构才清楚。


7. App 为什么要改起始地址?

默认情况下,App 工程一般是按照:

复制代码
0x08000000

作为起始地址编译的。

但是加入 Bootloader 后,0x08000000 已经被 Bootloader 占用了,App 实际要放到:

复制代码
0x08004000

所以 App 工程也必须修改 Flash 起始地址。

例如 Keil 中需要修改:

复制代码
IROM1 Start:0x08004000
IROM1 Size :根据实际剩余 Flash 设置

如果 App 工程不改起始地址,可能会出现这些问题:

  • App 写入位置和编译地址不一致;

  • Bootloader 跳转后程序跑飞;

  • 中断向量表位置错误;

  • 串口、定时器等中断不能正常工作。

所以做 Bootloader 时,App 起始地址修改是非常关键的一步。


8. bin 文件和 hex 文件有什么区别?

STM32 编译后常见的固件文件有 .hex.bin

8.1 hex 文件

hex 文件是文本格式,文件里面带有地址信息。

它常用于下载器烧录,比如 ST-LINK 下载程序。

8.2 bin 文件

bin 文件是纯二进制文件,不带地址信息。

它里面保存的就是要写入 Flash 的原始程序数据。

做串口 Bootloader 升级时,通常使用 bin 文件。

因为 Bootloader 已经知道 App 要写到哪里,比如:

复制代码
0x08004000

所以串口只需要发送 bin 文件内容,Bootloader 按固定地址写入 Flash 即可。


9. bin 文件怎么通过串口写进 Flash?

这是串口升级的核心流程。

整体流程可以理解为:

复制代码
上位机发送升级命令
    ↓
Bootloader 进入升级模式
    ↓
上位机发送 App 的 bin 文件
    ↓
Bootloader 通过串口接收数据
    ↓
Bootloader 擦除 App 区 Flash
    ↓
Bootloader 将接收到的数据写入 Flash
    ↓
接收完成后跳转到 App

9.1 发送升级命令

可以先通过串口发送一个升级命令,例如:

复制代码
start:len

其中:

  • start 表示开始升级;

  • len 表示后面要发送的 bin 文件长度。

9.2 分包接收 bin 文件

因为 bin 文件可能比较大,不能一次性全部放进 RAM,所以一般采用分包接收。

例如:

复制代码
每次接收 256 字节
接收一包,写入一包
直到接收长度达到 len

9.3 写入 Flash

Bootloader 接收到数据后,将数据写入 App 区域:

复制代码
0x08004000

写入时要注意:

  1. 写 Flash 前要先擦除;

  2. 只能擦除 App 区,不能擦除 Bootloader 区;

  3. 写入地址必须在 App 区范围内;

  4. 写入地址要注意对齐;

  5. 写完后可以校验接收长度。


10. STM32 Flash 擦除与写入流程

STM32 内部 Flash 不能像 RAM 一样直接随便覆盖写入。

写 Flash 前一般需要先擦除,擦除后的 Flash 内容通常为:

复制代码
0xFF

基本操作流程是:

复制代码
解锁 Flash
    ↓
擦除 App 区对应 Flash 页
    ↓
写入接收到的 bin 数据
    ↓
写入完成后锁定 Flash

这部分是 Bootloader 实现升级功能的核心之一。


11. Bootloader 怎么跳转到 App?

Bootloader 写完 App 后,不能直接跳到 0x08004000 就结束。

因为 App 起始地址处存放的是中断向量表,不是普通函数代码。

App 起始地址处有两个重要内容:

复制代码
0x08004000      → App 初始栈顶地址
0x08004000 + 4  → App 复位中断入口地址

所以 Bootloader 跳转 App 的流程应该是:

复制代码
1. 判断 App 起始地址是否合法;
2. 关闭中断;
3. 读取 App 初始栈顶地址;
4. 读取 App 复位中断入口地址;
5. 设置 MSP 主堆栈指针;
6. 跳转到 App 复位入口;
7. App 开始运行。

伪代码可以这样写:

复制代码
#define APP_START_ADDR  0x08004000

typedef void (*pFunction)(void);

void Jump_To_App(void)
{
    uint32_t app_stack;
    uint32_t app_reset;
    pFunction jump_to_app;

    app_stack = *(__IO uint32_t*)APP_START_ADDR;
    app_reset = *(__IO uint32_t*)(APP_START_ADDR + 4);

    __disable_irq();

    __set_MSP(app_stack);

    jump_to_app = (pFunction)app_reset;
    jump_to_app();
}

12. App 为什么要重定位中断向量表?

App 原来默认是从:

复制代码
0x08000000

开始运行的。

但是加入 Bootloader 后,App 实际从:

复制代码
0x08004000

开始运行。

所以 App 的中断向量表也要跟着偏移到新的地址。

在 App 初始化时可以设置:

复制代码
SCB->VTOR = 0x08004000;

如果不设置中断向量表偏移,可能会出现:

  • main 函数可以运行;

  • 串口中断进不去;

  • 定时器中断异常;

  • 外部中断异常;

  • 程序运行不稳定。

所以,只要 App 不在默认地址 0x08000000 运行,就一定要注意中断向量表重定位问题。


13. 串口升级的完整流程总结

最后把整个串口升级流程串起来:

复制代码
1. STM32 上电;
2. Bootloader 从 0x08000000 开始运行;
3. Bootloader 初始化串口;
4. Bootloader 等待升级命令;
5. 如果收到升级命令,开始接收 bin 文件;
6. Bootloader 擦除 App 区 Flash;
7. Bootloader 将 bin 数据写入 0x08004000;
8. 接收完成后检查数据长度;
9. Bootloader 设置 MSP;
10. Bootloader 跳转到 App 复位入口;
11. App 设置中断向量表偏移;
12. App 正常运行。

14. 总结

STM32 程序升级的核心可以总结为一句话:

复制代码
Bootloader 先运行,通过串口接收新 App,写入 App 区 Flash,然后跳转到 App 执行。

理解这套流程,重点要搞清楚:

  1. Bootloader 是什么;

  2. ISP 和 IAP 有什么区别;

  3. STM32 Flash 地址为什么从 0x08000000 开始;

  4. Bootloader 和 App 为什么要分区;

  5. App 为什么要改起始地址;

  6. Bootloader 怎么跳转到 App;

  7. bin 文件怎么通过串口写进 Flash。

这几个问题弄明白后,就基本理解了 STM32 Bootloader 串口升级的基础流程。

相关推荐
qq_571099351 小时前
学习周报四十七
学习
Wonderful U1 小时前
基于Python+Django的私有化云笔记系统:从痛点分析到完整实现
笔记·python·django
问心无愧05131 小时前
ctf show web 入门66
前端·笔记
记帖1 小时前
STM32C542开发(2)----BOOT_SEL设置
stm32·stm32cubemx·stm32cubeide·stm32cubemx2·stm32c542cct6·boot_set·串口烧录
会编程的土豆1 小时前
Redis 常用操作笔记(Go 开发实战)
redis·笔记·golang
Rsingstarzengjx1 小时前
【stm32】尚硅谷基础篇笔记
笔记·stm32·嵌入式硬件
AOwhisky2 小时前
Ceph系列第五期:Ceph 对象存储(RADOS Gateway)精讲
linux·运维·笔记·ceph·gateway·对象存储
凉、介2 小时前
深入理解 ARMv8-A|异常/中断处理
笔记·学习·嵌入式·arm
吃好睡好便好2 小时前
矩阵的求逆运算
人工智能·学习·线性代数·matlab·矩阵