ArduPilot STM32 引导加载程序(AP_Bootloader)完全指南:构建、硬件配置与 PX4 兼容刷写协议解析
2026/9/14 2:03:06 网站建设 项目流程

ArduPilot STM32 引导加载程序(AP_Bootloader)完全指南:构建、硬件配置与 PX4 兼容刷写协议解析

【免费下载链接】ardupilotArduPlane, ArduCopter, ArduRover, ArduSub source项目地址: https://gitcode.com/GitHub_Trending/ar/ardupilot

导读

AP_Bootloader 是 ArduPilot 官方为所有基于 STM32 的飞控板(ArduCopter、ArduPlane、ArduRover、ArduSub、AP_Periph 等固件的载体)提供的二级引导加载程序,负责在上电后校验并跳转主固件、以及在 USB/UART/CAN/以太网等通道上接收主机刷写指令。本文以仓库中的 Tools/AP_Bootloader/README.md 为骨架,结合wscriptbl_protocol.cppsupport.cpphwdef-bl.dat等源码级证据,带你掌握引导加载程序的完整构建流程、板卡级硬件配置文件语法、板卡 ID 分配机制,以及底层刷写协议的命令帧格式,使你能独立为一块自定义 STM32 飞控板编译并部署 Bootloader。

一、AP_Bootloader 是什么

AP_Bootloader 是 ArduPilot 用于STM32 系列飞控板的引导加载程序,源码位于仓库的 Tools/AP_Bootloader 目录。它的职责是:

  • 在飞控上电后,校验 Flash 中的应用固件完整性(CRC 与向量表),决定是直接跳转运行主固件,还是停留在 Bootloader 中等待刷写;
  • 接收上位机(如 Mission Planner、QGroundControl)通过USB 或 UART 串口下发的固件数据,将其写入 Flash,完成固件升级;
  • 从源码实现看,它还支持CAN 总线、以太网(Network)与 SD 卡三种额外的固件加载途径。

关于代码的体积约束,AP_Bootloader.cpp 的头部注释给出了一个关键设计决策:"It does not use the full AP_HAL API in order to keep the firmware size below the maximum of 16kByte required for F4 based boards. Instead it uses the ChibiOS APIs directly"——为了把体积控制在 F4 平台要求的 16KB 以内,Bootloader不依赖完整的 AP_HAL 抽象层,而是直接使用 ChibiOS 的ch.hhal.h等底层 API。这一点解释了为什么 Bootloader 需要单独构建、并拥有自己独立的硬件配置文件。

二、构建 Bootloader:waf 两条命令

README 给出了最核心的构建方法,只需要两条命令:

./waf configure --board BOARDNAME --bootloader ./waf bootloader

其中:

  • --board BOARDNAME指定目标飞控板的板卡名(例如Pixhawk1CubeOrange);
  • --bootloader告诉 waf:本次配置的目标是Bootloader 固件而不是主飞行固件;
  • ./waf bootloader执行实际的编译动作。

从 Tools/AP_Bootloader/wscript 的源码可以看到,waf 构建系统只有在bld.env.BOOTLOADER标志被设置时才会真正构建该目录,并且会把AP_MathAP_CheckFirmwareAP_NetworkingAP_ROMFSAP_Common等精简后的库链入,同时编译 DroneCAN 的libcanard(CAN 刷写协议依赖),最终通过bld.ap_program(..., program_groups='bootloader')产出可执行文件。

构建产物位置

编译完成后,产物位于build/BOARDNAME/bin目录:

产物路径用途
.binbuild/BOARDNAME/bin/二进制镜像,配合 DFU 工具上传
.hexbuild/BOARDNAME/bin/Intel HEX 格式镜像,仅在主机安装了 intelhex Python 模块时生成,同样通过 DFU 上传
.elfbuild/BOARDNAME/AP_Bootloader带符号的可执行文件,用于配合gdb调试 Bootloader 本身

README 特别强调:bin 和 hex 通常都用 DFU(Device Firmware Upgrade)方式上传。对大多数飞控板而言,进入 DFU 模式后,主机端使用dfu-util即可刷写:

