OpenBLT 嵌入式 BootLoader 技术解析

基于 STM32F407 + Modbus RTU(RS485)的实战经验总结


一、什么是 OpenBLT

OpenBLT(Open BootLoader Technology)是荷兰 Feaser 公司开源的嵌入式 BootLoader 框架(GPL v3),提供了一套完整的固件远程升级方案:

  • MCU 端:一个极简的 BootLoader 固件,常驻 Flash 头部
  • PC 端:一个开箱即用的 GUI 上位机(MicroBoot)

它的最大价值在于:即使你完全没写过 BootLoader,也能在几小时内给 STM32 加上远程固件升级功能


二、整体架构

┌─────────────────────────┐        RS485/Modbus RTU       ┌──────────────────────────┐
│      上位机 (Windows)     │ ──────────────────────────→  │    BCU 控制器 (STM32F407)  │
│                          │ ←─────────────────────────── │                           │
│  ┌───────────────────┐   │                              │  ┌────────────────────┐   │
│  │   MicroBoot.exe   │   │  功能码 109(自定义)          │  │   BootLoader 固件   │   │
│  │   (Pascal GUI)    │   │  [地址][109][len][XCP][CRC]  │  │   位于 Flash 头部    │   │
│  └────────┬──────────┘   │                              │  └────────────────────┘   │
│           ↓              │                              │           ↓               │
│  ┌───────────────────┐   │                              │  跳到用户 APP(如果有效)   │
│  │  LibOpenBLT.dll   │   │                              │                           │
│  │    (C 语言库)      │   │                              │                           │
│  └────────┬──────────┘   │                              │                           │
│           ↓              │                              │                           │
│  ┌───────────────────┐   │                              │                           │
│  │  XCP 协议层        │   │                              │                           │
│  │  (会话/编程/擦除)   │   │                              │                           │
│  └────────┬──────────┘   │                              │                           │
│           ↓              │                              │                           │
│  ┌───────────────────┐   │                              │                           │
│  │ Modbus RTU 传输层  │   │                              │                           │
│  │  (xoxtpmbrtu.c)    │   │                              │                           │
│  └───────────────────┘   │                              │                           │
└─────────────────────────┘                              └──────────────────────────┘

三、MCU 端:BootLoader 固件

3.1 Flash 布局

MCU 内部 Flash(STM32F407VE, 512KB)
┌───────────────────────────┐ 0x08000000  低地址
│   BootLoader               │
│   32KB(sector 0~1)        │
├───────────────────────────┤ 0x08008000
│   用户 APP 固件             │
│   480KB(sector 2~11)      │
│                            │
│   ...                      │
└───────────────────────────┘ 0x08080000  高地址

BootLoader 通过链接脚本限定为 32KB:FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 32K。同时 flash.c 中的 flashLayout[] 表将 sector 0~1 注释掉(保留给 BootLoader),APP 可用扇区从 0x08008000 的 sector 2 开始。OpenBLT 的擦写操作只针对 flashLayout[] 中列出的扇区,绝对不会误擦 BootLoader 自身。

3.2 上电启动流程

上电/复位
  ↓
BootLoader 初始化(HAL、时钟、串口)
  ↓
检查 APP 是否有校验和?
  ├─ 有 → 校验和验证通过?
  │   ├─ 通过 → 跳转到 APP ✓ 正常启动
  │   └─ 失败 → 留在 BootLoader 等升级
  └─ 无 → 留在 BootLoader 等升级
  
留在 BootLoader 后的主循环:
  while(1) {
      收 Modbus 帧 → 解析 XCP 包 → 执行命令(擦除/编程/跳转)
      CopService();  // 喂狗
  }

3.3 文件结构

