3. STM32 CANFD外设架构:FDCAN内核介绍、FDCAN消息RAM管理、FDCAN中断与标志位、FDCAN时钟配置
好,咱们进入正题。这一章我打算把 STM32 的 FDCAN 外设架构拆开揉碎了讲给你听。说实话,我刚接触 FDCAN 的时候,也被那复杂的消息 RAM 和一堆中断标志位搞得有点晕。但后来我发现,只要理清了内核、存储和中断这三条线,剩下的就是水到渠成的事。
3.1 FDCAN内核介绍
FDCAN 内核,说白了就是 CANFD 协议的硬件实现核心。STM32 用的这个内核是基于 Bosch M_CAN 的,这是个非常成熟的 IP 核。我个人习惯把 FDCAN 内核想象成一个独立的小处理器——它有自己的状态机、自己的 RAM、自己的中断控制器。
内核主要分三块:
- 协议控制器:负责处理 CANFD 帧的收发、位时序、错误检测。说白了就是跟总线打交道的那部分。
- 消息处理单元:负责把收到的报文存到 RAM 里,或者从 RAM 里取出要发送的报文。这是内核的“大脑”。
- 配置寄存器组:我们通过操作这些寄存器来告诉内核怎么工作。
我在项目中遇到过一个问题:同样的 FDCAN 配置,在 STM32H7 上跑得好好的,换到 G4 上就不行了。后来发现是内核版本有细微差异——H7 用的是 M_CAN v3.x,G4 用的是 v2.x。所以啊,别以为所有 STM32 的 FDCAN 都一样,还是要看参考手册。
核心要点:FDCAN 内核的工作模式有三种——经典 CAN 模式、CANFD 模式(带 BRS)和 CANFD 模式(不带 BRS)。我个人建议,只要总线上的节点都支持,就开启 BRS,不然你买 FDCAN 干嘛?
3.2 FDCAN消息RAM管理
嗯,这里要重点讲。FDCAN 的消息 RAM 是很多人踩坑的地方。
STM32 的 FDCAN 内部有一块专用的 SRAM,大小通常是 2.5KB 或者 10KB(具体看型号)。这块 RAM 不是让你随便用的,它被划分成了几个固定的区域:
| 区域 | 用途 | 大小(典型值) |
|---|---|---|
| Rx FIFO 0 | 接收缓冲区 0 | 可配置 |
| Rx FIFO 1 | 接收缓冲区 1 | 可配置 |
| Rx Buffer | 专用接收缓冲区 | 可配置 |
| Tx Event FIFO | 发送事件记录 | 可配置 |
| Tx Buffer | 发送缓冲区 | 可配置 |
| Tx FIFO/Queue | 发送队列 | 可配置 |
你可能会问:为什么搞这么复杂?其实是为了灵活性。比如,你可以把高优先级报文放到 Rx Buffer 里,用硬件直接路由;普通报文走 FIFO,按顺序处理。
避坑指南:我曾经在配置消息 RAM 时,把 Rx FIFO 和 Tx Buffer 的总大小算错了,导致程序跑起来后,发送几帧就卡死。后来才发现是 RAM 溢出,后面的配置覆盖了前面的。记住:所有区域的大小加起来不能超过总 RAM 大小!
配置消息 RAM 的步骤,我一般这么干:
- 先算好需要多少个 Rx FIFO 元素、多少个 Tx Buffer 元素。
- 每个元素的大小:经典 CAN 是 18 字节,CANFD 是 72 字节(含 64 字节数据)。
- 用
FDCAN_RxFifoConfig()和FDCAN_TxBufferConfig()设置起始地址和大小。 - 检查
FDCAN_GetRAMUsedSize()是否超过总 RAM。
/* 消息RAM配置示例 */
FDCAN_RxHeaderTypeDef RxHeader;
FDCAN_TxHeaderTypeDef TxHeader;
/* 配置Rx FIFO 0:起始地址0,深度32个元素 */
hfdcan1.RxBuffersNbr = 0; /* 不使用专用Rx Buffer */
hfdcan1.RxFifo0ElmtsNbr = 32; /* FIFO 0 深度 */
hfdcan1.RxFifo0ElmtSize = FDCAN_DATA_BYTES_64; /* CANFD 64字节数据 */
/* 配置Tx Buffer:起始地址自动计算,深度8个元素 */
hfdcan1.TxElmtsNbr = 8;
hfdcan1.TxElmtSize = FDCAN_DATA_BYTES_64;
hfdcan1.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; /* 使用FIFO模式 */
HAL_FDCAN_Init(&hfdcan1);
我的小技巧:如果你不确定 RAM 怎么分配,可以用 STM32CubeMX 的图形化配置工具。它会自动计算剩余空间,还会用颜色标出是否溢出。我刚开始学的时候,全靠这个工具帮我避坑。
3.3 FDCAN中断与标志位
FDCAN 的中断系统,说实话,比经典 CAN 复杂了不少。但别怕,咱们一条一条理清楚。
FDCAN 的中断源主要分这几类:
- 接收中断:Rx FIFO 有新消息、Rx Buffer 有新消息、消息丢失等。
- 发送中断:Tx Buffer 发送完成、Tx FIFO 发送完成、发送取消等。
- 错误中断:总线错误、协议错误、错误状态变化等。
- 状态中断:时钟校准、时间戳溢出等。
每个中断源都有对应的标志位寄存器(IR)和使能寄存器(IE)。我习惯的做法是:先使能需要的中断,然后在中断服务函数里读 IR 寄存器,判断是哪个事件触发的。
/* 使能接收中断 */
HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0);
/* 中断服务函数 */
void FDCAN1_IT0_IRQHandler(void)
{
HAL_FDCAN_IRQHandler(&hfdcan1);
}
/* HAL库的回调函数 */
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
FDCAN_RxHeaderTypeDef RxHeader;
uint8_t RxData[64];
if(RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE)
{
HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxHeader, RxData);
/* 处理接收到的数据 */
}
}
你可能会问:为什么有两个中断线(IT0 和 IT1)?这是为了把不同优先级的中断分开。比如,把错误中断放到 IT1,接收中断放到 IT0。这样高优先级的接收不会被错误处理阻塞。
经验之谈:我在做车载项目时,把所有的 FDCAN 中断都扔到了同一个中断线。结果有一次总线出现大量错误,中断服务函数里一直在处理错误,导致接收丢帧。后来我把错误中断单独拎出来,用轮询方式处理,问题就解决了。
3.4 FDCAN时钟配置
时钟配置是 FDCAN 能跑多快的决定性因素。FDCAN 的时钟源通常有两个选择:
- APB 时钟(通常是 APB1 或 APB2)
- 外部时钟(从某个引脚输入)
我个人习惯用 APB 时钟,因为省事。但要注意:FDCAN 的时钟频率决定了它能支持的最高比特率。比如,STM32H7 的 FDCAN 时钟最高可以到 80MHz,那理论上数据段比特率可以跑到 8Mbps。
时钟配置的核心是位时序(Bit Timing)。FDCAN 的位时间由三部分组成:
- 同步段(Sync Seg):固定 1 个时间量子(tq)
- 传播段(Prop Seg):可配置,1-8 tq
- 相位缓冲段(Phase Seg1 + Phase Seg2):可配置,各 1-8 tq
采样点位置 = (Sync + Prop + Phase1) / (Sync + Prop + Phase1 + Phase2)。我一般把采样点设在 75%-85% 之间,这样对总线延迟的容忍度最好。
/* 时钟配置示例:40MHz时钟,1Mbps仲裁段,4Mbps数据段 */
hfdcan1.Init.DataBaudRatePrescaler = 2; /* 预分频器 */
hfdcan1.Init.DataSyncJumpWidth = 2; /* 同步跳转宽度 */
hfdcan1.Init.DataTimeSeg1 = 4; /* 时间段1 */
hfdcan1.Init.DataTimeSeg2 = 3; /* 时间段2 */
/* 计算:tq = 2/40MHz = 50ns */
/* 位时间 = (1 + 4 + 3) * 50ns = 400ns = 2.5Mbps */
/* 采样点 = (1 + 4) / (1 + 4 + 3) = 62.5% */
注意:仲裁段和数据段的位时序是分开配置的!仲裁段通常用 1Mbps,数据段可以更高。但别忘了,总线上的所有节点必须用相同的仲裁段比特率,数据段比特率可以不同(只要都支持 BRS)。
最后,别忘了检查时钟使能。我见过有人配了半天,结果发现 FDCAN 的时钟根本没打开:
__HAL_RCC_FDCAN_CLK_ENABLE(); /* 使能FDCAN时钟 */
嗯,这一章的内容就到这里。消息 RAM 的管理和中断配置是 FDCAN 开发中最容易出问题的地方,建议你动手写代码的时候多留个心眼。下一章咱们会讲 FDCAN 的初始化流程和收发实现,到时候会用到今天讲的知识。