news 2026/7/17 11:09:28

STM32双区IAP在线升级实现详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32双区IAP在线升级实现详解

1. 嵌入式系统在线升级(IAP)技术实现详解

在工业控制、物联网终端、智能仪表等长期部署的嵌入式设备中,程序更新需求日益频繁。现场更换硬件或返厂升级已无法满足快速迭代与远程维护的要求。在线升级(In-Application Programming, IAP)技术成为保障设备持续可用性与功能演进的核心能力。本文以STM32F103RB为硬件平台,完整阐述一种基于双应用区(App1 + App2)与BootLoader协同工作的可靠IAP方案。该方案不依赖外部调试器,仅通过串口即可完成固件安全更新,具备分区管理、校验保护、向量表重映射及异常回滚等工程级特性。

1.1 系统设计目标与约束条件

本方案面向资源受限的Cortex-M3内核MCU,需在128KB片内Flash(共128页,每页1KB)约束下达成以下目标:

  • 零外部依赖:升级过程不中断设备主功能,无需JTAG/SWD调试接口参与;
  • 断电安全:升级过程中意外掉电后,系统仍能从已验证的旧版本启动;
  • 版本可控:支持运行时识别当前固件版本,并在升级失败时自动回退;
  • 协议可扩展:底层升级逻辑与传输协议解耦,便于后续替换为Wi-Fi、BLE或LoRa等无线通道;
  • 内存隔离:BootLoader、App1、App2三者代码空间严格分离,互不覆盖。

上述目标决定了必须采用“引导程序+双应用区”的经典架构,而非单区覆盖式升级。其本质是将Flash划分为功能明确的物理区域,通过运行时跳转与数据搬运实现新旧固件的原子切换。

2. Flash存储分区规划与地址映射

STM32F103RB的主Flash地址空间为0x08000000 ~ 0x0801FFFF(128KB)。为支撑IAP机制,需对该空间进行静态划分。本方案采用三级分区策略,各区域起始地址、大小及用途如下表所示:

分区名称起始地址大小页范围主要用途
BootLoader区0x0800000020KB第0~19页存放引导程序,永不更新
App1区0x0800500056KB第20~75页当前运行的应用程序主区
App2区0x0801400044KB第76~127页升级包暂存区(备份区)

:20KB BootLoader预留空间已充分考虑未来功能扩展(如USB DFU、加密校验等),实际初始BootLoader代码仅占用约8KB。

该划分遵循三项工程原则:

  1. BootLoader不可覆盖性:将其置于Flash起始位置,Keil默认链接脚本即从0x08000000开始加载,避免因配置失误导致引导代码被擦除;
  2. App1与App2容量非对称设计:App1作为主运行区需容纳完整业务逻辑,故分配更大空间;App2仅用于暂存待升级的bin文件,按典型固件尺寸(≤40KB)预留;
  3. 页对齐擦除:所有分区边界严格对齐1KB页边界(0x08005000= 20×1024),确保Flash擦除操作可精确控制,避免误擦相邻区域。

3. BootLoader核心机制解析

BootLoader是系统上电后首条执行的代码,其唯一职责是决定启动哪个应用程序。本方案BootLoader流程高度精简,仅包含三个确定性步骤:

3.1 启动判据:App2区有效性标志

为避免每次启动均执行冗余拷贝,BootLoader需快速判断App2区是否存有有效升级包。此处采用标志位法:利用App2区末尾地址0x0801FFFC(即最后1个字)作为状态寄存器。

  • 空闲状态:Flash擦除后该地址值为0xFFFFFFFF(全1);
  • 有效升级包:App程序在完成bin文件写入后,将该地址写入特征值0xAAAAAAAA

此设计优势显著:

  • 零开销检测:仅一次32位读取操作,耗时<1μs;
  • 抗干扰性强0xAAAAAAAA与擦除态0xFFFFFFFF汉明距离达16位,单粒子翻转(SEU)极难误触发;
  • 无额外存储开销:复用Flash物理空间,不占用RAM或独立EEPROM。

3.2 固件搬运:App2→App1安全拷贝

当检测到0x0801FFFC == 0xAAAAAAAA时,BootLoader执行关键动作——将App2区全部内容复制至App1区。该过程需严格遵循Flash操作规范:

