ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

国产APM32F072移植moonglow,自制Kvaser兼容USB-CAN分析仪

国产APM32F072移植moonglow,自制Kvaser兼容USB-CAN分析仪 如果你调过CAN总线大概率对Kvaser这个牌子不陌生——一个正经的USB-CAN分析仪动辄上千而我要折腾的方向是用一块二十来块钱的国产APM32F072把moonglow这个开源固件移植进去让电脑直接把设备识别成Kvaser Leaf Light直接用Kvaser官方驱动和软件做CAN收发。这不是天方夜谭而是已经有人验证过的路子我这次把它搬到了国产芯片上。这篇文章适合两类人一是手上有APM32F072或者国产F072兼容片、想低成本做CAN调试工具的开发者二是单纯想研究USB-CAN设备枚举、Kvaser协议兼容和CAN控制器底层初始化的学习者。我会从原理讲到实操把移植过程中踩过的坑一次性说清楚。整个移植周期大概一到两个晚上硬件成本不高但学到的东西相当值。1. 被Kvaser软件驱动的“兼容设备”到底动了谁的奶酪1.1 Kvaser为什么贵贵在哪里Kvaser在CAN总线调试圈子里一直属于“专业级”工具价格贵不只是因为硬件更多是它的软件生态和驱动体系。Kvaser官方提供了一套完整的CANlib SDK围绕这套API有大量调试、分析、标定、诊断工具比如最常见的CANKing以及各种支持CANlib的第三方上位机。问题在于这把“生态”和“硬件”绑死了。你要用Kvaser的软件就得买Kvaser的硬件你买了它的硬件很大一部分钱其实是在为驱动、协议和软件兼容性买单。对个人开发者来说这个成本确实不低。1.2 moonglow的工作原理用USB协议伪装moonglow这个开源项目的思路很直接从USB层面逆向并复刻Kvaser Leaf Light的通信协议让设备在电脑眼中看起来就是一台Kvaser Leaf Light。这样就不需要自己写上位机、写驱动把设备插上USBWindows自动匹配Kvaser官方驱动CANKing之类的软件直接就能用。这个思路和candleLight项目很像但两者目标不同。candleLight模拟的是PCAN-USBmoonglow走的是Kvaser兼容路线。技术核心都在USB的厂商自定义请求和批量数据传输这两块——设备必须能正确处理Kvaser驱动下发的各种命令帧比如总线波特率设置、CAN报文发送、接收队列读取等。只要这些命令处理得足够完整上位机眼中的设备行为和原装Kvaser基本没有差别。1.3 为什么不是candleLightcandleLight对应的硬件是STM32F105主芯片价格、货源稳定性、BOM复杂度都比F072高一个级别。而moonglow用Cortex-M0级别的F072就能跑Flash占用低外设也刚好够用。APM32F072作为F072的国产兼容替代成本进一步压下来板子做出来就是真正的“几十块钱的USB-CAN”。这个思路也符合嵌入式工具链的一个普遍趋势硬件HAL层差异越来越小真正值钱的是通信协议栈和上位机生态。自己从零写一套CAN调试软件难度不大但工程量不小而兼容已有生态是更聪明的做法。2. APM32F072的底子能不能扛起移植这面旗2.1 芯片规格对照可以替代到什么程度先说结论APM32F072和STM32F072不是简单“可共用代码”而是大部分场景下可以直接替换。拿我用的APM32F072CBT6来说Cortex-M0内核最高48MHz128KB Flash16KB SRAM带USB 2.0全速设备控制器和一个CAN 2.0B控制器LQFP48封装引脚排列和ST对应型号基本一致。这套资源和moonglow原项目的要求是完全吻合的。资源项STM32F072CBT6APM32F072CBT6移植影响内核Cortex-M0Cortex-M0无主频48MHz48MHz无Flash / SRAM128K / 16K128K / 16K无USBUSB 2.0 FS DeviceUSB 2.0 FS Device寄存器地址基本一致CANbxCAN 2.0BCAN 2.0B寄存器写法兼容性高库函数风格ST SPL / HALAPM32 SDK需要适配从功能角度这块芯片完全可以承接moonglow固件。真正的差异不在“有没有外设”而在“寄存器定义和时钟树细节”。2.2 外设差异逐个核对时钟、GPIO、USB、CAN移植之前我把几个关键差异点列了一遍这是决定工作量的主要部分。时钟系统APM32F072对应的时钟管理单元叫RCM不是ST的RCC但寄存器布局基本沿用同一套思路。HSI内部高速时钟8MHz通过PLL倍频到48MHz然后作为系统时钟和USB时钟源。这个配置路径在ST上怎么写在APM32上照着写大概率能通但不能直接拿ST的库函数头文件去编译需要用APM32的寄存器定义头文件替换。GPIO复用APM32的GPIO寄存器和ST一样有MODER、OTYPER、OSPEEDR、PUPDR、AFRL、AFRH这些配置项。USB的PA11/PA12复用功能编号是AF1CAN的PB8/PB9是AF4这一点在两个芯片上一致的。USB外设APM32F072的USB控制器和ST一样支持内部D上拉通过USB_BCR寄存器的相关位控制不需要外部上拉电阻。这对硬件设计很友好。CAN外设APM32F072的CAN控制器不仅支持CAN 2.0A/B而且寄存器结构与ST的bxCAN高度一致CAN_BTR、CAN_TIxR、CAN_RDTR这些寄存器操作几乎可以原样复用。2.3 移植路线定调寄存器优先还是库函数优先既然寄存器层面这么接近移植策略就很清晰了保留moonglow里的寄存器直接操作代码替换掉ST的CMSIS头文件和启动文件最小化代码改动。为什么这么选moonglow这种固件涉及USB协议时序、CAN中断处理如果改用APM32 SDK的库函数重写等于把底层逻辑重新翻译一遍引入了大量不确定因素调试周期会拉长很多。而直接寄存器操作本来就绕开了库函数的API差异换个头文件定义就能编过。当然这不代表APM32 SDK没用。后续如果要加功能比如用串口打印调试信息、用ADC采集总线电压直接用APM32 SDK写新模块会更快。我最后就是这么干的核心固件保持寄存器操作新增代码走SDK。3. 源码级移植过程从改时钟到USB枚举成功3.1 拿到工程之后先动哪几个文件moonglow固件拿到手后先别急着编译把工程目录里几个关键文件挑出来梳理一遍。第一是启动文件。原工程带的是startup_stm32f0xx.s这是Cortex-M0的中断向量表理论上可以用但为了减少不必要的兼容问题我换成极海SDK里的启动文件反正中断处理函数名都是一样的标准写法比如USB_IRQHandler、CAN_TX_IRQHandler这些。第二是系统时钟文件。原工程大概率带的是system_stm32f0xx.c这里面的SystemInit函数依赖ST的RCC寄存器定义直接替换成APM32 SDK里的对应文件更省事。第三是芯片型号头文件。moonglow核心代码里包含的头文件要把stm32f0xx.h换成apm32f0xx.h。别小看这一步编译报错里大部分坑都出在这里。第四才是功能代码。真正需要大动的就是时钟初始化和GPIO复用配置其他部分比如USB描述符、CAN收发逻辑基本不动。3.2 时钟树改造HSIPLL到48MHzAPM32F072上电后默认使用HSI 8MHz系统时钟跑在8MHz这个状态USB是没法正常工作的。必须在main函数一开始就把系统时钟配置到48MHz并且保证USB外设使用的就是这48MHz。当时我在配PLL时踩了一个典型的坑原代码里PLL倍频配置写的是PLLMUL_6也就是8MHz乘6等于48MHz这个逻辑在ST上没有毛病。但是如果APM32的RCM头文件寄存器位定义和ST的布局有一点出入这种魔法数值配置很容易出现PLL不能锁定或者时钟输出不是预期值的现象。解决方式很简单不去硬抄数值直接看APM32 SDK里PLL相关的枚举定义用头文件里的宏来配置而不是直接用裸的数字。我当时把RCM_CFGR的PLLSRC、PLLMUL、USBPRE这几个位分别核对了一遍确认APM32F072的PLL源选择HSI、USB时钟来自PLL时不需要额外分频然后才把SystemInit挂上。48MHz准不准直接关系到后面USB枚举能不能成功别图省事跳过去。3.3 GPIO与AF映射把USB D/D-、CAN TX/RX接到正确引脚moonglow的原理图里USB走PA11和PA12CAN走PB8和PB9这是F072最标准的分配APM32F072也一样。关键区别在复用功能编号和库函数写法。在ST标准库时代配置PA11为USB功能往往直接操作GPIOA的AFRL寄存器把AFRL[11:8]写成AF1。APM32F072的GPIO有完全类似的AFR寄存器所以这段逻辑直接平移过去就行。我当时为了少走弯路直接在初始化代码里写了一个接口函数把USB的引脚配置、CAN的引脚配置、以及UART调试口的配置集中放在一起每换一个芯片平台只需要看这一个函数。移植到APM32F072时我特意把AF编号打印出来验证了一遍PA11/PA12 配置为AF1对应USB功能PB8/PB9 配置为AF4对应CAN功能调试串口USART1的PA9/PA10配置为AF1对应USART功能3.4 USB描述符与设备枚举让电脑把它认成Leaf LightUSB描述符是整个兼容方案里最微妙的部分。moonglow固件里的设备描述符中带有Kvaser的VID和PID所以Windows会把它归到Kvaser设备类别加载对应驱动。USB字符串描述符里的厂商名、产品名也按照Kvaser Leaf Light的格式填好了。这里有一个正经的合规提醒这套固件本质上是逆向兼容协议仅供个人学习研究、降低开发调试成本不建议做成产品去商用。自己实验室用、做课题、做工具替代完全没问题。枚举流程中还有一个容易被忽略的地方USB设备的D上拉必须在配置好时钟之后打开。如果在USB外设时钟还没稳定时就拉高D主机端就会收到一个无法正常响应的设备导致枚举失败。好的固件会在SystemInit完成、时钟稳定之后才去设置USB_BCR里的上拉控制位。移植后第一次上电设备管理器里如果出现未知设备或者设备描述符请求失败不要急着怀疑驱动先回来检查时钟配置和D上拉顺序这两个原因占了USB枚举失败的大头。4. 烧录上电与PC端驱动配合识别只是第一步4.1 烧录方式与工具选择APM32F072支持SWD和串口ISP两种烧录方式。SWD调试口是标准的Cortex-M0调试接口用DAP-Link、J-Link或者ST-Link都可能连上。个人推荐用一个CMSIS-DAP的调试器几十块钱兼容性很好。OpenOCD配合DAP-Link烧录bin文件的命令大致是openocd -f interface/cmsis-dap.cfg -f target/stm32f0x.cfg \ -c program firmware.bin 0x08000000 verify reset exit如果手头没有SWD调试器APM32F072内置了系统bootloader通过USART1也能下载程序只不过需要一个手动进入boot模式的过程。有调试器的话走SWD最省事。4.2 Kvaser驱动安装为什么建议用官方驱动固件烧好、上电之后Windows会尝试给设备装驱动。因为我们复刻的是Kvaser Leaf Light的VID/PID所以系统会自动匹配到Kvaser官方驱动不需要自己折腾inf文件。直接去Kvaser官网下载对应版本驱动装好后在设备管理器里能看到“Kvaser Leaf Light”相关的设备项这说明USB枚举和协议握手已经过了第一关。这里有个实操建议不一定非要装最新版驱动。新版驱动有时会对协议兼容性做更严格校验反而可能让克隆固件罢工。如果你装最新版驱动后设备虽然被识别但始终无法打开通道可以考虑装历史版本驱动再试试很多玩兼容固件的老外也这样操作。4.3 第一轮验证设备管理器里的关键信息装上驱动后打开设备管理器在通用串行总线控制器或者Kvaser分类下找到设备右键看属性状态显示“这个设备工作正常”说明USB通信链路已经跑通。到这里只能说“枚举成功 驱动加载成功”CAN功能还是未知数。打开Kvaser CANKing看一下设备列表。如果软件能列出这个通道且没有报错就可以创建一个虚拟通道或者直接开始CAN测试。但我的建议是先用回环方式把硬件路径测一遍再上真实总线别一上来就把高压器件接上。5. CAN收发实测与完整排查链路5.1 回环测试和双机对测拿到固件后第一件事我先做控制器内部回环测试把CAN控制器的环回模式打开在CANKing上发一帧看能不能收到。这个测试的意义在于验证CAN控制器寄存器配置、中断处理和USB接收队列是否正常。回环通过后再做一次外部物理回环把PCB上CAN收发器的CAN_H和CAN_L两个引脚短接然后正常发送报文。因为是自己回环报文发出去后会从CAN收发器接收端回到控制器这时候如果还能收到说明CAN收发器的工作状态至少在物理层没问题。真正有说服力的是双机对测。我手头有两块APM32F072小板都刷了同一份moonglow固件一块连电脑当调试工具另一块接在总线上当普通CAN节点。一端用CANKing发周期报文另一端收到后原样回一帧两边都能看到记录整个链路才算真正打通。5.2 CAN位定时参数的计算与设置在Kvaser软件里选择波特率驱动会在后台计算好位定时参数并下发给设备。这些参数最终要写入CAN_BTR寄存器公式是波特率 CAN时钟频率 / ((1 TS1 TS2) * BRP)以48MHz的CAN时钟、目标1Mbps为例可以这样配BRP预分频取2此时有效时钟是24MHz需要总时间量子数等于24也就是1 TS1 TS2 24。取TS116、TS27采样点约为70.8%满足CAN通信常用范围。移植moonglow固件时位定时计算逻辑一般在固件内部或者驱动下发参数时已经处理好了用户不需要在代码里写死波特率。但移植后如果遇到“上位机选了1M但总线上却经常报错”的情况就要怀疑是不是固件解析驱动下发的位定时参数时出了偏差这时候把CAN_BTR寄存器读出来和公式算出的理论值对照排查起来很快。5.3 我踩过的坑和排查思路整个过程最折磨人的一次问题插上USB后设备管理器能识别Kvaser Leaf Light驱动也显示正常但CANKing打开通道时一直提示设备应答超时。看起来是枚举成功但协议层握手没有走通。排查思路是这样的第一先排除上位机和驱动问题。把同样的固件烧到朋友的ST板子上发现问题依旧说明问题不在APM32本身大概率是固件某个功能被我改动了。第二对比我改动过的部分最后锁定了GPIO初始化顺序和CAN外设时钟使能的先后关系。原来代码里把CAN外设时钟使能放在了GPIO配置之后当时钟使能还没打开时就先写了CAN_BTR寄存器写操作被硬件忽略了导致设备虽然能枚举但CAN控制器根本没有进入初始化状态。调整顺序后问题立刻消失。还有一个和终端电阻有关的教训。CAN总线上需要两个120欧终端电阻分别放在总线两端。如果你只是把CAN收发器的CAN_H和CAN_L直接和另一个收发器对接没有加终端电阻小数据量测试可能侥幸能通但波特率一高或者报文一多波形反射会让通信极不稳定。建议调试初期就养成良好的习惯板上预留120欧电阻位置测试时把总线的两个端点都终端匹配到位。6. 移植之后还能往哪个方向折腾固件跑通之后别急着收工。有几个方向我觉得很值得继续玩。第一把固件和APM32F072的SDK更深度地结合起来。比如增加一个串口命令行接口可以在PC上用串口查看CAN总线的实时状态、调试信息、甚至动态修改USB描述符非常方便。第二增加错误统计和报文过滤功能。Kvaser兼容链路已经很稳定在此基础上可以扩展固件能力比如统计总线错误帧、在固件层做硬件过滤、记录掉帧率这些在上位机端做会占用USB带宽放在设备端效率更高。第三把硬件设计开源化。单芯片方案最便宜的BOM甚至可以把整体成本压到三十元以内做完之后整理一份原理图、PCB、外壳设计方案相当于给自己留下一套低成本CAN调试工具的完整方案。最后分享一个我自己的习惯每个版本编译出来的固件bin文件都会加一个带日期的文件名存档烧录之前先看一眼Flash占用和栈顶地址确认固件不超Size。很多时候排查“奇怪问题”到最后发现就是自己上一次烧错了版本。嵌入式开发这事儿很多时候瓶颈不是技术而是流程和习惯。
返回列表