4. CAN FD 标准波特率配置:Nominal & Data Bit Timing 计算

好,咱们进入第四章。这一章是 CAN FD 配置里最“硬核”的部分——波特率计算。说白了,就是告诉芯片:你什么时候采样,什么时候跳转,什么时候发下一个位。

我刚开始接触 CAN FD 时,觉得这东西不就是设几个寄存器嘛。结果第一次调试,总线死活不通。后来才发现,是 Nominal 和 Data 两套时序没分开。嗯,这里面的坑,咱们一个个填。

4.1 Nominal Bit Timing:仲裁段的“心跳”

Nominal Bit Timing 负责 CAN FD 的仲裁段和 ACK 段。它的时钟源通常是 APB 时钟(比如 80MHz),然后经过分频、时间段组合,生成一个位时间。

一个 Nominal 位时间由三部分组成:

  • Sync_Seg:同步段,固定 1 个 Tq(Time Quantum)。
  • BS1(Prop_Seg + Phase_Seg1):传播段 + 相位段1,可编程 1~16 Tq。
  • BS2(Phase_Seg2):相位段2,可编程 1~8 Tq。

采样点就在 BS1 和 BS2 的交界处。这个位置很关键——采样点太靠前,信号还没稳定;太靠后,可能错过边沿。

Nominal 位时间公式:

Nominal Bit Time = (Sync_Seg + BS1 + BS2) × Tq
Tq = (BRP + 1) / f_APB
Nominal Baud Rate = 1 / Nominal Bit Time

举个例子。APB 时钟 80MHz,目标波特率 1Mbps,采样点 75%。

参数 计算过程 结果
位时间 Tq 数 80MHz / 1Mbps = 80 Tq 80
Sync_Seg 固定 1 Tq 1
BS1 + BS2 80 - 1 = 79 Tq 79
采样点 75% BS1 = 79 × 0.75 ≈ 59 Tq 59
BS2 79 - 59 = 20 Tq 20

等等,BS2 最大才 8 Tq,20 超了。这说明什么?说明 80MHz 下 1Mbps 用 75% 采样点,单靠 Tq 数调不过来。得换分频比。

我的经验:Nominal 位时间 Tq 数建议在 8~25 之间。太少,采样精度不够;太多,寄存器位数浪费。我一般先定 Tq 数,再反推 BRP。

4.2 Data Bit Timing:数据段的“高速通道”

Data Bit Timing 是 CAN FD 独有的。它负责数据段的快速传输,最高可达 8Mbps 甚至更高。它的结构和 Nominal 类似,但 Tq 更短,时间段更紧凑。

Data 位时间同样有三段:

  • Sync_Seg:固定 1 Tq。
  • BS1:可编程 1~16 Tq。
  • BS2:可编程 1~8 Tq。

但注意,Data 段的 Tq 时钟源可能和 Nominal 不同。STM32 的 CAN FD 控制器通常允许独立配置 Data 段的预分频器。

Data 位时间公式:

Data Bit Time = (Sync_Seg + BS1 + BS2) × Tq_Data
Tq_Data = (DIV + 1) / f_APB
Data Baud Rate = 1 / Data Bit Time

我做过一个项目,要求 Nominal 500kbps,Data 2Mbps。当时我直接把 Nominal 的 BRP 套到 Data 上,结果 Data 段全是错误帧。后来才意识到,Data 段的 Tq 必须单独算。

参数 Nominal (500kbps) Data (2Mbps)
APB 时钟 80MHz 80MHz
预分频 BRP = 3 → Tq = 50ns DIV = 0 → Tq = 12.5ns
位时间 Tq 数 40 Tq 40 Tq
Sync_Seg 1 Tq 1 Tq
BS1 29 Tq 29 Tq
BS2 10 Tq 10 Tq
采样点 75% 75%

你看,Tq 数一样,但实际时间差 4 倍。这就是 Data 段能跑高速的秘密。

4.3 采样点配置:别小看这 1%

采样点位置直接影响总线抗干扰能力。CAN 标准推荐采样点在 75%~87.5% 之间。我个人习惯:

  • 低速(125kbps 以下):采样点 80%~85%,容忍线缆延迟。
  • 中速(250k~500k):采样点 75%~80%,平衡延迟和抖动。
  • 高速(1M 以上):采样点 70%~75%,优先保证边沿捕获。