// 伪代码:App2→App1拷贝流程 if (*(uint32_t*)0x0801FFFC == 0xAAAAAAAA) { // 1. 擦除App1区全部页(第20~75页) for (uint16_t page = 20; page <= 75; page++) { FLASH_ErasePage(0x08000000 + page * 1024); } // 2. 逐页拷贝App2数据(源地址0x08014000,目标0x08005000) uint32_t src_addr = 0x08014000; uint32_t dst_addr = 0x08005000; uint32_t len = 0x0000B000; // 44KB while (len > 0) { uint32_t word = *(uint32_t*)src_addr; FLASH_ProgramWord(dst_addr, word); src_addr += 4; dst_addr += 4; len -= 4; } // 3. 清除App2有效标志,防止重复拷贝 FLASH_ProgramWord(0x0801FFFC, 0xFFFFFFFF); }

关键约束:Flash编程必须以“字”(32位)为单位,且目标地址需4字节对齐;擦除操作以“页”为单位,不可跨页部分擦除。

3.3 应用跳转:栈指针重置与向量表重映射

拷贝完成后,BootLoader需跳转至App1入口。此过程涉及两个底层硬件操作:

(1)主栈指针(MSP)初始化

Cortex-M3复位后从地址0x08000000读取初始MSP值(位于向量表首字)。App1的向量表位于0x08005000,故需手动加载其首字:

