双扇区STM32动态固件更新的Bootloader适配方案咨询
STM32双应用扇区OTA固件适配问题与解决方案
项目背景
基于STM32开发带Bootloader的固件更新系统,设备包含两个应用扇区(APP1、APP2),用于存储同一应用的不同版本。OTA升级逻辑为:当前运行APP1时,将新版本写入APP2;当前运行APP2时,写入APP1。复位后Bootloader通过读取两个扇区上方的元数据,选择版本较新的应用启动。
核心问题
项目仅编译单固件镜像,但链接脚本硬编码绑定到APP1或APP2的存储地址,导致固件与特定存储区域强绑定,无法动态适配所在的扇区。
初步思路
考虑使用两个不同的链接脚本编译两次,生成对应APP1和APP2的固件,OTA前根据当前运行扇区选择对应版本。但希望了解是否存在Bootloader动态配置应用的方法,或更优的单镜像适配双扇区方案。
附:内存映射
/* Specify the memory areas */ MEMORY { /* Bootloader region */ FLASH_BOOTLOADER(rx) : ORIGIN = 0x08000000, LENGTH = 32K /* METADATA region for App1 */ FLASH_METADATA_APP1 (r) : ORIGIN = 0x08008000, LENGTH = 1K /* Application 1 region */ FLASH_APP1 (rx) : ORIGIN = 0x08008400, LENGTH = 363K /* METADATA region for App2 */ FLASH_METADATA_APP2 (rx) : ORIGIN = 0x08063000, LENGTH = 1K /* Application 2 region */ FLASH_APP2 (rx) : ORIGIN = 0x08063400, LENGTH = 363K /* Eeprom section */ FLASH_EEPROM (rx) : ORIGIN = 0x080BE000, LENGTH = 8K /* Reserved region */ FLASH_RESERVED (r) : ORIGIN = 0x080BF000, LENGTH = 60K /* Wireless stack region */ FLASH_BLE_STACK (x) : ORIGIN = 0x080CE000, LENGTH = 120K /* FUS region */ FLASH_FUS (rx) : ORIGIN = 0x080EC000, LENGTH = 16K /* RAM region */ RAM (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8 RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K }
附:链接脚本片段
/* The startup code goes first into FLASH */ .isr_vector : { . = ALIGN(4); KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(4); } >FLASH_APP1
解决方案
方案1:单镜像位置无关代码(PIC)适配
- 核心逻辑:让应用代码脱离绝对地址依赖,运行时自动识别所在扇区并调整运行环境
- 实现步骤:
- 修改链接脚本,将应用链接到虚拟基地址(如
0x00000000),开启编译器位置无关代码选项(GCC需添加-fPIC或-fpic) - 在应用启动阶段(如启动文件的复位处理函数中),读取当前PC值,判断其落在APP1或APP2的地址范围内,计算自身所在扇区的基地址
- 动态设置中断向量表偏移:通过
SCB->VTOR寄存器,将向量表地址设置为当前应用扇区的起始地址 - 所有访问自身Flash数据的代码,均通过相对地址计算(如使用段属性标记数据,通过偏移量访问)
- 修改链接脚本,将应用链接到虚拟基地址(如
- 注意:部分STM32型号对VTOR的设置有对齐要求(如必须是0x200的倍数),需确保扇区起始地址符合要求;外设初始化代码若有硬编码Flash地址,需改为相对偏移。
方案2:Bootloader动态重定位应用
- 核心逻辑:由Bootloader负责调整应用的运行环境,让应用无需感知自身所在扇区
- 实现步骤:
- 编译应用时,链接脚本使用虚拟地址或统一基地址(无需绑定到APP1/APP2)
- Bootloader读取选中应用的元数据,获取其所在扇区的基地址
- 设置
SCB->VTOR为该应用扇区的起始地址,然后跳转到应用的复位向量 - 若应用需要访问自身Flash数据,Bootloader可将应用基地址写入共享RAM区域,应用启动后从该区域读取基地址并计算实际访问地址
- 优势:无需修改应用的编译流程,仅需Bootloader增加少量逻辑,兼容性较好。
方案3:双镜像编译(初步思路优化)
- 核心逻辑:编译两个分别绑定APP1和APP2的固件镜像,OTA时根据当前运行扇区选择对应镜像下载
- 实现步骤:
- 复制现有链接脚本,生成两个版本:
linker_app1.ld(绑定FLASH_APP1)和linker_app2.ld(绑定FLASH_APP2) - 在编译脚本(Makefile)或IDE工程中添加两个编译目标,分别使用不同的链接脚本,生成两个独立的固件镜像
- OTA过程中,Bootloader先读取当前运行的应用扇区,然后请求对应版本的固件镜像,写入另一扇区
- 复制现有链接脚本,生成两个版本:
- 优势:实现最简单,无需修改应用核心代码,兼容性最好,适合对开发复杂度要求低的场景。
内容的提问来源于stack exchange,提问作者WITC
相关产品推荐
相关产品推荐