dfu-util -a 0 -d 0483:df11 -s 0x08000000:leave -D build/BOARDNAME/bin/AP_Bootloader.bin

0483:df11是 STM32 系统 Bootloader 的常见 USB VID:PID,实际以板卡为准;-s ...:leave表示刷写完成后跳转运行。)

三、--bootloader背后的硬件配置机制:hwdef-bl.dat

--bootloader选项的关键作用,是让 waf 从hwdef-bl.dat文件读取硬件配置。README 明确指出其查找路径为:

libraries/AP_HAL_CHibiOS/hwdef/BOARDNAME/hwdef-bl.dat

(注意仓库中实际目录名为libraries/AP_HAL_ChibiOS/hwdef/BOARDNAME/hwdef-bl.dat。)例如 Pixhawk1 的 Bootloader 配置直接包含 FMUv3 的配置:

# libraries/AP_HAL_ChibiOS/hwdef/Pixhawk1/hwdef-bl.dat include ../fmuv3/hwdef-bl.dat

让我们以 fmuv3/hwdef-bl.dat 为例,逐行解读 Bootloader 硬件配置的关键语法:

# MCU class and specific type MCU STM32F4xx STM32F427xx # board ID. See Tools/AP_Bootloader/board_types.txt APJ_BOARD_ID TARGET_HW_CUBE_F4 # crystal frequency OSCILLATOR_HZ 24000000 # ChibiOS system timer STM32_ST_USE_TIMER 5 # flash size FLASH_SIZE_KB 2048 # location of application code FLASH_BOOTLOADER_LOAD_KB 16 # bootloader loads at start of flash FLASH_RESERVE_START_KB 0 # baudrate to run bootloader at on uarts define BOOTLOADER_BAUDRATE 115200 # uarts and USB to run bootloader protocol on SERIAL_ORDER OTG1 USART2

各配置项的含义与底层影响如下:

  • MCU STM32F4xx STM32F427xx:声明 MCU 系列与具体型号,决定链接时选择哪个芯片启动文件与 Flash 驱动;
  • APJ_BOARD_ID TARGET_HW_CUBE_F4:板卡 ID,必须与 board_types.txt 中的宏定义一致。它最终会编译进board_info.board_type(见 AP_Bootloader.cpp),在刷写握手阶段上报给上位机,用于校验固件与板卡是否匹配
  • FLASH_BOOTLOADER_LOAD_KB 16:Bootloader 占据 Flash 起始的 16KB,应用固件从 16KB 之后开始存放。这与第一节提到的"16KB 体积上限"直接对应;
  • define BOOTLOADER_BAUDRATE 115200:串口刷写的默认波特率,见 support.cpp 中的#define BOOTLOADER_BAUDRATE 115200,可通过PROTO_SET_BAUD命令在线切换;
  • SERIAL_ORDER OTG1 USART2Bootloader 监听哪些设备通道。README 明确说明:"The bootloader can load from USB or UARTs. The list of devices to load from is given in the SERIAL_ORDER option in hwdef-bl.dat"。这里OTG1指 USB OTG1 接口,USART2是飞控的遥测串口 TELEM1。它会被展开为BOOTLOADER_DEV_LIST宏(见 support.cpp),驱动 Bootloader 在所有列出的通道上轮询命令字节,并在收到第一个有效命令后"锁定"到该通道(lock_bl_port())。

此外,hwdef-bl.dat 还可以声明:USB 的 VBUS 检测引脚(PA9 VBUS INPUT OPENDRAIN)、USB 数据引脚(PA11/PA12 OTG_FS_DM/DP)、SWD 调试引脚(PA13/PA14)、Bootloader 状态 LED(PE12 LED_BOOTLOADER OUTPUT)以及片选引脚(CS)等。这些配置与主固件的 hwdef.dat 共享同一套由chibios_hwdef.py处理的语法。

四、板卡 ID 与 PX4 协议兼容性

4.1 为什么兼容 PX4

README 指出:"The bootloader protocol is compatible with that used by the PX4 project for boards like the Pixhawk." 这意味着:凡是 Pixhawk 系列(FMUv1~v6 等)使用的上位机刷写工具链,都能直接用于 ArduPilot 的 Bootloader,不需要额外的驱动或专属工具。

