资讯动态

飞控板卡移植指南:hwdef.dat与BootLoader详解

发布时间:2026/9/28 16:50:01 来源:尧图企业网站定制
先声明一下这篇文章不是什么官方的移植教程而是我自己在把Ardupilot往一块非主流飞控板上搬的时候踩着一路坑填出来的记录。如果你正准备把Ardupilot从一个现成板卡挪到自己的硬件上或者想搞清楚hwdef.dat到底在整件事里扮演什么角色那这篇文章应该能帮你省下好几个通宵。我一直觉得Ardupilot的硬件移植这件事难度不在于代码本身而在于“你根本不知道该在哪里找错”。报错信息跳出来的时候你甚至连“这个错是编译期的还是链接期的”都要反应半天。而且网上能找到的资料要么是官方文档那种“点到为止”的抽象描述要么是某个论坛里几条零散的回复真要按图索骥去操作能把自己绕晕。所以我更愿意把这次移植的全过程拆开揉碎把我遇到的每一个报错、每一次反复、每一个“原来是这么回事”的瞬间都记录下来。硬件移植这件事说白了就是两个大头一个是怎么让Ardupilot认识你的硬件这靠hwdef.dat和周边的板级配置文件另一个是怎么让这块硬件能跑起来引导程序这就是BootLoader的活了。这两者之间的协作关系很微妙不少人以为只要搞定其中一头就完事了结果经常是花了一整天时间把BootLoader刷进去然后发现编译固件的时候又卡在莫名其妙的外设定义错误上。1. 内容整体设计与思路拆解先说清楚一件事Ardupilot移植并不是“改改引脚就能跑”的事。官方的很多板卡像Pixhawk系列、Cube系列它们的开发板配置文件都是维护得非常好的你可以直接基于它们生成一个属于自己的板卡支持包。但如果你拿到的是一块完全陌生的、没有现成参考的硬件那这个工作量就会从“改配置”直接跳级到“设计板级支持包”两者是完全不同的游戏。我这次移植的目标板卡主控是STM32H743片上资源不算差但问题在于它没有一个对应的官方Ardupilot目标名称。这就意味着所有的一切都得从零开始包括时钟树配置、外设映射、Flash布局、BootLoader和固件程序的地址划分等等。整个流程我当时是这么规划的先做板级硬件分析理清MCU型号、Flash和RAM大小、外部晶振频率、各种外设接口UART、SPI、I2C、CAN、PWM输出这些分别挂在哪些引脚上。再搭编译环境这一步其实花不了太多时间就是个交叉编译链和Python环境的问题。接着改hwdef.dat这是最核心的一步。板卡的灵魂都在这一个文件里它决定了固件编译出来是什么样。然后调BootLoader这里有两种选择要么用Ardupilot官方的bootloader源码改一改MCU目标和地址映射重新编译要么就根据情况自己做一定的适配。最后烧录联调把BootLoader烧进去再把固件通过USB或者调试器刷进去上电看现象有问题就回来改配置文件循环往复。这套流程看上去像是一条直线但实际操作起来基本是“编译-烧录-看日志-又回去改配置”的无限循环。做好这个心理准备比什么都重要。而在这其中hwdef.dat和BootLoader的协作关系才是真正容易让人犯迷糊的地方。很多人以为BootLoader就只是“上电先跑一段程序然后把主固件捞起来放到内存里执行”这么简单。对也不对。对于一个真正要上天的飞控板来说BootLoader还需要负责让USB进入DFU模式、校验固件CRC、管理固件版本和签名等等。一旦hwdef.dat里定义的固件起始地址和BootLoader里面烧录地址对不上表现就是BootLoader刷成功固件也刷进去“成功”了但一上电就是死活不跑。2. 核心细节解析与实操要点2.1 硬件分析与引脚规划——端口映射不是随便拍脑袋的事这一步看着简单却是整个移植过程中最不能出错的地方。我强烈建议你第一步先做一张引脚功能对照表。别嫌麻烦这是你后续所有工作的基石。你可以自己买几根杜邦线然后用万用表一个一个测或者直接看原理图把每个引脚对应的功能标出来。这块板子的原理图不全很多引脚复用的说明都没写清楚当时我光是确定一组UART的TX/RX脚就折腾了两个小时。引脚规划有几个关键点得注意尽量避免引脚冲突尤其注意UART和SPI复用的引脚很多MCU的引脚不是“你想怎么用就怎么用”的一个引脚挂了多个功能编译的时候很容易出现“resource conflict”的报错。PWM输出引脚Ardupilot对PWM输出有明确的要求不同定时器对应的通道要理清楚。如果随便配轻则PWM不出波重则飞控上电直接烧电调信号线这个我是真踩过。调试串口的预留在硬件调试阶段无论如何你都需要至少一组USART来做控制台输出这对查找问题太重要了。没有调试串口很多问题根本无从下手。我把这块板子要用的外设整理成了表格类似这样功能模块使用的MCU外设引脚分配备注说明调试串口USART1PA9(TX), PA10(RX)波特率115200用于日志输出第一路GPSUSART2PA2(TX), PA3(RX)也可接其他串口外设第二路外设USART3PB10(TX), PB11(RX)备用I2C总线I2C1PB6(SCL), PB7(SDA)外接IMU与气压计主IMUSPI1PA5, PA6, PA7等注意SPI时钟频率PWM输出PWM1-PWM8不同定时器通道必须避免定时器通道重叠SD卡SDIOPC8-PC12等注意电源域提示有些Ardupilot官方板卡会直接把整个pinout表放在硬件的docs目录里如果找不到对应的板卡支持包那就一定要自己维护好这份表。移植过程里你这张表要随时拿起来对照减少大量无效试错。2.2 hwdef.dat的逐行拆解——每个参数背后都有坑hwdef.dat是Ardupilot固件生成过程中最重要的输入文件之一它本质上是一份“板级描述文件”里面以特定的语法描述了整个板子的硬件资源。很多初学者第一次打开一个官方板卡的hwdef.dat时看到几十行、上百行的宏定义整个人是懵的。但其实它的核心结构并不复杂我按照我当时修改的顺序把必须搞懂的段落给你理一遍。首先是MCU的定义MCU STM32H743xx这行看着简单但它决定了后面所有跟MCU相关的编译选项。有些时候这个型号写错一个字母后面编译出来的启动文件就完全不对。然后是FLASH_SIZE和RAM_SIZEFLASH_SIZE 2048 RAM_SIZE 512Flash大小在选BootLoader方案时是硬指标。我当时这块板子Flash有2MB按理说很充裕但如果不小心把整个Flash都划分给固件区BootLoader根本没有地方放这也是后面我专门去改Flash布局的原因。接下来是外设使能和引脚分配PA9 USART1 TX PA10 USART1 RX这一条条的映射规则看着一目了然但真正的坑在于“一致性”。比如你在这里定义了USART1的TX/RX那么后面还要确保DMA的配置与它匹配尤其是用DMA的时候稍不留神DMA的请求映射错了数据就一直出不来。还有一个非常容易被忽略的字段是PWM相关的定义。Ardupilot的PWM输出需要绑定到具体的定时器和通道例如PE9 TIM1_CH1 PWM(1) PE10 TIM1_CH2 PWM(2)如果你的板子上PWM输出比较多这里尤其要小心。我当时就是图省事直接把两个PWM定义到了同一个定时器的同一个互补通道上编译不报错硬件上定时器输出始终只有一个信号能出来。最后查了两天才意识到是这里的通道冲突。最后是HAL相关的定义像HAL_INS_DEFAULT、HAL_BARO_DEFAULT等等。这些定义决定了Ardupilot在启动时默认启用哪个驱动。如果你用的是外部I2C接口的IMU就必须把HAL_INS_DEFAULT指到对应的器件型号上比如define HAL_INS_DEFAULT HAL_INS_ICM20602这行配置最容易出的问题是写错了型号或者驱动里没启用对应的宏导致上电后日志显示“INS: no sensor found”这一卡就耽误很长时间。2.3 BootLoader的作用与烧录策略——先搞懂它和固件的关系很多人一听BootLoader就头大觉得这是嵌入式底层里的高深玩意。其实在Ardupilot移植的语境下BootLoader的作用可以简化成一句话在上电时把固件从Flash的某个地址“搬运”到RAM中去执行并提供一套刷写固件的通道。Ardupilot官方在很多板卡上用的是自己维护的一份BootLoader源码在固件仓库里可以找到对应平台的bl目录它包含了DFU和USB协议支持能直接通过USB口刷写固件体验非常友好。移植BootLoader时最核心的一件事就是地址对齐。Ardupilot的BootLoader一般会被放在Flash的起始位置而固件则会放在紧随其后的另一个地址。这个地址偏移量如果对不上就会出现我前面提到的“上电没反应”的问题。比如在H743上比较常见的布局是BootLoader占用从0x08000000开始的64KB固件从0x08010000开始放置这两个地址都是在hwdef.dat或者链接脚本里定义好的。改hwdef.dat的时候如果动过FLASH_SIZE或者自定义了分区千万别忘了同步改BootLoader侧的链接地址偏移。我当时为了图省事一开始打算直接用串口烧录工具跳过BootLoader把Ardupilot的二进制固件直接烧到Flash开头。结果上电一看日志输出全是乱的过了一会儿整块板子直接「死机」了。后来才意识到固件编出来默认是从“BootLoader之后”的地址开始执行的没有BootLoader做跳转它从一开始就不知道把自己放哪。注意不要把Ardupilot固件和BootLoader看作两个互不相关的独立部分。在实际移植过程中固件编译的链接脚本会动态地引用BootLoader占用的空间大小有些版本甚至会在编译时检查是否越界。如果你改了硬件存储布局一定要同时检查两边的地址定义。3. 实操过程与核心环节实现3.1 环境准备与编译链条搭建Ardupilot官方建议的编译环境是Ubuntu我用的就是Ubuntu 20.04 LTS。刚开始的时候你先别急着装依赖先把仓库clone下来再说git clone --recurse-submodules https://github.com/ArduPilot/ardupilot.git cd ardupilot git submodule update --init --recursive然后安装编译依赖Tools/environment_install/install-prereqs-ubuntu.sh -y这步会装一堆Python依赖、交叉编译工具链以及一些必要的库。如果中途报错基本就是网络或者Python版本的问题。我当时用的是Python 3.8官方要求一般也在这个范围内。依赖装好后configure一下你新建的板卡目标。假设你的板子名字就叫MyBoard那么在libraries/AP_HAL_ChibiOS/hwdef/目录下新建一个MyBoard文件夹里面先放一个hwdef.dat和一个hwdef.dat引用的defaults.parm参数默认值文件。然后返回根目录./waf configure --board MyBoard如果配置通过恭喜你基础环境已经OK了。接着就可以开始编译固件./waf plane或者如果你要的是Copter固件./waf copter这一步会花掉不少时间第一次编译基本都在10分钟以上取决于你的机器性能。如果是用交叉编译环境可能还要更久一些。没关系趁编译的空隙你就可以继续往下看因为接下来要处理的才是大头——hwdef.dat的完整编写和BootLoader的定制。3.2 hwdef.dat的编写实战新建的MyBoard/hwdef.dat一开始是空的你要把之前整理的引脚表一点点翻译成配置语句。以H743为例我给你的建议是直接参考官方现有板卡里的hwdef.dat比如MatekH743或者HolybroH743因为它们的硬件资源跟大部分自研板子比较类似避免从零起步。从参考板卡复制过来之后最优先要改的是这几段MCU与系统时钟段MCU STM32H743xx FLASH_SIZE 2048 RAM_SIZE 512 OS_STARTUP_STACK_BYTE 8128注意OS_STARTUP_STACK_BYTE这个参数在H743这种比较复杂的MCU上如果它设置太小系统启动的时候会直接进HardFault而且日志还特别难查。一开始我不知道随便填了个很小的值结果上电死机最后还是一个一个排除才定位到这个参数。串口段PA9 USART1 TX PA10 USART1 RX PA2 USART2 TX PA3 USART2 RX如果要启用DMA常见做法是在同一行加上DMA标记但我建议先把DMA关掉保证系统能跑通再说。因为DMA一旦参与进来你要排查的问题就可能从“能不能发数据”变成“数据为什么乱”问题排查的复杂度直接翻倍。SPI与IMU段PA5 SPI1 SCK PA6 SPI1 MISO PA7 SPI1 MOSI PE3 SPI1 CS0这里的CS0表示第一个片选信号对应到驱动里通常就是第一个外设的使能引脚。Ardupilot里面很多IMU驱动的选择不单是在hwdef里定义还需要在编译配置里开启对应的HAL_INS_DEFAULT所以检查完hwdef.dat还要回头检查config相关的头文件比如libraries/AP_HAL_ChibiOS/hwdef/common/STM32H743.h里对应外设的宏定义。把最低限度的东西配好之后先不要着急加别的功能。确保串口能通、IMU能读到数据再一步步把别的外设加进去。3.3 BootLoader定制流程接下来是重头戏。Ardupilot的BootLoader代码存放在仓库的Tools/bootloaders或者集成在固件源码里具体位置每个版本会有区别。如果你是直接用的官方仓库可以先看看现有板卡对应的bootloader是怎么编的。我当时需要的操作简单说就是在BootLoader源码里找到当前板卡的MCU类型宏定义。大多数BootLoader代码都是“一套代码适配多种MCU”靠的是编译时的宏定义来区分。所以你在编译之前要把STM32H743xx这个宏加到对应的编译命令行里或者在那个bootloader的构建脚本里加一行判断。确认Flash地址布局。在BootLoader的链接脚本中比如stm32h743.ld通常会定义FLASH_LOAD_ADDR和FLASH_BOOT_ADDR之类的符号。你需要把FLASH_BOOT_ADDR设置为你的BootLoader实际存放地址把FLASH_LOAD_ADDR设置为固件的放置地址。这两个值和hwdef.dat里分配的Flash区域必须一致。编译BootLoader。一般直接进入BootLoader的目录执行make或者用配套的构建脚本就行。我当时踩过一个非常隐蔽的坑是关于“固件加密”和“签名校验”的。有些BootLoader版本默认开启了固件签名校验如果你没有关闭这个功能自己编译的固件没被签名BootLoader会直接拒绝启动。这个现象会让你以为固件没刷进去但实际是BootLoader在第一次校验阶段就把固件“枪毙”了。解决方法是要么在BootLoader编译时关掉校验功能具体宏名在各个版本里不同常见的是ALLOW_BOOTLOADER_UPDATE或者类似的选项要么用官方提供的签名工具对你的固件签名后再刷写。如果你不确定自己的BootLoader到底有没有开校验最简单的验证方式是打开调试串口观察BootLoader启动时输出的日志。Ardupilot的BootLoader在正常启动时会输出BootLoader: starting一类的信息如果它卡在invalid signature就说明校验这关没过。3.4 烧录与联调全记录把BootLoader编译出来之后先用调试器我用的ST-Link烧到Flash起始地址。具体协议在ST-Link上一般就是STM32 ST-LINK Utility或者直接命令行st-flash write bootloader.bin 0x08000000。烧完BootLoader接上USB线看看能不能在电脑上识别出一个串口或者出现DFU设备。有些板卡需要进入DFU模式才能刷写固件通常做法是在上电瞬间按住BOOT按钮或者用命令行进入。一切正常的话就可以通过USB刷写Ardupilot固件了。我自己比较推荐用Mission Planner或者QGroundControl的固件刷写功能但如果你更习惯命令行直接这样操作也能跑通dfu-util -a 0 -D arducopter_with_bl.bin这里的arducopter_with_bl.bin是在编译Ardupilot时生成的带BootLoader头版本。不过第一次用的时候建议还是刷一个不带BootLoader的普通版本的.bin文件减少变量。刷写完固件后先别急着上电用调试器把那根USB转串口的线接好打开串口监视器波特率设成115200然后再上电。正常情况你会看到一串启动日志从MCU信息开始到各种传感器的初始化最后是EKF初始化。如果日志输出正常但提示“no GPS”或者“no compass”不用着急那是正常现象——你还没接外设。但如果连启动日志都看不到那就要按下面的排查清单一项项过。4. 常见问题与排查技巧实录4.1 编译失败hwdef.dat里引脚重复定义这个报错最常见的表现形式是Error: Pin PE5 already used by TIM4_CH1或者类似的外设冲突提示。遇到这种先不要怀疑编译器回到hwdef.dat里检查是不是有个别引脚被你定义到了不同外设上。有些引脚是交错复用比如一个引脚既能做TIM4_CH1也能做UART的TX你如果两个都写了编译器肯定会报错。排查技巧把hwdef.dat里所有引脚定义列一张表然后拿你手里的硬件原理图去比对。切忌随手改改错一个引脚你可能后面整整两个小时都在找为什么某个外设不工作。4.2 固件刷写成功但上电无反应这种故障十个里面有八个是BootLoader和固件地址区域重叠或者偏移错位。你可以先把BootLoader去掉直接在调试器里把固件烧到BootLoader应该跳转的目标地址看看固件能不能直接跑起来。如果固件在目标地址能跑说明问题大概率在BootLoader的跳转逻辑如果固件在目标地址都不能跑那问题就在固件链接脚本或者hwdef.dat的存储布局上。还有一个被很多人忽略的因素是“水泥电源问题”H743这种高性能MCU对电源纹波特别敏感尤其在上电瞬间如果电源不稳可能BootLoader还没跑起来就直接复位了。排查办法是在调试器里打断点或者观察复位原因寄存器如果显示是“欠压复位”那就得回去检查硬件电源设计。4.3 编译固件时生成的地址和BootLoader不一致这个是我个人觉得最隐蔽的坑。Ardupilot在编译固件时会从hwdef.dat读取FLASH_SIZE等信息然后在链接脚本里计算固件的加载地址。但是BootLoader侧也有自己的一套链接脚本。两边只要有一个字节的偏差整个系统就可能起不来。我建议把两边最终生成的可执行文件里的地址符号都打印出来用arm-none-eabi-objdump看比猜来猜去高效得多。例如arm-none-eabi-objdump -h build/MyBoard/bin/arducopter.bin看每一段的VMA和LMA手工核对一下是否落在正确的区间。这个方法虽然原始但对于排查地址错位问题非常奏效。4.4 传感器检测不到日志里出现HAL_INS_... not found之类或者QGC的HUD上高度、姿态都是0。排查方向确认SPI时钟、引脚、片选有没有配错确认IMU的驱动类型有没有在hwdef.dat里设定确认IMU的供电和复位引脚是否正常。特别想提醒一下如果你用的是外部I2C的IMU比如某些气压计和罗盘要注意I2C总线上拉电阻的问题。有些小模块为了省电板子本身没有上拉需要你外接两个几kΩ的上拉电阻到3.3V否则通信时好时坏看着像软件问题其实是硬件问题。4.5 刷写太多次偶尔变砖别慌这事儿基本不会真的变砖。Ardupilot的BootLoader具有很好的容错性绝大多数情况下你都能重新进入DFU模式或者通过调试器救回来。如果USB刷写不识别先检查是不是驱动问题Windows上换一个USB接口Mac和Linux换一根数据线很多时候是线材质量问题导致供电不稳。如果实在救不回来就用ST-Link/J-Link直接接SWD接口用命令行强制擦除整片Flash然后重新烧BootLoader。这个方法可以解决99%的“变砖”问题。5. 常用工具与命令行速查为了少走弯路我把整个过程中我觉得最实用的几个命令整理成了一张速查表功能命令/工具说明配置板卡./waf configure --board MyBoard生成编译配置编译Copter固件./waf copter目标产物在build目录下编译带BootLoader固件./waf copter --bootloader生成可直刷文件下载固件到板子dfu-util -a 0 -D xxx.bin需要进入DFU模式使用ST-Link烧录st-flash write bootloader.bin 0x08000000也可以直接用STM32CubeProgrammer查看ELF段信息arm-none-eabi-objdump -h file.elf排查地址偏移查看串口日志screen /dev/ttyACM0 115200Linux下常用这组工具链如果你能熟练使用后面调其他飞控项目也能顺手不少。6. 移植完成后的验证清单与后续扩展建议等你终于看到启动日志正常输出、QGC和Mission Planner能连上、遥控器摇杆能响应、电调校准能通过那一刻真的很爽。但先别急着庆祝我建议按照下面这个清单把板子过一遍[ ] 启动日志有无异常报错比如CRC mismatch、Bad IMU。[ ] 校准加速度计、陀螺仪和磁力计检查数值是否在合理范围。[ ] 检查PWM输出确保每个通道在电机测试页面都能正常输出对应频率和脉宽。[ ] 用示波器或逻辑分析仪看一下PWM波形确认频率和占空比符合预期。[ ] 整机连续通电几个小时看会不会出现无故重启、日志闪断等问题。[ ] 把电池接上做一次完整的电压校准确认电压检测回路正常。这些都是我反复踩过坑才总结出来的“最低验证标准”别嫌多每一条背后都对应着一次意外。后续如果想继续深入可以考虑给板子加上OSD、数传、CAN外设等更多功能或者针对自己的应用场景开启一些独有特性比如自定义混控、故障保护策略等。Ardupilot的灵活性很高只要底层的hwdef.dat结构清晰加功能只是“再加一行配置”的事。我个人在实际操作中的一个体会是Ardupilot的移植问题十有八九不是代码能力的问题而是信息差的问题。很多东西在官方文档里看着很简单但实际操作会遇到各种文档里没写的边界情况。这时候唯一靠谱的方法就是自己一点点把日志、地址、引脚对应关系全部摊开来看用最笨的办法排查最隐蔽的问题。最后再分享一个小技巧如果你在调试中实在找不到原因可以去翻一下Ardupilot官方仓库里的issue记录很多奇怪的报错其实都有人提交过了。把关键词换成英文搜索大概率能十秒钟定位到你花了一个下午才找到答案的问题。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