Skip to content

LV025-MCU有BankSwap落地方案 ​

一、 硬件 Bank Swap 的原理 ​

1. 什么是 Bank Swap ​

上一篇(LV022-MCU无BankSwap落地方案)的核心难题是"链接地址绑定":镜像编译时链接到某个固定地址,放到另一个槽就跑不起来。硬件 Bank Swap 正是为解决这个问题而设计的机制:

Flash 在物理上分成两个 Bank,芯片内部有一套地址映射电路,可以把两个 Bank 与"低位地址 / 高位地址"的对应关系整体对调——而且是硬件完成的对调,软件看到的地址永远不变。

2. 地址映射交换 ​

Bank Swap 的地址映射交换

  • 正常模式:Bank 1 占据 0x08000000(运行地址),Bank 2 占据高位地址供写入
  • 交换模式:置位交换请求并复位后,硬件把 Bank 2 换到 0x08000000,Bank 1 退到高位地址

关键在于:应用固件永远链接并运行在 0x08000000,它完全不感知自己实际存在于哪个物理 Bank。中断向量表、常量池、跳转地址全部有效,因此只需要编译一份镜像。

3. 与无 Bank Swap 方案的本质差异 ​

维度无 Bank Swap(F103)有 Bank Swap(L4/H5/G4)
镜像份数两个地址变体一份
切换动作改 VTOR 指向不同槽位(地址不同)改硬件映射(地址不变)
回滚实现Bootloader 按标志选槽再次交换映射即可
Bootloader 复杂度需理解两套地址只需处理一个运行地址
升级包体积可能双倍(双变体)单份

二、 STM32L4 / L5 的实现要点 ​

1. 双 Bank 结构与 BFB2 ​

以 STM32L4 系列(如 STM32L471、STM32L476)为例:

  • Flash 分为 Bank 1 与 Bank 2,各占一半容量(如 1 MB 型号各 512 KB)
  • 选项字节(Option Bytes)中有一位 BFB2(Boot from Bank 2):
    • BFB2 = 0:从 0x08000000 开始执行(Bank 1 位于低位)
    • BFB2 = 1:硬件交换两个 Bank 的地址映射,从 Bank 2 开始执行
  • 页大小 2 KB(L4),Bank 内可按页擦写

升级流程因此变得非常简洁:

(1)运行在当前 Bank 的 App 下载新固件,写入另一个 Bank(正被执行的 Bank 无法自我擦写,天然隔离)

(2)校验新固件 CRC 与签名

(3)置位 BFB2(指向新固件所在 Bank),并置"待确认"标志与计数

(4)复位,芯片从新 Bank 启动,运行地址仍是 0x08000000

(5)新固件自检通过 → 清除待确认标志;失败或超限 → Bootloader 复位 BFB2 切回旧 Bank

2. 代码实现 ​

c
// bank_ota.c —— STM32L4 双 Bank 升级关键操作(HAL 实现)
#include "stm32l4xx_hal.h"
#include <stdbool.h>

#define BANK1_START   0x08000000u
#define BANK2_START   0x08040000u   /* 512 KB 型号:Bank 2 起始 */
#define FLASH_PAGE_SZ 2048u

/* 判断当前运行在哪个 Bank,返回目标写入 Bank 的起始地址 */
uint32_t ota_target_bank_addr(void)
{
    uint32_t pc = (uint32_t)ota_target_bank_addr;   /* 取当前代码地址 */
    return (pc < BANK2_START) ? BANK2_START : BANK1_START;
}

/* 写入新固件到目标 Bank */
int ota_write_firmware(const uint8_t *image, uint32_t len)
{
    uint32_t dst = ota_target_bank_addr();
    uint32_t bank = (dst == BANK2_START) ? FLASH_BANK_2 : FLASH_BANK_1;
    FLASH_EraseInitTypeDef erase = {0};
    uint32_t page_error = 0;

    HAL_FLASH_Unlock();

    erase.TypeErase   = FLASH_TYPEERASE_PAGES;
    erase.Banks       = bank;
    erase.Page        = 0;
    erase.NbPages     = (len + FLASH_PAGE_SZ - 1u) / FLASH_PAGE_SZ;
    if (HAL_FLASHEx_Erase(&erase, &page_error) != HAL_OK) {
        HAL_FLASH_Lock();
        return -1;
    }

    for (uint32_t i = 0; i < len; i += 8u) {
        uint64_t dword = 0xFFFFFFFFFFFFFFFFull;    /* 不足 8 字节补 0xFF */
        uint32_t chunk = (len - i >= 8u) ? 8u : (len - i);
        for (uint32_t k = 0; k < chunk; k++) {
            dword &= ~((uint64_t)0xFFu << (k * 8u));
            dword |=  ((uint64_t)image[i + k] << (k * 8u));
        }
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,
                              dst + i, dword) != HAL_OK) {
            HAL_FLASH_Lock();
            return -2;
        }
    }

    HAL_FLASH_Lock();
    return 0;
}

/* 切换启动 Bank:改写选项字节 BFB2,然后复位 */
int ota_switch_boot_bank(bool boot_from_bank2)
{
    FLASH_OBProgramInitTypeDef ob = {0};
    HAL_FLASH_Unlock();
    HAL_FLASH_OB_Unlock();

    HAL_FLASHEx_OBGetConfig(&ob);
    ob.OptionType = OPTIONBYTE_USER;
    ob.USERType   = OB_USER_BFB2;
    ob.USERConfig = boot_from_bank2 ? OB_BFB2_ENABLE : OB_BFB2_DISABLE;

    if (HAL_FLASHEx_OBProgram(&ob) != HAL_OK) {
        HAL_FLASH_OB_Lock();
        HAL_FLASH_Lock();
        return -1;
    }
    HAL_FLASH_OB_Launch();             /* 触发选项字节重载(内部会复位) */
    return 0;                          /* 不会执行到这里 */
}