这一兼容性来自代码层面的继承:bl_protocol.cpp 的注释明确写道,该协议移植自 PX4 项目的bl.c,由 Andrew Tridgell 移植到 ChibiOS。协议版本号BL_PROTOCOL_VERSION当前为5(见 bl_protocol.cpp)。

4.2 board_types.txt:板卡 ID 的注册中心

README 要求"为兼容性起见,我们在本目录维护一份板卡 ID 列表",即 board_types.txt。这份文件集中登记了所有使用该 Bootloader 协议的主板类型,格式为宏名 数值,例如:

TARGET_HW_PX4_FMU_V2 9 TARGET_HW_PX4_FMU_V4 11 AP_HW_CUBEORANGE 140 AP_HW_KAKUTEF7 123

README 强调:"the board IDs in that file match the APJ_BOARD_ID in the hwdef.dat and hwdef-bl.dat files"——即 hwdef 文件里的APJ_BOARD_ID必须引用该文件中的宏。我们在上一节看到的APJ_BOARD_ID TARGET_HW_CUBE_F4正是如此,其值为9

4.3 ID 分配规则

从 board_types.txt 的注释中可以提炼出三条硬性规则:

  1. 1000 ~ 19999区间保留给 ArduPilot Bootloader 专用,外部厂商不得私自占用,只能通过向该文件提交 PR 分配;
  2. 非 OpenDroneID(ODID)板卡的 ID 不得超过10000,ODID 板卡使用10000 + 基础板卡 ID的编码方式(如AP_HW_CubeOrange_ODID 10140);
  3. 厂商若需预留一批 ID,每次申请上限为 10 个,且应优先填补已分配 ID 之间的空隙,而不是在文件末尾追加("please fill gaps rather than adding past ID #7109")。

五、底层刷写协议详解:命令帧与状态机

理解协议的最好方式,是直接阅读 bl_protocol.cpp 顶部的协议规范注释。

5.1 帧格式

命令帧: <opcode>[<command_data>]<EOC> 应答帧: [<reply_data>]<INSYNC><status>
  • <opcode><status>取值来自协议宏定义;
  • EOC(End Of Command)标志命令结束;
  • INSYNC是应答前导字节,status指示命令执行结果。

关键协议字节(bl_protocol.cpp):

方向名称含义
应答前导PROTO_INSYNC0x12应答的 "in sync" 字节
命令结束PROTO_EOC0x20命令结束标志
应答PROTO_OK0x10命令成功
应答PROTO_FAILED0x11命令失败
应答PROTO_INVALID0x13非法命令
命令PROTO_GET_SYNC0x21重新建立同步
命令PROTO_GET_DEVICE0x22读取设备 ID 信息
命令PROTO_CHIP_ERASE0x23擦除程序区并复位写地址
命令PROTO_PROG_MULTI0x27写入字节并递增地址
命令PROTO_READ_MULTI0x28读取字节并递增地址
命令PROTO_GET_CRC0x29计算并返回整个可写区的 CRC
命令PROTO_GET_OTP0x2a读取 OTP 指定地址
命令PROTO_GET_SN0x2b从 UDID 区读取序列号
命令PROTO_GET_CHIP0x2c读取芯片版本(MCU IDCODE)
命令PROTO_SET_DELAY0x2d设置最小启动延时
命令PROTO_GET_CHIP_DES0x2e读取 ASCII 芯片描述
命令PROTO_GET_VERSION0x2f读取 Bootloader 版本
命令PROTO_BOOT0x30启动应用
命令PROTO_SET_BAUD0x33切换串口波特率
命令PROTO_EXTF_ERASE0x34擦除外置 Flash 扇区
命令PROTO_EXTF_PROG_MULTI0x35写入外置 Flash
命令PROTO_EXTF_GET_CRC0x37计算外置 Flash 的 CRC
命令PROTO_CHIP_FULL_ERASE0x40强制全擦(跳过磨损优化)