BootLoader 工程/
├── App/
│   ├── blt_conf.h    ← ★ 核心配置(晶振、Flash大小、通信接口、超时参数)
│   ├── hooks.c       ← 回调钩子(LED、看门狗、后门入口)
│   └── app.c/h       ← BootLoader 启动入口
│
├── Core/             ← STM32CubeIDE 生成的 HAL 层(main.c、时钟、外设初始化)
├── Drivers/          ← CMSIS + HAL 库
│
├── Loader/           ← OpenBLT 源码
│   ├── *.c/h         ← 核心:boot.c、com.c、xcp.c、backdoor.c...
│   └── ARMCM4_STM32F4/
│       ├── cpu.c     ← MCU 初始化(中断向量重映射)
│       ├── flash.c   ← 内部 Flash 驱动(擦除/编程)
│       ├── nvm.c     ← 非易失存储抽象层
│       ├── timer.c   ← 定时器(1ms 系统时钟 + 100kHz 自由计数器)
│       ├── rs232.c   ← RS232 传输层
│       ├── can.c     ← CAN 传输层
│       └── mbrtu.c   ← ★ Modbus RTU 传输层(自研添加)

3.4 核心配置:blt_conf.h

// ===== 时钟 =====
#define BOOT_CPU_XTAL_SPEED_KHZ          25000    // 外部晶振 25MHz
#define BOOT_CPU_SYSTEM_SPEED_KHZ        160000   // 系统频率 160MHz

// ===== 通信接口选择 =====
#define BOOT_COM_MBRTU_ENABLE            1        // Modbus RTU
#define BOOT_COM_MBRTU_BAUDRATE          9600     // 9600bps
#define BOOT_COM_MBRTU_CHANNEL_INDEX     2        // USART3
#define BOOT_COM_MBRTU_PARITY            2        // 偶校验
#define BOOT_COM_MBRTU_NODE_ID           1        // Modbus 从站地址

// ===== Flash =====
#define BOOT_NVM_SIZE_KB                 512      // Flash 大小
#define BOOT_FLASH_VECTOR_TABLE_CS_OFFSET 0x1AC   // 校验和位置(F4 系列)

// ===== 超时参数 =====
#define BOOT_COM_MBRTU_RX_MAX_DATA       129      // 收包最大数据量
#define BOOT_COM_MBRTU_TX_MAX_DATA       129      // 发包最大数据量

// ===== 功能钩子 =====
#define BOOT_COP_HOOKS_ENABLE            1        // 看门狗
#define BOOT_BACKDOOR_HOOKS_ENABLE       1        // 后门入口
#define BOOT_CPU_USER_PROGRAM_START_HOOK 1        // 跳转前回调

四、通信协议:XCP over Modbus RTU

这是整个系统最精妙的设计——把 XCP 标定协议嵌入 Modbus RTU 帧

4.1 为什么选 Modbus RTU?

对比 CAN RS232 Modbus RTU
需要额外硬件 CAN 收发器 直连 RS485 收发器
总线拓扑 多节点 点对点 多节点(一主多从)
BMS 行业通用性 一般 高(储能/工业标配)
抗干扰 强(差分信号)

BMS 现场本来就是 RS485 + Modbus 的总线,BootLoader 共用同一根线,不增加硬件成本。

4.2 帧格式

┌──────────┬──────────┬──────────┬───────────────┬──────────┐
│ 从站地址  │ 功能码    │ XCP长度   │  XCP 数据      │ CRC-16   │
│ 1 byte   │ 1 byte   │ 1 byte   │  N bytes      │ 2 bytes  │
├──────────┼──────────┼──────────┼───────────────┼──────────┤
│ 0x01     │  109     │  0x14    │  XCP 包原始字节 │  计算值   │
└──────────┴──────────┴──────────┴───────────────┴──────────┘
   ←──── Modbus RTU 帧头 ──→  ←── XCP 载荷 ──→  ← Modbus 尾 →

功能码 109:用户自定义功能码(65~72 或 100~110 范围内),
            表示"本帧数据是一个 XCP 协议包"

4.3 发送过程(MCU 端 mbrtu.c)