__asm void MSR_MSP(uint32_t ulAddr) { MSR MSP, r0 // 将ulAddr值载入主栈指针 BX r14 // 返回调用者 }
(2)向量表偏移寄存器(VTOR)配置

为使CPU从中断时能正确索引App1的向量表,需设置SCB->VTOR寄存器:

SCB->VTOR = 0x08005000; // 指向App1向量表基址
(3)绝对跳转执行

获取App1复位向量(向量表第二个字,地址0x08005004)并执行:

typedef void (*pFunction)(void); pFunction JumpToApp; uint32_t jumpAddress = *(uint32_t*)(0x08005004); // 复位地址 JumpToApp = (pFunction)jumpAddress; MSR_MSP(*(uint32_t*)0x08005000); // 加载App1栈顶 JumpToApp(); // 跳转执行

安全性校验:跳转前检查复位地址高位是否为0x2000xxxx(SRAM)或0x0800xxxx(Flash),防止跳转至非法地址导致HardFault。

4. App1应用程序升级逻辑实现

App1作为用户业务逻辑载体,在运行期间承担升级包接收与写入App2区的任务。其核心模块包括:串口通信驱动、YModem协议解析、Flash编程接口及版本管理。

4.1 YModem协议在嵌入式端的轻量化实现

YModem是XModem的增强版,支持1024字节大数据包及文件名传输,非常适合固件升级场景。本方案针对资源受限MCU进行裁剪,仅实现关键帧类型:

帧类型字节值作用说明
SOH0x011024字节数据包起始标识
EOT0x04文件传输结束标识
ACK0x06正确接收确认
NAK0x15校验失败请求重传
CCC0xC0连续三次0xC0表示会话建立

接收状态机设计
采用有限状态机(FSM)管理升级流程,状态定义如下:

  • TO_START:等待客户端发送SOH起始帧
  • TO_RECEIVE_DATA:接收连续数据包(包序号递增)
  • TO_RECEIVE_EOT2:收到首个EOT后等待第二个EOT(YModem要求双EOT确认)
  • TO_RECEIVE_END:升级完成,触发系统复位

CRC16校验实现
使用标准CRC-16-CCITT算法(多项式0x1021),对SOH帧后1024字节数据计算校验值,与帧末2字节比对:

uint16_t CRC16_CCITT(const uint8_t *data, uint16_t len) { uint16_t crc = 0xFFFF; for (uint16_t i = 0; i < len; i++) { crc ^= data[i] << 8; for (uint8_t j = 0; j < 8; j++) { if (crc & 0x8000) crc = (crc << 1) ^ 0x1021; else crc <<= 1; } } return crc; }

4.2 App2区Flash编程关键流程

接收完一个SOH数据包后,需将其有效载荷(1024字节)写入App2区对应地址。由于Flash编程需按“字”操作,需进行数据对齐处理:

// 将1024字节数据写入App2区指定页偏移 void WriteFlash(uint32_t WriteAddr, uint32_t *pBuffer, uint16_t NumToWrite) { HAL_FLASH_Unlock(); // 解锁Flash __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR); for (uint16_t i = 0; i < NumToWrite; i += 4) { uint32_t word = pBuffer[i/4]; if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, WriteAddr + i, word) != HAL_OK) { // 编程失败处理:记录错误,停止升级 Error_Handler(); } } HAL_FLASH_Lock(); // 锁定Flash }

关键注意事项

  • 写入前必须确保目标页已擦除(本方案在接收首帧时执行Erase_page(Application_2_Addr, 40));
  • HAL_FLASH_Program()返回HAL_OK才代表写入成功,需严格检查;
  • 避免在Flash编程期间响应中断(可临时关闭全局中断)。

4.3 版本信息管理与向量表重映射

App1启动后首要任务是重映射向量表,否则中断服务例程(ISR)将指向BootLoader区,导致不可预知行为:

// 在App1的main()函数开头执行 SCB->VTOR = 0x08005000; // 强制向量表定位到App1区 __set_MSP(*(uint32_t*)0x08005000); // 初始化主栈指针

版本信息通过宏定义固化在代码中,编译时生成:

#define APP_VERSION_MAJOR 0 #define APP_VERSION_MINOR 0 #define APP_VERSION_PATCH 1 #define APP_VERSION_STR "0.0.1"

升级完成后,App1通过串口打印当前版本,供运维人员验证:

printf("App Version: %s\r\n", APP_VERSION_STR);

5. Keil MDK工程配置要点

IAP方案成功落地高度依赖正确的IDE配置。本节列出Keil v5.37环境下关键设置项:

5.1 BootLoader工程配置

配置项说明
Target → IRAM10x20000000,20KRAM区大小,满足BootLoader栈需求
Target → IROM10x08000000,20KFlash起始地址与大小
Output → Create HEX File✅启用生成.hex用于烧录
C/C++ → DefineBOOTLOADER条件编译标识

分散加载文件(scatter file)示例

LR_IROM1 0x08000000 0x00005000 { ; load region size_region ER_IROM1 0x08000000 0x00005000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x00005000 { .ANY (+RW +ZI) } }

5.2 App1工程配置

配置项说明
Target → IROM10x08005000,56K起始地址必须与分区规划一致
Target → IRAM10x20000000,20KRAM区需容纳YModem接收缓冲区
Output → Create BIN File✅启用生成.bin供YModem传输
C/C++ → DefineAPP1区分BootLoader与App1编译环境

关键链接脚本修改
在App1的startup_stm32f103xb.s中,将向量表起始地址修正为0x08005000

; 修改前(默认) ; VECT_TAB_OFFSET EQU 0x00000000 ; 修改后 VECT_TAB_OFFSET EQU 0x00005000 ; 相对于IROM1基址的偏移

6. 升级流程实操验证

6.1 分步烧录流程

  1. BootLoader烧录

    • 使用ST-Link Utility或J-Flash,选择BootLoader.hex文件;
    • 设置擦除方式为Erase Sectors,地址范围0x08000000~0x08004FFF(20KB);
    • 烧录完成后复位,通过串口1观察输出:
      > BootLoader running... > Checking App2... empty > Jumping to App1...
  2. App1首次烧录

    • 打开App1工程,修改APP_VERSION_STR"0.0.1"
    • 编译生成App1.bin
    • 使用ST-Link Utility烧录至0x08005000,擦除范围0x08005000~0x08013FFF
    • 启动后串口1输出:
      > App Version: 0.0.1 > Ready for upgrade...

6.2 YModem升级操作

  1. 使用Xshell连接开发板串口2(升级专用通道);
  2. 在Xshell中执行rz -y命令(需安装lrzsz工具),选择App1_v0.0.2.bin文件;
  3. 观察串口1(调试通道)实时日志:
    > Receive start... > Receive data bag:128 byte > Receive data bag:256 byte > ... > Receive end... > Upgrade success! Resetting...
  4. 系统复位后,串口1输出变为:
    > App Version: 0.0.2

6.3 断电恢复测试

在YModem传输过程中强制断电,再次上电后BootLoader检测到App2区未写入完整0xAAAAAAAA标志,跳过拷贝直接启动App1(v0.0.1),确保业务连续性。

7. BOM清单与硬件资源占用

本方案纯软件实现,无需额外硬件器件。所依赖的STM32F103RB片上资源占用情况如下:

资源类型占用详情备注
FlashBootLoader: ≤8KB, App1: ≤56KBApp2区仅存储bin文件,不占代码空间
SRAM≈8KB(含YModem 1024B接收缓冲区)未启用堆内存,全部静态分配
UARTUSART1(调试打印),USART2(YModem)可复用同一串口,但需时分复用
GPIO全部通过串口通信,无需LED指示灯
定时器SysTick(HAL_Delay)用于超时控制

兼容性说明:本方案可无缝迁移至STM32F103C8T6(64KB Flash)、STM32F103VET6(512KB Flash)等同系列芯片,仅需调整分区地址与页数参数。

8. 方案扩展与工程优化方向

本基础IAP框架具备良好可扩展性,实际项目中可按需增强:

  • 安全加固:在App2写入前增加AES-128解密步骤,BootLoader校验App1区SHA256哈希值;
  • 多区冗余:扩展为App1/App2/App3三区,支持A/B/C滚动升级,彻底消除升级窗口;
  • 无线升级:将YModem接收模块替换为ESP8266 AT指令解析器,通过HTTP GET下载bin文件;
  • 差分升级:集成bsdiff算法,在服务器端生成差分包,终端侧执行bspatch还原,降低传输流量;
  • OTA云平台对接:集成MQTT协议,从云端获取升级包URL及数字签名,实现远程集中管控。

所有扩展均不改变BootLoader核心逻辑——其职责始终是“验证→搬运→跳转”,这正是嵌入式IAP设计的稳定性基石。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 14:05:07

小白也能用的视频修复神器:MTools开箱即用,GPU加速超简单

小白也能用的视频修复神器&#xff1a;MTools开箱即用&#xff0c;GPU加速超简单 你是不是也遇到过这种情况&#xff1a;翻出十几年前的家庭录像&#xff0c;想重温一下美好时光&#xff0c;结果发现画面模糊得像蒙了一层雾&#xff0c;声音也夹杂着刺耳的杂音&#xff0c;播放…

作者头像 李华
网站建设 2026/7/14 14:05:07

机器学习入门:用Ridge回归预测共享单车需求(手把手教程)

机器学习实战&#xff1a;用Ridge回归预测共享单车需求的完整指南 共享单车系统作为城市短途出行的解决方案&#xff0c;其需求预测直接影响运营效率。本文将带你从零开始构建一个Ridge回归模型&#xff0c;完整覆盖数据探索、特征工程到模型优化的全流程。不同于传统教程&…

作者头像 李华
网站建设 2026/7/14 14:05:06

OpenClaw极简方案:仅用QwQ-32B实现每日待办自动生成

OpenClaw极简方案&#xff1a;仅用QwQ-32B实现每日待办自动生成 1. 为什么选择这个极简方案 每天早上打开邮箱&#xff0c;总能看到十几封待处理的邮件——会议邀请、任务分配、项目跟进。手动整理这些信息至少需要20分钟&#xff0c;而最痛苦的是优先级判断&#xff1a;哪个…

作者头像 李华
网站建设 2026/7/14 14:05:25

VLC效率倍增指南:解锁全能媒体播放的7个实战技巧

VLC效率倍增指南&#xff1a;解锁全能媒体播放的7个实战技巧 【免费下载链接】vlc VLC media player - All pull requests are ignored, please follow https://wiki.videolan.org/Sending_Patches_VLC/ 项目地址: https://gitcode.com/gh_mirrors/vl/vlc &#x1f4cc;…

作者头像 李华
网站建设 2026/7/14 14:05:26

告别C盘!Win10下将Apache2.4安装到D盘的保姆级图文教程

告别C盘&#xff01;Win10下将Apache2.4安装到D盘的保姆级图文教程 对于Windows开发者而言&#xff0c;系统盘空间管理始终是个绕不开的痛点。尤其是像Apache这样的服务型软件&#xff0c;默认安装路径往往直接占用宝贵的C盘资源。本文将手把手教你如何从零开始&#xff0c;将A…

作者头像 李华