GET_DEVICE命令通过参数区分要读取的信息(bl_protocol.cpp):

  • PROTO_DEVICE_BL_REV(1):Bootloader 协议版本;
  • PROTO_DEVICE_BOARD_ID(2):板卡 ID(即board_info.board_type);
  • PROTO_DEVICE_BOARD_REV(3):板卡硬件版本;
  • PROTO_DEVICE_FW_SIZE(4):可刷写区大小;
  • PROTO_DEVICE_VEC_AREA(5):向量表 7~10 的内容;
  • PROTO_DEVICE_EXTF_SIZE(6):外置 Flash 可用大小。

5.2 标准刷写工作流

bl_protocol.cpp 定义了协议版本 3 的标准流程,这也是所有兼容上位机的刷写步骤:

GET_SYNC 验证板卡在线(重新建立同步) GET_DEVICE 读取板卡类型(决定上传哪个固件) CHIP_ERASE 擦除程序区并复位地址计数器 循环: PROG_MULTI 分块写入固件字节(单次最大 64 字节,见 PROTO_PROG_MULTI_MAX) GET_CRC 校验整个可刷写区的 CRC(crc32) BOOT/RESET 收尾写入、复位芯片并启动应用

值得注意的工程细节:

  • PROG_MULTI要求字节数为 4 的整数倍,且写入地址不能越过board_info.fw_size边界(bl_protocol.cpp);
  • 前导 32 字节(RESERVE_LEAD_WORDS 8个 32 位字)会被暂存而不会立即写入 Flash,直到收到BOOT命令确认整个上传成功后,才在 PROTO_BOOT 分支中补写。这样可避免"写到一半断电导致半成品固件被误启动";
  • 防误擦除保护CHIP_ERASE只有在done_syncGET_DEVICE信息齐备后才执行,防止串口噪声误触发擦除("lower chance of random data on a uart triggering erase");
  • 超时回退机制:收到无效命令时,只要尚未开始擦除(!done_erase),超时时间会恢复为原始值,避免卡死在 Bootloader(bl_protocol.cpp)。

5.3 跳转应用:jump_to_app 的校验逻辑

bl_protocol.cpp 的 jump_to_app() 展示了 Bootloader 跳转前的三道安全闸门:

  1. 若启用了AP_CHECK_FIRMWARE_ENABLED,先调用check_good_firmware()做固件 CRC 校验,失败则点亮LED_BAD_FW并停留在 Bootloader;
  2. 检查应用起始地址处前 8 个字(RESERVE_LEAD_WORDS)是否为0xffffffff(未写满即视为上传未完成),并验证向量表第二个字(入口地址)落在合法的 Flash 区间内;
  3. 对 CAN 启动的固件,跳转前先初始化并喂狗(stm32_watchdog_init()/stm32_watchdog_pat()),一旦应用在 30 秒内未改写 RTC 标志导致看门狗复位,Bootloader 会停留在自身,等待用户刷入修复固件(AP_Bootloader.cpp)。

跳转前还会按 MCU 系列关闭外设时钟与缓存(F7/H7 需SCB_DisableDCache/ICache),并把SCB_VTOR切换到应用向量表,最后以do_jump()汇编代码装载栈指针与入口地址完成交接。

六、固件加载通道:不止 USB 与 UART

虽然 README 重点描述的是 USB/UART 两条通道,但从源码结构看,Bootloader 实际上支持四类加载途径:

通道源码说明
USB / UARTsupport.cpp +BOOTLOADER_DEV_LIST默认通道,由SERIAL_ORDER决定监听列表
CANcan.cpp / can.h基于 DroneCAN/libcanard,通过 CAN 总线刷写(配合RTC_BOOT_CANBL快速进入、can_set_node_id设置节点)
以太网network.cpp / network.h通过网络刷写与状态上报,受 AP_Bootloader_config.h 中AP_BOOTLOADER_NETWORK_ENABLED控制
SD 卡flash_from_sd.cpp / flash_from_sd.h从 SD 卡读取固件镜像刷写,由AP_BOOTLOADER_FLASH_FROM_SD_ENABLED开关控制(默认关闭)

从 AP_Bootloader.cpp 的 main() 可以看到完整的启动决策链:

flash_init() 初始化 Flash 页表布局 check_ecc_errors()(H7 启用 ECC 时) 检查 ECC 错误 快速启动判定(AP_FASTBOOT_ENABLED) 看门狗复位 / RTC_BOOT_FAST / CAN 更新请求 check_good_firmware() 固件完好性检查(可选) 可选“stay in bootloader”引脚 强制停留 ext_flash.init() 外置 Flash 初始化(若有) try_boot ? jump_to_app() 直接启动应用 init_uarts() / can_start() / network.init() flash_from_sd() SD 卡刷写 bootloader(timeout) 进入协议监听循环(USB/UART) 超时后 jump_to_app() 无命令则启动应用

这段流程清晰地体现了"固件好就秒启动、固件坏就停留等待刷写"的容错设计:超时时间默认HAL_BOOTLOADER_TIMEOUT(5000ms,见 AP_Bootloader.cpp),一旦在超时窗口内收到有效命令,lock_bl_port()会锁定端口并清零超时,保证刷写过程中不会中途跳走。

七、Bootloader 目录源码地图

以下文件均位于 Tools/AP_Bootloader:

文件职责
AP_Bootloader.cpp入口main(),启动决策、看门狗、快速启动逻辑
bl_protocol.cpp刷写协议状态机(全部命令处理)
bl_protocol.h协议声明、LED 状态定义
support.cppUART/USB 读写、Flash 读写擦除封装、board_info
support.hstruct boardinfo定义与 Flash 函数声明
can.cppCAN 通道刷写支持
network.cpp以太网通道刷写与状态输出
flash_from_sd.cppSD 卡固件刷写
AP_Bootloader_config.h编译期开关(SD 刷写、网络刷写)
mcu_f1/f3/f4/f7/h7/g4/l4.h各 MCU 系列的 ID/修订版描述表
board_types.txt板卡 ID 注册表
wscriptwaf 构建脚本

其中boardinfo结构体(support.h)是上位机识别板卡的核心数据,在 AP_Bootloader.cpp 中由编译期宏初始化:

struct boardinfo board_info = { .board_type = APJ_BOARD_ID, // 来自 hwdef-bl.dat .board_rev = 0, .fw_size = (BOARD_FLASH_SIZE - (FLASH_BOOTLOADER_LOAD_KB + FLASH_RESERVE_END_KB + APP_START_OFFSET_KB))*1024, .extf_size = (EXT_FLASH_SIZE_MB * 1024 * 1024) - (EXT_FLASH_RESERVE_START_KB + EXT_FLASH_RESERVE_END_KB) * 1024 };

八、实践总结:为自定义板卡启用 Bootloader 的步骤

综合 README 与源码,为一个新的 STM32 飞控板启用 Bootloader 的完整链路如下:

  1. 编写硬件配置:在libraries/AP_HAL_ChibiOS/hwdef/<BOARDNAME>/下创建hwdef.dat(主固件)与hwdef-bl.dat(Bootloader),后者至少声明MCUAPJ_BOARD_IDFLASH_SIZE_KBFLASH_BOOTLOADER_LOAD_KBSERIAL_ORDER

  2. 注册板卡 ID:在 Tools/AP_Bootloader/board_types.txt 中为板卡申请一个唯一 ID(遵循 1000~19999 区间规则),并让hwdef-bl.dat中的APJ_BOARD_ID引用该宏;

  3. 构建 Bootloader

    ./waf configure --board <BOARDNAME> --bootloader ./waf bootloader
  4. 获取产物build/<BOARDNAME>/bin/下的.bin/.hex通过 DFU 上传(安装 intelhex 后自动生成.hex),build/<BOARDNAME>/AP_Bootloader.elf用于 gdb 调试;

  5. 验证通道:确认SERIAL_ORDER覆盖了目标 USB 口与调试串口,上电后 Bootloader 会按第四节所述流程等待主机命令或直接启动应用。

这套 Bootloader 之所以能在 Pixhawk 生态中"即插即用",正是因为它严格遵循了 PX4 兼容的协议版本 5 与统一的板卡 ID 注册体系——理解这两点,就掌握了整个 ArduPilot 固件刷写链路的地基。

【免费下载链接】ardupilotArduPlane, ArduCopter, ArduRover, ArduSub source项目地址: https://gitcode.com/GitHub_Trending/ar/ardupilot

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询