void MbRtuTransmitPacket(blt_int8u *data, blt_int8u len) {
    blt_int8u txPacket[BOOT_COM_MBRTU_TX_MAX_DATA + 5];

    // 1. 拼 Modbus 帧头
    txPacket[0] = BOOT_COM_MBRTU_NODE_ID;    // 从站地址 = 0x01
    txPacket[1] = 109;                        // 功能码 = XCP 包
    txPacket[2] = len;                        // XCP 数据长度

    // 2. 原样拷贝 XCP 数据
    memcpy(&txPacket[3], data, len);

    // 3. 计算 CRC-16(Modbus 算法)
    checksum = MbRtuCrcCalculate(txPacket, len + 3);
    txPacket[len+3] = checksum & 0xFF;
    txPacket[len+4] = checksum >> 8;

    // 4. RS485 DE 拉高 → 发送 → DE 拉低
    MbRtuDriverOutputControlHook(BLT_TRUE);
    for (i = 0; i < len + 5; i++)
        MbRtuTransmitByte(txPacket[i], ...);
    MbRtuDriverOutputControlHook(BLT_FALSE);
}

4.4 接收过程

收到一帧 Modbus RTU 数据后,按顺序校验:

  1. T3_5 帧间隔 → 收到完整帧
  2. CRC-16 校验 → 数据正确
  3. rxPacket[0] == 我的地址?→ 不是就丢弃
  4. rxPacket[1] == 109?    → 不是就丢弃
  5. rxPacket[2] == 实际长度?→ 不匹配就丢弃
  6. 全部通过 → 提取 rxPacket[3] 开始的 N 字节 = XCP 包

4.5 XCP 协议在做什么?

XCP(Universal Measurement and Calibration Protocol)是 ASAM 标准的标定协议,这里只用它的内存操作功能:

XCP 命令 作用
CONNECT 建立连接(握手)
SET_MTA 设置目标内存地址
PROGRAM 写入一段数据到内存
PROGRAM_RESET 写入后复位 MCU,跳转到新 APP
GET_SEED / UNLOCK Seed/Key 安全解锁(可选)

五、上位机:MicroBoot

5.1 技术栈

组件 语言 说明
MicroBoot.exe Pascal(Lazarus/Free Pascal) GUI 主程序
LibOpenBLT.dll C 核心库(XCP 协议 + 传输层 + S-Record 解析)
libseednkey.dll C Seed/Key 安全算法(可选)

5.2 固件升级流程

MicroBoot 工作线程的状态机:

  IDLE
   ↓
  INITIALIZING     加载 S-Record 固件文件,解析段信息
   ↓
  CONNECTING       XCP CONNECT 命令,失败自动重试(后门模式)
   ↓
  CHECK_INFO_TABLE 校验固件兼容性(可选,防刷错型号)
   ↓
  ERASING_MEMORY   擦除 Flash(按 32KB 分段,占进度 20%)
   ↓
  PROGRAMMING      写入 Flash(按 256 字节分段,占进度 80%)
   ↓
  FINISHING_UP     断开连接,MCU 自动跳转到新 APP
   ↓
  IDLE

5.3 上位机从哪来?

不需要自己开发。OpenBLT 自带 MicroBoot 的完整源码,按以下步骤就能用:

1. cd Host/Source/MicroBoot/
2. 用 Lazarus IDE 打开 MicroBoot.lpr
3. 编译 → 得到 MicroBoot.exe
4. 打开后选 "XCP on Modbus RTU" → 配 COM 口 → 选 .srec 文件 → 点 Start

六、总结

问题 OpenBLT 的答案
BootLoader 怎么写? 不需要从头写,改 blt_conf.h 几个宏就行
上位机怎么写? 自带 MicroBoot,编译即用
走什么通信? RS232 / CAN / USB / TCP / Modbus RTU 可配
安全性? Seed/Key 握手 + 校验和完整性检查(可选)
APP 崩溃了怎么办? 后门入口——上电 1.5 秒内强制进 BootLoader
固件格式? 标准 Motorola S-Record(.srec)
移植到新 MCU? 换平台驱动 + 改 blt_conf.h,核心代码不变

OpenBLT 的核心价值不是"提供了多牛的算法",而是把 BootLoader 开发中所有踩坑的地方都封装好了——你只需要关心板子的硬件配置,剩下的协议、校验、超时、重试、安全、上位机,全都有现成的。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