采样点计算公式:

Sample_Point = (Sync_Seg + BS1) / (Sync_Seg + BS1 + BS2) × 100%

举个例子,BS1=29,BS2=10,Sync=1:

Sample_Point = (1 + 29) / (1 + 29 + 10) = 30 / 40 = 75%

我曾经踩过的坑:有一次把采样点设到 68%,结果总线长度超过 10 米就疯狂丢帧。后来改成 80%,问题解决。采样点太靠前,远端节点的信号还没建立稳定就被采样了。

4.4 同步跳转宽度(SJW):给时钟一点“弹性”

SJW 决定了 CAN 控制器在一个位时间内最多能调整多少 Tq 来同步。说白了,就是容忍时钟偏差的能力。

SJW 的取值范围:1~4 Tq,且不能超过 BS2 的值。

为什么不能超过 BS2?因为 SJW 是在同步段之后、BS2 之前调整的。如果 SJW 比 BS2 大,调整后可能超出位时间边界。

场景 SJW 建议 理由
晶振精度高(±20ppm) 1~2 Tq 时钟偏差小,不需要大跳转
晶振精度低(±100ppm) 2~3 Tq 需要更多余量补偿偏差
多节点长距离 3~4 Tq 线缆延迟和时钟累积偏差大

我一般设 SJW = 2 Tq。除非总线节点超过 20 个,或者线缆长度超过 50 米,我才会考虑 3 Tq。

4.5 波特率表设计:一次配好,到处复用

实际项目中,一个设备可能要适配多种波特率。比如诊断模式用 250kbps,正常通信用 500kbps,固件升级用 1Mbps。手动一个个算太累,我习惯建一个波特率表。

下面是我常用的 STM32 CAN FD 波特率表模板(APB=80MHz):

模式 波特率 BRP Sync BS1 BS2 SJW 采样点
低速 125kbps 7 1 13 6 2 70%
标准 250kbps 3 1 13 6 2 70%
高速 500kbps 1 1 13 6 2 70%
CAN FD Data 2Mbps 0 1 29 10 2 75%
CAN FD Data 5Mbps 0 1 11 4 2 75%

这个表怎么用?我一般把它定义成结构体数组,放在头文件里。初始化时根据需求查表,直接写寄存器。

typedef struct {
    uint32_t baudrate;
    uint8_t  brp;
    uint8_t  sync_seg;
    uint8_t  bs1;
    uint8_t  bs2;
    uint8_t  sjw;
} CANFD_BitTiming_TypeDef;

const CANFD_BitTiming_TypeDef canfd_timing_table[] = {
    {125000,  7, 1, 13, 6, 2},
    {250000,  3, 1, 13, 6, 2},
    {500000,  1, 1, 13, 6, 2},
    {2000000, 0, 1, 29, 10, 2},
    {5000000, 0, 1, 11, 4, 2},
};

我的习惯:波特率表里多留几行空位,方便后期加新速率。另外,Data 段的时序参数我单独放一个表,不和 Nominal 混在一起,避免配错。

4.6 本章知识体系图

下面这张图把 Nominal 和 Data 的时序关系、采样点、SJW 串起来了。你看一眼就能明白整个配置流程。

CAN FD 波特率配置知识体系 CAN FD 波特率配置知识体系 APB 时钟 (80MHz) 预分频 (BRP / DIV) Nominal Bit Timing Data Bit Timing Sync_Seg(1) + BS1(13) + BS2(6) = 20 Tq Sync_Seg(1) + BS1(29) + BS2(10) = 40 Tq 采样点 = (1+13)/20 = 70% 采样点 = (1+29)/40 = 75% SJW = 2 Tq (≤ BS2) SJW = 2 Tq (≤ BS2) 波特率表 → 寄存器配置

这张图从左到右展示了配置流程:时钟 → 分频 → 两路时序 → 采样点 + SJW → 最终输出到波特率表。你写代码时,按这个顺序一步步来,基本不会出错。

最后提醒一句:寄存器配置完成后,一定要用示波器或逻辑分析仪抓一下 CAN FD 的波形。我见过太多“理论计算正确,实际波形一塌糊涂”的案例。尤其是 Data 段的高速模式,稍微有点容性负载,边沿就变缓了。实测才是硬道理。