ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32H7 FDCAN兼容经典CAN:时钟、位时序与完整工程配置实战

STM32H7 FDCAN兼容经典CAN:时钟、位时序与完整工程配置实战 简介STM32H7 FDCAN与CAN兼容完整工程面向使用意法半导体Cortex-M7内核、需要在传统CAN与CAN-FD通信间平滑迁移的嵌入式开发者与单片机工程师。工程基于STM32CubeMX完成引脚分配、时钟、中断优先级及滤波器配置并提供CAN_AudioCard_TEST音频卡测试程序可验证总线收发、数据流与错误处理。资源共344个文件以HAL库头文件h、源文件c、编译中间文件o/crf以及可执行hex/axf、工程配置ioc等为主打包后约25.95MB。已有4198人学习下载。深入研读该工程能够掌握FDCAN位速率设定、滤波器配置与中断处理理解CAN-FD协议中高速数据段与低速标识段的差异同时结合音频流传输场景学习如何通过CAN总线集成设备控制与上层通信还可借助错误帧与状态机管理排查总线异常并为实际项目中优化CAN通信可靠性提供直接参考。 前阵子做项目主控从F103直接跳到STM32H7然而现场还有上百个老的CAN 2.0节点。本以为H7的FDCAN外设就是“高级版CAN”结果一上手才发现FDCAN和经典CAN的兼容不是你打开CubeMX勾个选项就完事的。时钟树怎么挂、位时序怎么算、消息RAM怎么分、滤波器怎么配哪一步错了总线上都是错误帧。这篇就把我这套STM32H7 FDCAN与CAN兼容完整工程的搭建过程、计算方法和实测中踩过的坑一次性整理出来给正在做平台升级或打算引入CAN FD的开发者做个参考。1. 为什么FDCAN满天飞我还是要做经典CAN兼容FDCAN这个外设名字里带个“FD”看起来就是CAN FD的专用硬件。但很多项目换到STM32H7之后总线上的节点并没有跟着升级整车、工业设备、医疗仪器里存量最大的还是经典CAN 2.0节点。这个时候FDCAN能不能老老实实当CAN用直接决定项目能不能落地。1.1 CAN FD和经典CAN差的不只是数据场变大了很多人对CAN FD的理解停留在“数据场从8字节变成了64字节”实际上它的变化是系统性的。CAN FD在保留经典CAN的报文帧结构基础上引入了可变速率仲裁段继续用最高1Mbps的普通速率而数据段可以切到更高的速率实现数据场快速传输。同时CRC从15位升级为17位或21位还改了位填充规则降低误码率。项目经典CAN 2.0CAN FD数据场最大长度8字节64字节位速率整帧固定一般不超过1Mbps仲裁段最高1Mbps数据段可到数MbpsCRC15位17位/21位帧兼容性所有CAN节点可识别经典CAN节点无法识别FD帧这些差异意味着如果总线上出现一帧FD格式的报文老节点会直接把它当成错误帧甚至引发错误计数器飙升。所以只要你的总线里还有任何一个只支持CAN 2.0的老节点整个网络就必须按经典CAN模式运行FD能力只能暂时搁置。1.2 FDCAN外设本身就是个“双模控制器”STM32H7上的FDCAN内核是博世的M_CAN它天生支持两种方式。一种是把外设配置成FD模式收发CAN FD帧另一种是配置成经典CAN模式只收发经典帧。这一点非常关键FDCAN不是“只能做FD”而是一个可以向下兼容的控制器在经典模式下它生成的时序、帧格式、错误处理都和传统CAN控制器没有本质区别。实际工程里我更倾向于把它当成“一个支持经典CAN模式的高性能外设”来用。这样做的好处是即便以后总线上的老节点逐步淘汰我也不需要改硬件只需要改配置、改发送头的格式就能切换到CAN FD。硬件平台一次设计软件升级就能满足后续需求。1.3 哪些场景必须锁死Classic模式结合我做的项目下面几类场景基本逃不掉经典CAN兼容总线混网现场设备一部分是CAN FD控制器一部分是老CAN节点这时候全网络只能按最慢的节点跑发FD帧等于自己给自己找麻烦。固件升级场景如果升级包要经过老节点转发老节点根本解不了FD帧数据就断了。诊断协议很多诊断规范里虽然有CAN FD选项但实际产线工具还在用经典CAN诊断仪和ECU之间必须统一。所以决策很简单除非你能确认整条链路所有节点都支持FD否则就老老实实先把经典CAN模式跑通。2. STM32H7 FDCAN资源盘点先把底牌摸清写代码之前我习惯先把外设的资源边界搞清楚。H7的FDCAN和F1/F4时代的bxCAN有挺大差别尤其是消息RAM和中端路径很多老手第一次上手也会懵。2.1 实例数量、内核时钟和引脚路由以我常用的H743/H750为例芯片上有两个FDCAN实例分别是FDCAN1和FDCAN2。部分其他H7子型号的实例数量会有差异动手前一定要翻一下对应型号的数据手册别拿着默认外设列表就开干。每个FDCAN实例都有独立的引脚映射可以路由到不同的GPIO组合比如FDCAN1_RX/TX可以出现在PA11/PA12也可以出现在PB8/PB9等引脚。这块我在CubeMX里直接配重点注意一点同一组引脚不要和其他外设冲突尤其像PB8/PB9这种和I2C1复用的引脚焊好的板子飞线改不了的话调试时就很被动了。2.2 一块消息RAM怎么分给多个FDCANH7的FDCAN和经典CAN很大的一个区别是它没有再为每个邮箱分配固定的SRAM而是用一整块“消息RAM”来统一管理收发缓冲区。这块RAM可能被多个FDCAN实例共用不同型号大小不同。如果同时用FDCAN1和FDCAN2就必须给每个实例划分好发送缓冲区、接收FIFO、过滤器的地址范围。这块划分不是自动完成的。CubeMX在初始化时会生成一份配置但如果你手动改代码或者两个实例初始化顺序不对很可能出现FDCAN2把FDCAN1的数据覆盖掉的情况。后面第4章我会专门讲这个坑。2.3 中断路径不是你想的那样直接经典CAN的中断很简单一个CAN1_SCE、一个CAN1_RX进中断直接处理。FDCAN这边不一样每个实例有两根中断线命名类似FDCAN1_IT0、FDCAN1_IT1。你可以把不同的中断事件接收FIFO、错误状态、总线关闭等映射到不同的中断线上这样紧急事件和普通事件可以分开优先级处理。实际使用中我通常把接收FIFO事件挂在IT0把错误和总线关闭事件挂在IT1这样接收中断不会被错误事件频繁打断调试时也更容易定位问题。3. 完整工程搭建时钟、位时序、滤波器、收发一条龙这一章是真正的核心。说句实话CAN外设最麻烦的不是收发那几行代码而是接入总线之前所有参数都必须是“对”的。时钟不对位时序不对采样点不对上去就是错误帧。3.1 内核时钟到底挂哪个PLL填错就白跑STM32H7的FDCAN内核时钟不是直接从APB总线拿的而是来自一个独立的内核时钟源可选的来源包括PLL1Q、PLL2Q、HSE等。你必须在HAL_RCCEx_PeriphCLKConfig里明确选择否则即使APB时钟配置正确FDCAN也完全没反应。RCC_PeriphCLKInitTypeDef PeriphClkInit {0}; PeriphClkInit.PeriphClockSelection RCC_PERIPHCLK_FDCAN; PeriphClkInit.Fdcan1ClockSelection RCC_FDCANCLKSOURCE_PLL1Q; PeriphClkInit.Fdcan2ClockSelection RCC_FDCANCLKSOURCE_PLL1Q; HAL_RCCEx_PeriphCLKConfig(PeriphClkInit);这块我建议在工程最早期就验证用示波器量FDCAN_TX引脚的波形。如果TX引脚完全没波形多半就是内核时钟没配上。别问我怎么知道的我最早调试时在这卡了几乎一个下午回头一看CubeMX里FDCAN时钟源默认没选。3.2 位时序计算用40MHz算出500kbps的80%采样点很多人直接用CubeMX的自动计算功能生成位时序这当然可以但我建议你要能自己手算一遍因为实际调线缆长度、调兼容性时改的就是这几个数。FDCAN的位时间由四段组成同步段、传播时间段TSEG1、相位缓冲段TSEG2以及重同步跳转宽度SJW。计算公式是位时间tq数 1 TSEG1 TSEG2 采样点 (1 TSEG1) / (1 TSEG1 TSEG2)举个例子假设FDCAN内核时钟为40MHz目标波特率500kbps我想要80%采样点每个bit时间为2us。设BRP分频值为1对应内部实际2分频则tq 2 / 40MHz 50ns。2us / 50ns 40个tq所以 1 TSEG1 TSEG2 40。采样点80%TSEG1 31TSEG2 8SJW 4。CubeMX里对应的配置就是hfdcan1.Init.NominalPrescaler 1; hfdcan1.Init.NominalSyncJumpWidth 4; hfdcan1.Init.NominalTimeSeg1 31; hfdcan1.Init.NominalTimeSeg2 8;这里要注意HAL库的NominalPrescaler写的是分频值实际BRP是减一还是不减一在不同版本库里有差异最好的办法是用示波器配合USBCAN工具实测波特率微调分频参数不要盲信代码注释。关于采样点选择我的经验是如果总线上全是自家设备采样点放在80%或85%问题不大如果混网老节点多采样点适当前移比如75%对长线缆和收发器延迟的容忍度会更好。3.3 标准帧/扩展帧的滤波器配置FDCAN的滤波器分为标准ID滤波器和扩展ID滤波器支持两种模式Mask模式掩码匹配和List模式精确列表。做兼容工程时我习惯用Mask模式因为可以快速放行一整段ID。标准ID在FDCAN内部寄存器里不是直接放的需要左移18位这个细节很容易被忽略。例如放行ID为0x123、掩码为0x7FFFDCAN_FilterTypeDef sFilterConfig; sFilterConfig.IdType FDCAN_STANDARD_ID; sFilterConfig.FilterIndex 0; sFilterConfig.FilterType FDCAN_FILTER_MASK; sFilterConfig.FilterConfig FDCAN_FILTER_TO_RXFIFO0; sFilterConfig.FilterID1 (0x123U 18U); sFilterConfig.FilterID2 (0x7FFU 18U); HAL_FDCAN_ConfigFilter(hfdcan1, sFilterConfig);如果掩码写0x000那就等于所有ID都进FIFO。做前期调试时可以这样先全放行跑通了再收紧。实际项目中我通常把业务报文精确匹配到FIFO0把诊断报文全部放到FIFO1两个FIFO分开处理互不干扰。3.4 发送和接收代码怎么组织发送方面我不建议用单独的Tx Buffer模式做高频发送因为buffer不够用时就只能等。更稳妥的做法是配置成FIFO队列模式发消息时调用HAL_FDCAN_AddMessageToTxFifoQueue让硬件自己排队。FDCAN_TxHeaderTypeDef txHeader; uint8_t txData[8] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; txHeader.Identifier 0x123; txHeader.IdType FDCAN_STANDARD_ID; txHeader.TxFrameType FDCAN_DATA_FRAME; txHeader.DataLength FDCAN_DLC_BYTES_8; txHeader.BitRateSwitch FDCAN_BRS_OFF; txHeader.FDFormat FDCAN_CLASSIC_CAN; txHeader.TxEventFifoControl FDCAN_NO_TX_EVENTS; txHeader.MessageMarker 0; HAL_FDCAN_AddMessageToTxFifoQueue(hfdcan1, txHeader, txData);接收我开了FIFO0的新报文中断在回调里只做一件事把消息从FIFO搬出来放进自己的消息队列后续业务逻辑在任务里处理不在中断里做耗时操作。void HAL_FDCAN_RxFifo0MsgPendingCallback(FDCAN_HandleTypeDef *hfdcan) { FDCAN_RxHeaderTypeDef rxHeader; uint8_t rxData[8]; if (hfdcan-Instance FDCAN1) { if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, rxHeader, rxData) HAL_OK) { // 把ID和数据入队或者置标志位 } } }如果你用的是FreeRTOS回调里用xQueueSendFromISR入队然后portYIELD_FROM_ISR通知任务。裸机环境的话置一个标志位或者用简单的环形队列都行。4. 混网实测中踩过的坑和完整的排查链路配置再多不如实测一次来得真实。这一章我挑四个我实际踩过的坑每个都说清楚现象、排查过程和最终解决方式。4.1 采样点设得太激进长线开始狂刷错误帧第一次把采样点设到87.5%总线上挂一个USBCAN盒子和一个老设备短距离测试一切正常。后来把线延长到大概10米跑了一段时间错误帧开始周期性出现。一开始以为是终端电阻问题后来用CAN盒统计错误帧发现错误集中在某个相位附近。排查下来问题出在采样点太靠后采样时刻落在信号振铃区间导致误判。解决方式是把采样点从87.5%下调到80%并且把SJW从2加大到4增强重同步能力。调整之后同一根线缆连续跑12小时错误帧计数为0。这里也提醒各位采样点不是越高越好也不是越靠前越好要根据实际线缆质量和节点数量调整。80%是一个我实测下来比较均衡的值。4.2 收发器的STB引脚悬空只能收不能发有块板子用的TJA1044收发器板子焊完调试时发现一个诡异现象能收到总线上的数据但自己发出去的帧一个都上不了总线对方完全收不到。用示波器测量MCU的FDCAN_TX引脚波形正常再量收发器总线侧几乎没波形输出。查原理图才发现TJA1044的STB引脚悬空了这个引脚默认电平是高的而高电平恰好是静默模式收发器在这个模式下只能接收、不能正常驱动总线。解决办法很简单把STB引脚拉低到正常模式问题立刻消失。这个坑对新手很隐蔽因为很多开发板上收发器的STB已经固定拉低不会出问题但自己画板子的时候如果照抄参考设计漏掉这个上拉或下拉就会出现“收得到发不出”的情况。4.3 两个FDCAN实例共用消息RAM初始化顺序反了H7的部分型号中FDCAN1和FDCAN2共享一块消息RAM。如果两个实例的RAM偏移没有显式划分后初始化的那个会把先初始化的配置覆盖掉。我遇到的现象是FDCAN1单独跑没问题FDCAN2初始化后FDCAN1突然收不到数据了。排查方法比较直接在初始化FDCAN2之前先读出FDCAN1的滤波器配置发现已经被覆盖成FDCAN2的内容。解决方式是调用HAL_FDCAN_ConfigRamOffsets给两个实例分别划分发送缓冲区、接收FIFO和过滤器区域让它们各用各的地址空间。用CubeMX生成工程时这一步通常会自动处理。一旦你改成手动初始化或者从旧工程移植过来就必须重新确认。4.4 Bus Off之后回不来了得靠自己拉一把总线关闭Bus Off在强干扰环境下不算罕见。FDCAN进Bus Off后会停止收发如果应用不及时恢复整个节点就相当于掉线了。HAL库有错误状态回调我在里面加了恢复逻辑void HAL_FDCAN_ErrorStatusCallback(FDCAN_HandleTypeDef *hfdcan) { if (hfdcan-ErrorStatus FDCAN_ERROR_BUS_OFF) { HAL_FDCAN_Restart(hfdcan); } }建议在恢复之前读取一下FDCAN的错误计数器TEC/REC确认确实是总线关闭而不是其他错误。恢复逻辑做完之后还要在外层加一个看门狗或者状态机监控防止反复Bus Off导致系统行为异常。5. 压力测试数据与现在固定的调试习惯工程跑通只是开始稳定才是交付的前提。最后分享一些实测数据和我在实践里沉淀下来的习惯。5.1 用周立功盒子混网连续跑了一夜的结果我这边测试用周立功USBCAN-II连接总线把STM32H7的FDCAN1配置成经典CAN模式波特率500kbps、采样点80%和几个老CAN节点混网运行。测试脚本让H7以1ms周期发送两个标准帧同时从总线上接收所有报文并校验ID和数据。连续跑12小时错误帧计数为0FIFO溢出计数为0最大接收延迟也满足要求。后续我又在CAN FD数据段做过一次4Mbps的吞吐测试测试对象换成支持FD的收发器和USBCANFD盒子。64字节报文在数据段提速后的单帧时间比经典CAN拆8帧发送要短得多但这类场景必须是全链路FD才行只要有一个经典CAN节点性能优势就得放弃。5.2 中断优先级与LwIP/串口共存的经验如果在STM32H7上同时跑LwIP和CAN中断优先级设计会影响整体稳定性。我把以太网中断优先级设得较高FDCAN接收中断次之串口中断再次之。FDCAN接收中断回调里不调用任何阻塞函数只做入队操作避免中断内耗时过长挤占以太网中断。另外一个容易忽略的点CAN FIFO溢出问题。如果业务任务处理不及时接收FIFO满了之后新报文会直接丢弃但不会导致总线错误。我通过开启FIFO满中断在溢出时把这个事件记录下来方便事后排查是任务调度问题还是突发流量过大。5.3 我后来固定下来的工程习惯踩过这些坑之后我现在拿到H7的FDCAN工程第一步不是写业务代码而是按顺序做三件事先确认FDCAN内核时钟源再手算一遍位时序和采样点最后用回环模式跑通自发自收。这三件事没问题了才去接真实总线。回环测试有现成的配置开关把FDCAN模式改成FDCAN_MODE_INTERNAL_LOOPBACK不需要接外部设备就能验证驱动和中断链路非常适合新板子BringUp阶段。最后再提一句如果你需要解析CAN矩阵里的信号记得先确认DBC文件的字节序和位序定义是Intel还是Motorola。我见过不少项目硬件通讯正常但数据解析出来是乱的最后排查发现是矩阵字节序没对齐这已经不是FDCAN外设的问题而是整个数据链路的最后一公里。先把底层跑稳再处理上层解析可以减少很多不必要的返工。本文还有配套的精品资源点击获取
返回列表