ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

STM32H7 FDCAN到底支不支持DMA?一文说透RX与TX的真相

STM32H7 FDCAN到底支不支持DMA?一文说透RX与TX的真相 FDCAN支不支持DMA这个问题基本每隔一阵子就会在H7的交流群里看到一次。你直接搜STM32H753 FDCAN DMA能搜到一大堆互相矛盾的答案有人说可以有人说官方压根没支持。实际上两边都没全错接收方向硬件上确实有DMA通道可用发送方向你真找不出一个“把报文自动搬到邮箱再触发发送”的DMA机制。要理解这件事得先看看FDCAN这个外设和传统CAN控制器到底差在哪。这篇文章我会从FDCAN的内核架构讲起把RX/TX两条路径拆开分析然后给出一套能落地的RX DMA配置方案以及发送侧更实用的替代方案最后把调FDCAN时最容易踩的坑整理成速查表。无论你是在做CANFD高负载总线、运动控制还是想把H7现有的CAN中断改成DMA这篇都应该能帮你少走点弯路。1. 先搞清楚FDCAN到底是个什么结构1.1 FDCAN和传统CAN控制器最大的区别H753上的FDCAN用的是博世M_CAN内核这玩意和STM32F1/F4那一代bxCAN的架构差别非常大。bxCAN的做法很直白固定几个发送邮箱、几个接收邮箱你往邮箱寄存器写数据就行。M_CAN完全不是这个套路它把标准过滤、扩展过滤、RX FIFO、RX Buffer、TX Buffer、TX Event FIFO全部放在一段私有RAM里——H753每个FDCAN都有大概10KB的Message RAM通过一堆寄存器SIDFC、XIDFC、RXF0C、RXF1C、TXBC、TXEFC这些来配置每个区域的起始地址和大小。这个架构带来的直接结果就是报文收发本质上都是CPU或DMA跟Message RAM打交道而不是操作几个看似“收发邮箱”的寄存器。很多人在H7上第一次调FDCAN会用老思路去找类似CAN_TxData之类的寄存器结果发现根本不存在然后就开始头疼。实际上FDCAN的工作流程是总线上来一帧报文硬件先做ID过滤然后把整帧数据按配置存进RX FIFO0、RX FIFO1或者RX Buffer区发送的时候则需要CPU先往TX Buffer区里写好报文内容再往TXBAR寄存器写一个触发位硬件才真正开始往总线上发。整个过程里Message RAM就是唯一的“数据中转站”。理解了这一点后面再谈DMA就顺理成章了。1.2 Message RAM里到底放了什么用HAL库配置的时候很多人其实不太清楚自己到底在配些什么。就拿初始化结构体里那几个字段来说StdFiltersNbr、ExtFiltersNbr、RxFifo0ElmtsNbr、TxBuffersNbr、TxEventsNbr这些全是在划分Message RAM的布局。RX FIFO0区域就是接收报文暂存区里面每个“元素”除了报文数据本身还带了一个4字节的Header。Header里放着ID、DLC、BRS、ESI、Timestamp等信息数据区最长可以到64字节。所以一个FDCAN接收元素在RAM里占的空间不是简单的“DLC字节数”而是Header加Data一起按8字节对齐后的长度。我见过不少人在DMA里把长度写死成8字节或者16字节结果要么只搬到半个报文要么把相邻元素也搬进来了很坑。TX Buffer区就是发送邮箱的“Pro版”H753的FDCAN可以配置多个Dedicated TX Buffer每个Buffer都是独立的一段RAM空间。你可以往Buffer0写一帧、Buffer1写另一帧然后通过一次写TXBAR同时请求多个Buffer发送硬件会按ID优先级自动排。对高实时性应用来说这个能力比老的bxCAN邮箱灵活太多。TX Event FIFO则是用来记录发送事件的。每发完一帧硬件可以顺手往这个FIFO里写一条事件记录包含时间戳、Buffer编号、Message Marker等用来确认“这帧确实已经发出去了”。后面讲DMA的时候TX Event FIFO这个点会再次出现。1.3 为什么“支不支持DMA”不能一句话回答很多人的直觉是既然UART、SPI都有DMA那我FDCAN把DMA接上去应该就能像串口一样自动收发了。但CAN和UART的差异是本质性的。UART是字节流DMA只要在RXNE触发时把寄存器里的一个字节搬到内存搬完一个接一个数据天然连续DMA很好干。CAN是消息级的一条报文在总线上是一整帧DMA没法直接“看到”帧边界。M_CAN的做法是先把帧收进FIFO再产生DMA请求让DMA把FIFO里已经固化好的一条元素搬出去。也就是说DMA搬的不是“总线上正在到来的比特流”而是“已经完整存在的消息元素”。发送方向的逻辑更复杂。你要发一帧需要先构造Header和数据明确缓冲区号再请求发送。这个“构造”和“请求”是CPU的活硬件没有一个“发送完成触发DMA自动补下一帧”的机制。所以官方没有给FDCAN的TX Buffer做DMA通道这是M_CAN的设计选择不是什么阉割。2. DMA请求是怎么连到FDCAN的2.1 最核心的结论RX有DMA请求TX没有我把H7参考手册里FDCAN DMA相关段落翻来覆去看了很多遍总结下来就一句话FDCAN的DMA请求只在两种情况产生——当RX FIFO里存好了一条新消息或者TX Event FIFO里存好了一条发送事件。具体到H7的DMAMUX请求映射表你能看到FDCAN1_RX、FDCAN1_TX、FDCAN2_RX、FDCAN2_TX这几个DMA请求号。这里特别容易误解的就是FDCANx_TX这个信号。乍一看名字还以为是“FDCAN发送DMA请求”其实它对应的功能是把TX Event FIFO里的事件记录搬到内存方便软件确认发送结果并不是帮你把报文字节搬进TX Buffer再触发发送。换句话说接收方向真的有DMA搬运发送方向压根没有官方DMA通道只有“发送事件记录”的DMA搬运。这里有个很关键的点需要理清楚即便有了FDCAN1_RX这个DMA请求也不代表DMA会像串口那样无脑循环工作。FDCAN产生DMA请求的节奏是“每收到一条消息请求一次”。DMA搬完一条后需要准备好接收下一次请求。如果你把DMA配置成循环模式数据会不会错位会因为FIFO里每条元素的长度和内容不是固定大小的循环模式下DMA不知道帧边界在哪。所以FDCAN的RX DMA一般要用Normal模式一次搬一条消息搬完在中断里重新启动。2.2 和串口、SPI、ADC的DMA差异聊到这儿不妨把几种常见外设的DMA逻辑放一起对比一下这样你以后遇到类似疑问也容易判断。外设DMA触发源DMA搬的内容是否天然适合DMAUART RX每收到一个字节触发RXNE从数据寄存器搬一个字节天然适合连续字节流UART TXTXE标志触发从内存搬一个字节到数据寄存器天然适合SPI RX/TX每次传输完成触发从/到数据寄存器搬一个word天然适合ADC多通道扫描每次转换完成触发从数据寄存器搬一个word天然适合配合循环模式很爽FDCAN RXFIFO收到一条消息后触发从Message RAM搬一个完整元素可行但不算优雅需自行解析帧FDCAN TX没有针对TX Buffer的DMA请求无官方不支持很多人在STM32F0/F1/G4上做过“串口DMA接收空闲中断”或者在G4上用DMA循环采样ADC很自然地想把那套思路搬到FDCAN上。但实际上UART/ADC的DMA之所以好用是因为数据是定长的、连续的、源地址自增或者固定寄存器即可FDCAN的数据是变长的、带Header的DMA只负责搬运不负责理解帧格式。所以FDCAN的DMA更像是一种“硬件辅助拷贝”而不是“智能报文引擎”。2.3 DMAMUX1请求映射H753里怎么找FDCAN信号H7系列用的是DMAMUX1来把外设请求路由到DMA1或者DMA2的各个Stream/Channel。你在CubeMX里配置FDCAN的DMA时实际看到的不一定会是FDCAN1_RX这种直白名字而是会显示成“FDCAN1_RX”之类的请求源只要你选了DMA1或DMA2的某个流并指定请求源为FDCAN1_RXCubeMX会自动帮你配置DMAMUX的选择寄存器。这里我要特别提醒一下H7的DMA和F103那种还不太一样它没有“通道”这个说法的独占概念同一个DMA流可以被不同外设请求源复用靠DMAMUX来切换。所以在调FDCAN DMA时一定要确认DMAMUX的请求源确实是FDCAN1_RX而不是某个别的外设。CubeMX如果自动生成代码一般不会错但手动改代码时容易翻车。3. RX方向DMA配置实战能直接用3.1 CubeMX里的配置FDCAN DMA 中断既然硬件
返回列表