3. STM32 FDCAN 外设概览:从 G4 到 H7 的实战视角

好,咱们进入正题。这一章我打算把 STM32 的 FDCAN 外设掰开揉碎讲清楚。你可能会问,不就是个 CAN 控制器吗,有什么好讲的?

其实不然。FDCAN 和传统 bxCAN 差别很大,尤其是消息 RAM 和过滤器的设计思路,完全换了一套玩法。我当年从 F1 切到 G4 时,第一反应是——这寄存器怎么变这么少?后来才发现,原来都藏到 RAM 里去了。

3.1 STM32G4/H7 系列 FDCAN 模块

STM32 的 FDCAN 模块基于 Bosch M_CAN 内核,G4 和 H7 系列都搭载了这个 IP。但两者还是有些细微差别,我列个表给你看:

特性 STM32G4 STM32H7
FDCAN 实例数 1 个(FDCAN1) 2 个(FDCAN1/FDCAN2)
消息 RAM 大小 2.5 KB 10 KB
支持 CAN FD 是(最高 8 Mbps) 是(最高 8 Mbps)
Rx/Tx FIFO 各 3 个 各 3 个
专用 Tx 缓冲 最多 32 个 最多 32 个
硬件时间戳 16 位 32 位

看到没?H7 的消息 RAM 大了整整 4 倍。我在做多节点数据采集项目时,G4 的 2.5 KB 勉强够用,但 H7 就从容多了。如果你要处理大量 ID 过滤或长报文缓存,选 H7 会省心不少。

我的习惯: 项目初期先估算需要的 Rx FIFO 深度和 Tx 缓冲数量,再反推消息 RAM 够不够。别等到调通了才发现 RAM 溢出,那滋味不好受。

3.2 FDCAN 内核架构

FDCAN 的内核分三层,我画了张图帮你理解:

FDCAN 内核架构 CPU 接口层 寄存器访问 · 中断控制 · 时钟管理 消息处理层 Tx 调度 · Rx 过滤 · 消息 RAM 管理 时间戳 · 错误状态机 CAN 协议层 位时序 · CRC · 仲裁 · 错误检测 CAN 总线(CANH/CANL) 关键说明 • 消息 RAM 位于 消息处理层 • 过滤器在接收路径 上实时匹配 • Tx 事件 FIFO 记录 发送完成时间戳 • 错误状态机独立

这张图我建议你多看两眼。CPU 接口层负责你写的寄存器操作,消息处理层是核心调度器,CAN 协议层管物理层时序。三层之间通过内部总线通信,你基本不用操心。

我遇到过一个问题:某次调试时 FDCAN 一直报总线关闭,查了半天发现是位时序配置错了。协议层对时钟精度要求很高,尤其是 CAN FD 的高速数据段,采样点位置差一点就翻车。

3.3 消息 RAM 布局

消息 RAM 是 FDCAN 的「记忆体」。所有待发送、已接收、待过滤的报文都放在这里。布局是固定的,但你可以通过寄存器配置每个区域的大小。

标准布局如下:

区域 起始偏移 说明
Rx FIFO 0 0x000 接收 FIFO 0,优先级最高
Rx FIFO 1 可配置 接收 FIFO 1,优先级次之
Rx 缓冲 可配置 专用接收缓冲,用于特定 ID
Tx 事件 FIFO 可配置 记录发送完成的时间戳
Tx 缓冲 可配置 待发送报文,支持优先级调度
Tx FIFO/队列 可配置 按顺序发送的队列模式
注意: 消息 RAM 的总大小是固定的。你给 Rx FIFO 分配多了,Tx 缓冲就得少。我曾经在 G4 上把 Rx FIFO 0 设成 64 个元素,结果 Tx 缓冲只剩 4 个,发送稍微一多就丢帧。后来改成 32 个 Rx + 16 个 Tx,才稳住。

配置消息 RAM 时,我建议按这个顺序来:

  1. 先确定需要的 Rx FIFO 深度(一般 16-32 个元素够用)
  2. 再算 Tx 缓冲数量(根据应用层发送频率)
  3. 最后看要不要 Tx 事件 FIFO(调试时很有用)
  4. 剩下的空间给 Rx 缓冲(非必须)

3.4 过滤器配置

FDCAN 的过滤器比传统 bxCAN 灵活得多。它支持两种模式:

  • 经典过滤: 和 bxCAN 一样,用 ID + 掩码匹配
  • 范围过滤: 指定一个 ID 范围,落在区间内的都接收

我个人更喜欢范围过滤。为什么?因为经典过滤的掩码位操作容易把人绕晕。你想想看,要过滤 0x100-0x1FF 这 256 个 ID,经典过滤得算掩码,范围过滤直接写起始和结束 ID 就行。

过滤器配置的步骤:

  1. 设置过滤器标准(11 位或 29 位 ID)
  2. 选择过滤模式(经典或范围)
  3. 配置过滤元素(ID + 掩码,或起始/结束 ID)
  4. 关联到 Rx FIFO 0 或 Rx FIFO 1

避坑指南: 我曾经把过滤器配成了「拒绝所有」,结果总线上一帧数据都收不到。查了两天才发现是过滤元素配置反了——起始 ID 大于结束 ID,范围过滤直接失效。记住,起始 ID 必须小于等于结束 ID。

3.5 中断与标志位

FDCAN 的中断系统很丰富,但别被吓到。常用的就这几个:

中断源 标志位 触发条件
Rx FIFO 0 新消息 RF0N FIFO 0 收到新报文
Rx FIFO 1 新消息 RF1N FIFO 1 收到新报文
Tx 完成 TC 某 Tx 缓冲发送完成
总线错误 EW / EP / BO 错误状态变化
消息 RAM 溢出 RF0L / RF1L FIFO 满时新报文被丢弃

我的做法是:接收用中断,发送用轮询。为什么?接收不及时会丢帧,中断响应最快。发送嘛,应用层自己知道什么时候发,轮询更可控。

中断处理函数里,记得先读中断标志位,再清标志,最后处理数据。顺序错了容易漏中断。嗯,这个坑我也踩过。

小技巧: 调试阶段把总线错误中断打开,一旦出现 EW(错误警告)或 EP(错误被动),立刻打印日志。这样能快速定位物理层问题,比如终端电阻没焊、CANH/CANL 接反了。