【注意】

HAL_FLASH_OB_Launch() 会触发系统复位使新选项字节生效。在双 Bank 结构下,切换 Bank 本质上就是"改配置 + 复位",比无 Bank Swap 方案的"改判断逻辑 + 跳转"更接近硬件原生行为,出错概率更低。

3. 回滚实现 ​

回滚只需把 BFB2 改回去。工程上通常这样组织:

  • Bootloader 位于两个 Bank 的起始页(双 Bank 芯片支持把前几页设为"安全区/共享区",或分别在两个 Bank 各放一份 Bootloader)
  • 参数区记录 active_bank 与 pending_bank、试用计数,逻辑与 LV016-选槽与回滚机制 完全一致,只是"选槽"变成"选 Bank 并置 BFB2"

三、 STM32 其它系列的差异 ​

系列机制说明
STM32L4 / L4+双 Bank + BFB2 选项位最典型的 Bank Swap 实现
STM32L5 / U5双 Bank + SWAP_BANK 位 + TrustZone支持安全区/非安全区,可与安全启动组合
STM32H5双 Bank + SWAP_BANK,产品状态生命周期(PSA)引入 CLOSED / LOCKED 等状态,量产级安全
STM32G4部分型号支持双 Bank,DBANK 选项位需按具体型号手册确认
STM32G0B1 / G0C1双 Bank + SWAP_BANK 选项位G0 系列中支持双 Bank 的型号,性价比之选
STM32H7双 Bank(部分型号)+ SWAP_BANK大容量型号支持,注意 AXI/ITCM 别名地址
STM32F1 / F4(多数型号)不支持走 LV022-MCU无BankSwap落地方案

选型时的核查清单:芯片手册里是否有 SWAP_BANK / BFB2 选项位?双 Bank 是否支持独立擦写(RWW)?两个 Bank 是否存在容量不对称?这些决定了方案能否落地。

四、 特例:i.MX RT 片内无 Flash ​

i.MX RT(如 RT1052 / RT1060)是跨界 MCU,片内没有 Flash,固件存放在外部 QSPI NOR 或 eMMC 中,运行时拷贝到 SRAM / SDRAM:

text
外部 NOR Flash ──(BootROM + 二级 Bootloader)──▶ 内部 SRAM / 外部 SDRAM 运行

它天然具备"存储地址与运行地址解耦"的特性,因此:

  • 镜像只链接到 RAM 运行地址,放哪个存储槽都能跑
  • A/B 就是外部 Flash 里的两个槽,切换只需让 Bootloader 从另一个槽拷贝
  • 升级包不需要双变体,回滚只需切回原槽

代价是每次上电都要搬运一次(几十毫秒到几百毫秒),以及 RAM/SDRAM 的容量约束。

五、 厂商与开源现成方案 ​

1. ST X-CUBE-SBSFU ​

ST 官方的安全启动与安全固件更新套件(Secure Boot & Secure Firmware Update),支持:

  • 双槽(Dual Slot)或单槽 + 外部存储
  • 固件签名验证(ECDSA)
  • 防回滚(版本计数)
  • YMODEM 串口升级通道

它把 Bootloader、验签、回滚状态机全部实现好了,可直接作为工程起点,省掉自研 Bootloader 的坑。

2. 跨厂商开源 Bootloader ​

MCUboot 与 wolfBoot 不绑定单一厂商,在有 Bank Swap 的芯片上可直接用硬件换位实现双槽,比本篇的选项位操作更省事;它们在没有 Bank Swap 的芯片上如何模拟 Swap 语义、各自的代价是什么,见 LV022-MCU无BankSwap落地方案 第四节。

3. NXP SBL(Secure Bootloader) ​

面向 i.MX RT / LPC 系列,支持:

  • 从外部 Flash 启动的 SB 文件格式(含签名与元数据的镜像容器)
  • 通过 UART/USB 进行 In-Application 升级
  • 与 MCUXpresso 工具链集成

六、 对比与选型建议 ​

维度无 Bank Swap(F103 方案)有 Bank Swap(L4/L5 等)片内无 Flash(i.MX RT)
镜像份数两个地址变体一份一份
切换方式VTOR + 跳转选项位 + 复位Bootloader 换槽拷贝
回滚成本改参数区标志改选项位换槽拷贝
自研工作量高(Bootloader 全新写)低(逻辑已简化)中
附加限制Flash 空间紧张选项字节有擦写次数上限需外部存储与 RAM

选型建议:

(1)新项目优先选带 Bank Swap 的型号:STM32L4/L5/U5/H5、G4、H7 的大部分型号都支持,能省掉大量 Bootloader 复杂度

(2)选项字节擦写次数有限(通常约 1000 次):不要在每次升级都改 BFB2,而应让"当前运行 Bank"与"待升级 Bank"交替,把写选项字节的次数降到最低;更稳的做法是优先用参数区记录目标 Bank,仅在确实需要换位时写选项位

(3)老项目(F103/F407 等):按 LV022-MCU无BankSwap落地方案 的方案一到方案四选型

(4)优先复用官方套件:SBSFU、MCUboot、wolfBoot 已经把验签、回滚、断电安全做透,自研的目标应是"理解机制",而非"重新实现一遍"


本文档由 markdowncli 技能辅助生成