USB-CAN小板刷机指南:APM32F072开源固件移植与SocketCAN对接

发布时间:2026/9/6 14:41:24
USB-CAN小板刷机指南:APM32F072开源固件移植与SocketCAN对接 手头有个带APM32F072的USB-CAN小板一直想把它刷成兼容Kvaser工具链的开源固件顺便试了moonglow这套前前后后折腾了几天总算把整个流程捋顺了。这篇东西把移植、烧录、驱动、验证、排坑整个链条都记下来涉及USB-CAN协议选型、APM32F072的Bootloader模式切换、开源固件的改写适配以及Windows/Linux双系统下的兼容性处理。想把手里的国产USB-CAN小板变成能跟CANoe、CanKing、SocketCAN这些软件顺畅通信的调试利器这篇文章应该能帮你省掉一大半弯路。1. 项目概述与核心思路1.1 这个东西到底能做什么APM32F072是一颗国产Cortex-M0内核的MCU片上自带USB FS控制器和CAN 2.0B控制器两颗东西正好凑成USB-CAN协议转换器所需要的全部硬件资源。很多市面上十几块钱的USB-CAN分析仪小板用的就是这颗料芯片本身还完全兼容STM32F072的引脚定义所以ST生态的Bootloader方案、烧录工具、固件库都能直接套用。原厂出厂固件往往是私有协议想用PCAN-View、Kvaser CanKing这类专业CAN分析软件就得换一套思路。moonglow/kvaser这一类开源固件做的事情本质上就是重新实现设备端的USB描述符和CAN数据交换协议把它伪装成CAN上位机软件认识的“标准设备”。刷完后电脑端呈现的要么是一个虚拟串口CDC类要么是一个KVaser/Lawicel协议的USB设备然后就可以用开源的can-utils、Wireshark、CanKing或者商业软件去做总线监控、报文发送、DBC解析这类工作。先说说适用人群。如果你只是偶尔测一两块CAN板子不想买几百上千块的正版CAN卡或者你在做大学生电动方程式、机器人竞赛这类预算有限的项目又或者你就是单纯想折腾一下固件移植不想让手里的硬件吃灰——这套方案非常适合你。涉及到的基础知识门槛不算高懂一点串口和命令行操作就能上手即使完全没接触过CAN总线按文章里的步骤走也能把环境跑起来。1.2 为什么是APM32F072这颗芯片先说选型。跟STM32F072一样APM32F072内部有独立的USB和CAN外设不需要外挂USB转串口芯片也不需要额外的MCP2515 SPI转CAN芯片一颗芯片就把桥接功能全包了。这在小板子上意味着成本低、布线简单、功耗也低。开发过程中我对比过几种方案总的来说值得关注下面几个参数参数APM32F072说明内核Arm Cortex-M0 48MHz跑CAN/USB协议栈绰绰有余Flash/RAM128KB Flash / 16KB RAM装得下带协议栈的完整固件USBUSB 2.0 FS Device12MbpsCAN 500kbps 情况毫无压力CAN1路 CAN 2.0B支持标准帧和扩展帧工作电压2.0~3.6V板上一般有LDO稳压到3.3V兼容性引脚兼容STM32F072烧录和库函数基本无缝迁移有一点必须说清楚虽然是“兼容”但APM32F072的USB外设寄存器细节和小部分APM32自有库函数与ST原生firmware存在差异。做固件移植时尤其是直接用STM32F072的USB协议栈库可能会遇到USB枚举不稳定、设备反复断开重连这类问题。千万不能无脑把ST的整个库代码全部拿过来而是要根据APM32官方SDK重新适配。1.3 选型背景极海与ST的“恩怨”说个行业内的小背景。极海Geehy的APM32系列在硬件上对ST的STM32做了相当深度的兼容设计目的就是方便厂商做替代。很多国产USB-CAN小板在设计之初就是按STM32F072CBT6来画板的后来由于芯片供货和价格原因换成了APM32F072板子上的丝印也跟着变了。这种替代对开发者来说是把双刃剑。好的一面是烧录接口、BOOT0配置、串口引脚位置几乎完全一致市面上绝大多数ST的烧录工具和命令行工具都能用不好的一面是如果你直接拿ST的USB例程编译烧进去后在电脑上设备管理器里可能出现“未知USB设备设备描述符请求失败”这就是寄存器级差异导致的枚举失败需要按照APM32的数据手册修改USB时钟配置和端点描述符相关的初始化代码。2. 固件选型与原理拆解2.1 moonglow固件与Kvaser固件的定位差异开源USB-CAN固件圈子里有两类典型代表一类是moonglow另一类是Kvaser固件的重制/移植版。很多人以为它们是同一个东西其实区别还挺大。moonglow固件的核心思路是把板子做成一个CDC虚拟串口设备然后在串口链路上跑一种简化的CAN命令协议。电脑上不需要装厂商私有驱动系统自带USB串口驱动就能识别。这种方式的优势是跨平台友好Linux下可以通过slcan_attach把虚拟串口直接挂载成SocketCAN网络接口Windows下也可以用任何串口助手打开并手动发AT命令完成CAN报文收发。代价是实时性一般不适合高负载总线监控做学习和基础调试够用了。另一类Kvaser固件移植目标则是让设备在Windows下被识别为“Kvaser Leaf Light”这类商业硬件。这样Kvaser官方的CanKing、CANlib SDK、甚至CANoe部分版本都能把这个几块钱的国产小板当成正版Kvaser设备来用。这类固件需要在USB协议层面模拟Kvaser的厂商自定义类请求和端点传输格式对USB描述符和中断传输的时序要求更严格实现难度更高但确实能让用户体验到商业软件的高级功能。我的建议是如果你平时主要在Linux下用命令行调试直接上moonglow轻量干净如果需要在Windows下配合标定工具或商业CAN软件再考虑Kvaser兼容固件。两个固件在同一个板子上是可以随时切换刷写的只要前面刷写流程熟练切来切去也就一分钟的事。2.2 刷写前必须搞懂的Flash存储布局和Bootloader动手之前建议先把APM32F072的存储结构弄清楚否则刷错地址会导致固件跑不起来。APM32F072内置的Flash从0x08000000开始大小为128KB。用户固件一般放在0x08000000这就是应用程序的起始地址。出厂时芯片内部系统存储器System Memory里有一段固化Bootloader它的作用是从特定串口或USB接口接收数据并写入FlashBootloader本身位于一个独立的地址区域用户程序无法覆盖它。要让系统进入这个Bootloader只需要把BOOT0引脚拉高接到VDD然后复位芯片芯片上电后就会从系统存储器启动而不再执行用户Flash里的代码。如果你手里这块USB-CAN小板把BOOT0引脚引出来了那刷写就很简单用杜邦线短接BOOT0到3.3V然后重新上电。如果板子上没引出BOOT0也有另一个思路——利用固件内部预留的“应用内跳转Bootloader”功能比如在USB枚举失败或特定条件下执行NVIC_SystemReset跳转不过这就得依赖原厂固件有没有留这个后门不如直接硬件拉BOOT0来得稳。实际操作中我用一根公对母杜邦线把板子的BOOT0焊盘与3.3V引脚短接再用Micro-USB线连接电脑设备管理器里就会多出一个“STM32 BOOTLOADER”串口APM32F072的Bootloader兼容ST的标准协议这就是成功的信号。2.3 开源固件仓库里应该关注哪些文件拿到一个开源固件压缩包不要急着编译或烧录先看目录结构。一个规范的USB-CAN开源固件仓库通常有下面这些文件firmware/或src/源码目录包含USB描述符、CAN初始化、主循环代码release/、output/、hex/编译好的hex/bin文件通常按协议版本或板型区分tools/烧录脚本、驱动安装说明、上位机工具README.md硬件接线、编译环境、烧录步骤docs/协议说明包括串口命令格式、USB端点定义这里有个坑要提醒很多开源项目默认针对STM32F072编译如果你直接下载编译好的hex烧进APM32F072大概率能起步但可能不稳定。正确的做法是优先找项目里是否明确写了支持APM32如果没有就用源码自己定义APM32F072宏仔细核对APM32数据手册把USB时钟源配置和GPIO复用配置改对再重新编译。3. 硬件准备与工具链搭建3.1 硬件清单与接线说明整个项目需要准备的东西并不复杂我实际用的清单如下物料型号/规格用途USB-CAN小板带APM32F072CBT6的通用板刷写目标USB转TTL串口模块CH340/CP2102均可进入Bootloader后通过USART1刷写杜邦线若干公对母、母对母短接BOOT0连接RX/TXMicro-USB数据线确认支持数据传输连接电脑与板子Windows电脑/Linux电脑任意烧录与验证接线部分APM32F072的系统Bootloader默认使用USART1作为串口下载通道对应引脚是PA9USART1_TX和PA10USART1_RX。把USB转TTL模块的TX接板子的PA10RX接PA9GND一定要共地。注意千万别把TX接TXRX接RX这是新手最容易犯的错误直接表现就是烧录软件提示无法连接。3.2 进入系统Bootloader的三种方式有三种方式可以进入系统Bootloader我分别说下适用场景。第一种是硬件BOOT0拉高法最通用。断电状态把板子BOOT0焊盘或引脚用跳线帽/杜邦线接到3.3V上电后复位芯片就停留在系统Bootloader等待串口或USB数据。刷完后断电、去掉跳线再重新上电进入用户固件。第二种是软件复位法适用于固件里已经预留了跳转指令的情况。在用户代码执行中判断到一个GPIO电平或者USB命令然后调用NVIC_SystemReset()并设置SYSCFG_MEMRMP把启动地址切换到系统存储器。很多开源固件在检测到“短接某个引脚再上电”这个动作时会主动进入Bootloader但要注意这依赖固件本身不一定所有固件都有。第三种是USB DFU模式。APM32F072系统存储器里的Bootloader也支持USB DFU协议进入方式和第一种一样只是电脑端识别到的设备不再是串口而是一个“STM32 BOOTLOADER”DFU设备需要用DfuSeDemo或dfu-util来操作。这个方式优势是不需要额外USB转串口模块劣势是DFU工具在Windows下的安装配置比较麻烦所以我个人更推荐串口烧录。3.3 命令行烧录stm32flash与OpenOCDWindows下我用的是stm32flash这个轻量工具不需要安装命令行直接跑。安装好CH340驱动后先看设备管理器确认串口号假设是COM3刷写命令形如stm32flash -w moonglow_apm32f072.hex -v COM3-w表示写入-v表示写完校验文件路径根据实际修改。如果要先擦除整片Flash可以加一个-e参数不过我建议直接烧hex文件大部分工具会根据hex内部的地址信息自动擦除对应扇区。如果是在Linux下同样用stm32flash串口设备名一般是/dev/ttyUSB0sudo stm32flash -w moonglow_apm32f072.hex -v /dev/ttyUSB0芯片连接正常并且进入Bootloader后建议先用不加写入参数的模式读一下芯片信息stm32flash /dev/ttyUSB0输出会显示芯片型号、Flash大小、当前保护状态确认能识别到芯片再执行写入能够少踩很多坑。OpenOCD是另一种选择但它需要额外的ST-Link调试器接线到SWD接口。对于没有引出SWD的USB-CAN小板来说串口烧录反而更简单。只有当你需要调试固件源码、设置断点时ST-Link OpenOCD才真正有必要。3.4 安装USB驱动时最容易翻车的点串口烧录完成后断电、把BOOT0跳线去掉、重新上电这时候进入的是用户固件。Windows下如果识别不到设备或者设备管理器出现黄色感叹号多半就是驱动问题。APM32F072的固件如果实现的是CDC虚拟串口Windows 10/11自带usbser.sys驱动理论上插上就能枚举成COM口前提是USB描述符里的VID/PID和CDC接口配置正确如果固件实现的是Kvaser类厂商自定义设备就需要安装一个与固件配套的驱动。这里必须强调不要随便下载网络上来路不明的“万能驱动”最好用固件仓库里附带的驱动安装包或者用Zadig工具手动指定驱动。Zadig是一个开源驱动安装工具可以把未知设备强制绑定到WinUSB或libusb驱动。但绑定之前要想清楚WinUSB驱动适合libusb/can-utils这类跨平台工具不适合Kvaser官方驱动。Kvaser官方驱动会校验设备的VID/PID如果你的固件模拟的是Kvaser却装了WinUSB驱动反而没法用官方软件。4. 实操过程从擦除到验证4.1 我实测的刷写命令与踩坑记录下面是我刷moonglow固件到一块APM32F072板子上的完整过程一步一步记录包括中间遇到的插曲。先把USB转TTL模块插上电脑确认CH340枚举为COM3。然后用杜邦线短接板子BOOT0到3.3V再插上Micro-USB给板子供电可以先不接电脑USB数据只用外部5V供电也行但我实际是直接用电脑USB口供电的注意不要同时接两路电源。上电后执行stm32flash COM3正常情况下会显示stm32flash 0.7 http://stm32flash.sourceforge.net/ Using Parser : Raw BINARY Interface serial_posix: 115200 8E1 Version : 0x31 Option 1 : 0x00 Option 2 : 0x00 Device ID : 0x0442 (STM32F07x)这里看到Device ID是0x0442说明APM32F072在Bootloader里自报的ID和STM32F072一致。第一次刷的时候我用的hex文件是直接从别的板子拷贝的烧录后重新上电设备管理器直接不识别后来排查才发现是时钟配置里HSE频率不对。APM32F072和STM32F072一样外部晶振通常是8MHz但如果固件代码里用的是HSI内部时钟USB就需要PLL倍频到48MHz任何一个环节算错USB都枚举不出来。正确的烧写命令我最终定为stm32flash -v -e -w moonglow_apm32f072.hex COM3-e会先执行整片擦除避免Flash残留数据干扰。擦除时间看Flash大小128KB大约需要十几秒耐心等待最后会输出写入进度和校验结果。4.2 刷新后重新上电注意事项刷写成功后先不要急着拔线。关掉stm32flash软件断开USB转TTL模块和板子之间的RX/TX连接然后断电把BOOT0跳线恢复到GND位置最后重新上电。这里有个细节如果不把RX/TX线断开某些情况下BOOT0拉低后串口模块上残留的电平信号会干扰用户固件启动导致程序跑飞。虽然不绝对但养成好习惯能省不少事。重新上电后用lsusbLinux或设备管理器Windows确认USB枚举情况。如果我刷的是moonglow固件Linux下会看到类似Bus 001 Device 005: ID 1d50:606f这个VID/PID是OpenMoko项目分配的moonglow设备通常沿用这个标识代表一个CDC ACM设备。Windows设备管理器则会多出一个“USB 串行设备 (COMx)”。如果你刷的是Kvaser兼容固件设备管理器里会显示一个带Kvaser字样的设备。4.3 回环测试与收发验证刷完固件的第一个测试不要连真实CAN总线先做回环Loopback把CAN_H和CAN_L用一根短线直接连起来。这样既能验证MCU内部CAN控制器收发路径又不会因为总线上没有终端电阻或波特率不匹配导致错误帧刷屏。如果固件支持SLCAN协议moonglow系列通常支持Linux下先加载虚拟串口到CAN的驱动程序sudo slcand -o -s6 -t hw -S 500000 /dev/ttyACM0 can0 sudo ip link set up can0-s6对应500kbps波特率-S设置串口波特率。完成后用candump can0监听另开一个终端发送cansend can0 123#DEADBEEF如果回环正常candump终端会立刻打印出同样的报文。看到这个输出说明从USB到CAN控制器的整条链路已经通了。Windows下用CanKing或串口助手也同样可以测试发送一条标准帧ID0x123数据随便填回环模式下能收到自己发的报文就说明一切正常。5. 应用兼容与双系统驱动的折腾5.1 Windows下Kvaser驱动的安装思路如果你刷的是Kvaser兼容固件Windows下就可以尝试安装Kvaser官方驱动。安装时建议去Kvaser官网下载最新版“Kvaser Drivers for Windows”安装过程全默认即可。插件设备后打开Kvaser CanKing软件会自动扫描总线上的Kvaser设备识别到就能直接打开通道。这里有个容易踩的坑不是所有模拟Kvaser的固件都能通过官方驱动验证因为新版Kvaser驱动会检查固件版本和硬件序列号区域。如果固件没有正确实现这部分可能装完驱动后设备仍然提示“没有Kvaser设备”。解决办法有两种一是找一个旧版本的Kvaser驱动兼容性更宽松二是检查固件源码里的USB描述符确保VID/PID、iProduct字符串与固件说明文档一致。我个人更推荐用moonglow SocketCAN这套组合在工程里干活因为Linux下的诊断工具链足够丰富抓包、统计、DBC解析都能用命令行完成写脚本自动化测试也方便。5.2 用Wireshark抓USB-CAN报文做协议验证有一个很实用的验证技巧用Wireshark抓USB总线上的URB数据可以清晰看到上位机与USB-CAN设备之间的协议交互。在Linux下需要先加载usbmon模块然后Wireshark选usbmon0接口过滤条件填usb.idVendor 1d50就能看到moonglow设备的所有USB控制传输和中断传输。通过抓包你能够确认两件事第一USB描述符是否完整设备是否被正确枚举第二CAN报文在USB端点上走的哪个管道、什么格式。这个能力在调试自己写的上位机时特别有用可以直观看到请求是否发出、响应是否返回。顺便说一句抓包期间不要开其他占用USB的设备干扰太多会导致分析困难。5.3 Linux SocketCAN与can-utils的完整对接SocketCAN是Linux内核自带的CAN协议栈通过虚拟串口挂载的CAN设备使用起来和真实CAN卡几乎没有区别。完整的初始化流程可以这样写进脚本方便每次开机快速配置#!/bin/bash sudo slcand -o -s8 -t hw -S 1000000 /dev/ttyACM0 can0 sudo ip link set up can0 sudo ip link set can0 txqueuelen 1000txqueuelen这个参数很容易被忽略但实际工程中很重要。如果总线负载较高而队列长度太短内核会丢弃发送报文造成莫名其妙的丢帧。设成1000比较稳妥。配置完成后可以用ip -details link show can0查看波特率、状态、环回模式等详细参数确认一切正常再开始用cansend、candump、cangen进行测试。6. 常见问题与排查技巧实录6.1 刷写失败无法连接串口如果你执行stm32flash COM3提示Failed to connect优先检查三件事BOOT0是否真的拉高了、串口模块的TXD/RXD是否接反、芯片是否在上电状态。最直观的办法是用万用表量BOOT0引脚的电压确认上电后有接近3.3V的高电平。另外注意stm32flash默认用115200 8E1偶校验连接Bootloader某些CH340兼容性不好可能会出现连接失败。可以尝试降低串口波特率stm32flash -b 57600 COM3APM32F072的Bootloader对波特率自适应57600和115200都能用有时候降速反而更稳定。6.2 刷完电脑报告未知USB设备这个问题的出现频率最高。核心原因是USB描述符在枚举阶段没有被主机正确解析可能是固件里USB时钟没配成48MHz也可能是USB D上拉电阻配置错误。APM32F072的USB内置了D上拉电阻但需要软件在连接USB前通过USB_Attach()这类函数使能如果固件移植时漏了这一步主机侧会表现为“设备描述符请求失败”。排查步骤建议先在Linux下用dmesg看内核日志USB设备插上后会输出详细的枚举失败原因。如果日志显示device descriptor read/64, error -71基本就是时钟或上拉的问题回到源码检查SystemInit中的PLL配置。6.3 回环测试单向通、单向不通发送没问题但收不到或者接收超时这类问题往往不是固件bug而是CAN收发器方向控制引脚接错。USB-CAN板子上CAN收发器芯片如TJA1050通常有一个发送使能引脚由MCU的某个GPIO控制。移植固件时如果GPIO初始化不对会导致收发器一直处于发送模式收不到总线上其他节点发来的数据。检查固件源码里的GPIO初始化部分找到控制CAN收发器STB或RS引脚的定义用万用表测量该引脚电平是否与数据手册要求的常态电平一致。这类硬件相关的坑只靠逻辑分析仪很难发现手动对照原理图和代码是最快的路子。6.4 固件升级差分升级与恢复机制的小展望有朋友问过如果以后官方修复了bug新增了功能总不能每次都用串口线重新刷吧。这就涉及“固件差分升级”的思路利用Bootloader定期检查新固件的差分包只更新Flash中有变化的扇区而不是整片重写。APM32F072的Flash支持按页擦除每页1KB或2KB具体看型号做差分升级算法时可以先用crc32校验固件包的每个区块只擦写不一致的段。这样最大程度减少升级时间和Flash寿命损耗。即使不做差分升级我也建议读者朋友在Flash末尾留一小块区域存设备配置文件比如波特率、过滤规则、设备序列号。这样升级固件时不清除配置区能减少重复配置的麻烦。这个思路在开源圈子里很成熟而且能显著提升日常使用体验。7. 扩展玩法与个人体会7.1 一板多固件切换刷写流程熟练之后可以玩一个组合拳同一块USB-CAN小板在moonglow固件和Kvaser兼容固件之间自由切换。要连Linux用moonglow要配合Windows商业软件就刷Kvaser整体切换时间不超过三分钟。建议平时把两个固件的hex文件都放在同一个工程目录下一个叫moonglow.hex一个叫kvaser_compat.hex配合烧录脚本一键切换。脚本里用stm32flash分别完成擦除和写入还能打印当前文件名方便确认避免刷错。7.2 关于开源固件生态的几点思考这个项目让我重新审视了开源固件在工业调试工具生态里的地位。商业CAN卡动辄上千元但核心硬件成本可能只有几十块钱开源社区通过重写固件、模拟商业协议把工具的使用门槛打到极低。这本质上是一种“软件定义硬件”的打法同一块硬件换个固件就能在不同生态里工作价值完全靠代码挖掘。同时也要提醒模拟商业设备协议这件事存在一定的侵权灰色地带如果是商业项目或竞赛评审务必确认你使用的固件授权和商标合规性。个人学习和内部调试用途没问题但不要拿出去商用或宣称是正版设备。7.3 最后再分享一个实用小技巧如果你用的是moonglow这类CDC设备在Linux下会遇到一个叫“串口锁”的问题上次程序异常退出后/dev/ttyACM0可能还被残留进程占用导致slcand启动失败。解决办法很简单启动前先清理一下sudo pkill -f slcand sudo pkill -f cu.或者在脚本里加一个fuser -k /dev/ttyACM0提前释放端口。这些小细节看着不起眼但在现场调试时能救你一命。愿大家的USB-CAN小板都能物尽其用少走弯路。