首页 / 驱动算法 / CANopen专题

CAN FD 双波特率:采样点、数据域与仲裁段

CAN FD 提速的秘诀不是"把总线跑得更快",而是把一帧拆成两段:竞争时用慢速(仲裁段)、独占时用快速(数据域),两个波特率各配各的采样点。本文拆解位时间结构、采样点公式与工程取值,配位时间调节与双速率帧两个交互演示和位时序计算 C 代码。

1. 为什么数据域要提速:经典 CAN 的带宽瓶颈

经典 CAN(2.0)单帧最多 8 字节、全程一个波特率,500kbps 下 8 字节数据帧要占约 230µs 总线时间——其中 帧头、CRC、ACK 等开销占了一半以上。想提高吞吐只有两条路:要么降开销,要么提速。CAN FD(ISO 11898-1:2015)两条都走了:数据最多 64 字节(摊薄开销),并且 BRS 位切换出第二个、更高的数据波特率

同样传 64 字节,经典 CAN 需要 8 帧

经典:8 帧 × 8 字节,每帧约 230µs(500k)→ 64 字节总耗时 ≈ 1.85ms

CAN FD:1 帧 × 64 字节,数据域 2Mbps → 仲裁段 + 数据域总耗时 ≈ 0.5ms,吞吐提升约 3~4 倍

关键点:提速的只是"数据域"(Data Phase),帧的仲裁段(Arbitration Phase)仍然用慢速——这是由 CAN 的仲裁机制决定的。

2. CAN FD 帧结构:仲裁段、数据域与 BRS 切换位

一个 FD 帧(BRS=1)在时间轴上天然分成两段,中间用 BRS 位衔接:

场段包含位波特率作用
仲裁段(慢)SOF、ID(11/29)、RTR/SRR、IDE、FDF、res、BRS、ESI、DLC仲裁波特率(如 500k)多节点竞争、逐位仲裁、全网同步
数据域(快)数据(≤64B)、CRC(17/21)数据波特率(如 2M)赢家独占传输,只此一个节点在发
尾部(慢)CRC 定界符、ACK、EOF、IFS仲裁波特率切回慢速做应答与帧结束

三个关键位(都在仲裁段尾部):

⚠️ 不要混淆"总线速度"和"数据域速度"

CAN FD 不等于"总线跑 2M"。仲裁段永远是慢速:两个节点同时抢线时,如果采样节奏不一致,仲裁就会判错。所谓"双波特率"是同一根总线上按帧内位置切换,不是整条总线提速。

3. 为什么仲裁段不能提速:多主仲裁与位同步

CAN 是多主逐位仲裁:多个节点可以在同一时刻开始发送,边发边"听",ID 位为显性(0)的节点赢,隐性(1)的节点看到自己被压掉就退出。这个过程要求:

传输距离与位时间的关系(简化)

Lmax ≈ (tbit − 2·tprop_phy) × v / 2,v ≈ 5ns/m(线缆单程约 0.2c)

500k(2µs/位)→ 数米到几十米;2M(500ns/位)→ 只剩车内级短距离。所以仲裁段必须保持慢速,数据域提速只影响赢家独占的那一段。

数据域之所以能提速:仲裁已经结束,总线上只有一个节点在发,其余节点只需跟得上采样(BRS 位已提前告知),不需要再逐位比较——同步要求大幅放松。

4. 位时间与采样点:SYNC/PROP/PHASE 与采样点公式

一个 CAN 位时间(Tbit)由若干 时间量子 TQ(Time Quantum)组成,四个段各有分工:

典型长度作用
SYNC_SEG固定 1 TQ接收方检测下降沿/上升沿,同步起点
PROP_SEG1~8 TQ补偿总线传播延迟与收发器延迟
PHASE_SEG11~8 TQ相位缓冲,采样点前;可被 SJW 拉长(再同步)
PHASE_SEG21~8 TQ采样点后,留出采样与建立时间;可被 SJW 缩短
采样点公式(CAN 控制器通用)

Tbit = SYNC_SEG + PROP_SEG + PHASE_SEG1 + PHASE_SEG2

采样点 SP = (SYNC_SEG + PROP_SEG + PHASE_SEG1) / Tbit

采样发生在 PHASE_SEG1 与 PHASE_SEG2 交界处——SP 越接近 1,采样越靠近位末尾

采样点直接决定抗干扰与同步裕量的平衡:靠前(如 70%)留给传播延迟的裕量少、抗长线差;靠后(如 87.5%)对晶振误差和噪声的裕量变紧。业界推荐值:

波特率推荐采样点典型场景
125k / 250k87.5%长线、工业现场(最远节点距离大)
500k80%汽车/伺服总线最常见配置
1M75%短线高速,距离短、节点少
FD 数据域 1M~5M75%数据域短帧内独占传输,裕量要求低
✅ 一句话记住采样点

采样点 = "位时间里在哪一刻读电平":低速长线往 87.5% 靠,高速短线往 75% 靠;全网所有节点必须用同一个采样点,否则必然错帧。

5. 采样点怎么选:计算实例与双采样点

以 80MHz 外设时钟、500kbps 仲裁段、80% 采样点为例(每 TQ = 25ns):

500k / SP=80% 的段分配(80 TQ/位)

TQ/位 = 80MHz ÷ 500k = 160 TQ → 分频 BRP=2 后为 80 TQ

SYNC=1 + PROP=15 + PS1=48 + PS2=16 = 80 → (1+15+48)/80 = 80%

SJW 取 4(≤ PS2)

CAN FD 的双波特率意味着两个采样点要分别配:仲裁段 80%、数据域 75%(2Mbps、40 TQ/位:SYNC=1 + PROP=2 + PS1=27 + PS2=10 → (1+2+27)/40 = 75%)。Linux 下用 ip link 一行配好:

# CAN FD 双波特率 + 双采样点(仲裁 500k/80%,数据 2M/75%) sudo ip link set can0 type can bitrate 500000 sample-point 0.80 \ dbitrate 2000000 dsample-point 0.75 fd on sudo ip link set can0 up # 验证:显示仲裁/数据波特率与采样点、FD 状态 ip -details link show can0

注意 sample-pointdsample-point 是两个独立参数——只配波特率不配采样点时,内核用驱动默认值,不同内核/驱动默认可能不一致,这是"两台机器都配 500k 却通信不上"的隐蔽原因之一。

6. 交互式演示:位时间采样点调节与 FD 双速率帧

上半部分调节总 TQ 数与采样点,实时看四段分配和采样时刻;下半部分选仲裁/数据波特率,看一个 64 字节 FD 帧在时间轴上仲裁段慢、数据域快的实际比例,以及 BRS 切换点与两段各自的采样点位置。

位时间结构、采样点与 CAN FD 双速率帧演示
位时间:SYNC=1 + PROP=15 + PS1=48 + PS2=16 = 80 TQ · 采样点 (1+15+48)/80 = 80.0%

观察要点:采样点滑块改变的是 PHASE_SEG1/PHASE_SEG2 的分界;波特率选择改变的是时间轴上仲裁段与数据域的宽度比例——数据域提速越狠,64 字节有效载荷在整帧时间里占比越小,吞吐越高。

7. 配置与位时序计算(C语言)

下面是通用的位时序计算函数:给定目标波特率、采样点与外设时钟,自动搜索分频与段分配,输出可直接映射到 STM32 FDCAN / bxCAN 的 TSEG 寄存器值。

/* canfd_bitrate.c —— CAN/CAN FD 位时序计算示例 * 编译:gcc -o canfd_bitrate canfd_bitrate.c -lm * 运行:./canfd_bitrate 80000000 500000 0.80 (f_apb 目标波特率 采样点) */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <math.h> /* 搜索最优 BRP/TSEG 组合,使采样点最接近 sp 且波特率误差最小。 * 输出:*brp 预分频,*tseg1 时间段1(PROP+PS1,不含 SYNC),*tseg2 时间段2 * 寄存器映射:STM32 bxCAN/FDCAN 写入值为 TSEGx-1 */ int calc_bit_timing(uint32_t f_apb, uint32_t bitrate, double sp, int *brp, int *tseg1, int *tseg2) { double best_err = 1e9; int found = 0; for (int tq = 8; tq <= 25; tq++) { /* 每位的 TQ 数,8~25 可设 */ int br = (int)((double)f_apb / bitrate / tq); if (br < 1) continue; int s2 = (int)((1.0 - sp) * tq + 0.5); /* PS2 舍入 */ if (s2 < 1) s2 = 1; if (s2 > tq - 2) s2 = tq - 2; int s1 = tq - 1 - s2; /* SYNC 固定 1 TQ */ if (s1 < 1) continue; double actual_sp = (1.0 + s1) / tq; /* 实际采样点 */ double err = fabs(actual_sp - sp); if (err < best_err) { best_err = err; *brp = br; *tseg1 = s1; /* PROP+PS1 */ *tseg2 = s2; found = 1; } } return found; } int main(int argc, char **argv) { uint32_t f_apb = 80000000UL; double sp = 0.80; uint32_t rate = 500000; if (argc > 1) f_apb = strtoul(argv[1], NULL, 10); if (argc > 2) rate = strtoul(argv[2], NULL, 10); if (argc > 3) sp = atof(argv[3]); int brp, t1, t2; if (!calc_bit_timing(f_apb, rate, sp, &brp, &t1, &t2)) { printf("no solution\n"); return 1; } printf("bitrate=%u f_apb=%u target_sp=%.2f\n", rate, f_apb, sp); printf("BRP=%d TSEG1=%d TSEG2=%d (寄存器写 TSEGx-1)\n", brp, t1, t2); printf("实际采样点=%.2f%% 实际波特率=%.0f\n", (1.0 + t1) / (1 + t1 + t2) * 100, (double)f_apb / brp / (1 + t1 + t2)); return 0; }

FD 数据域同理调用一次(不同 TQ 与采样点),把结果填进 FDCAN 的 NBTP(仲裁段)与 DBTP(数据域)即可。

8. 工程要点与常见误区

要点 1:采样点是"全网统一"的

同一网段所有节点必须用同一个采样点(仲裁段)与同一种波特率,否则边沿同步后采样时刻不同,长帧必出错。新节点上线前先核对 ip -details link show 的采样点。

要点 2:总线越长,采样点越靠后

传播延迟占位时间比例变大时,把采样点推到 85%~87.5% 能留出更长的采样前窗口;短距离高速网可以放到 75%。采样点不是固定值,要按拓扑实测调。

要点 3:SJW 吸收晶振偏差,别设 0

SYNC 之后各节点晶振有偏差,靠位填充边沿触发重同步,SJW 决定单次能拉长/缩短多少。常见取 SJW = 4 TQ(≤ PS2),晶振容差差或节点多的场合要留足。

要点 4:FD 网段与经典节点要隔离

经典 2.0 节点看到 FDF=1 的帧直接判错。混跑方案:FD 节点只在需要时发经典帧,或把 FD 网段独立;不要指望 2.0 节点"容忍" FD 帧。

误区 1:以为数据域提速 = 总线提速

仲裁段仍是慢速,竞争、广播、ACK 都在慢速段完成。提速收益集中在"长数据帧",短帧(≤8 字节)用 FD 反而可能因帧头开销更慢——选型时按帧长权衡。

误区 2:采样点越靠后越好

87.5% 适合长线,但太靠近位末尾时,PS2 只剩很少 TQ,采样建立时间不足、对边沿噪声更敏感。采样点要按"传播延迟 vs 晶振/噪声"两边算,不是一味后移。

误区 3:双波特率只配波特率不配采样点

bitrate + dbitrate 只定了速率,采样点用驱动默认;不同内核版本默认可能不同,跨主机联调时"波特率明明一样却乱帧"十有八九是这个。

9. 小结

  1. CAN FD = 仲裁段慢速 + 数据域快速,BRS 位做切换,CRC 定界符切回
  2. 仲裁段不能提速:多主逐位仲裁要求全网同节奏采样,且位时间受传播延迟约束
  3. 位时间 = SYNC(1) + PROP + PS1 + PS2,采样点 SP = (1+PROP+PS1)/Tbit
  4. 采样点经验值:125k~250k 用 87.5%,500k 用 80%,1M 与 FD 数据域用 75%
  5. 双波特率要双采样点分别配置:sample-pointdsample-point
  6. 位时序用"TQ 总数 + 采样点"反解段分配,STMFDCAN 映射 NBTP/DBTP

至此 CAN 板块已经形成完整链路:CAN 帧结构(04)→ 双波特率与采样点(本篇)→ CANopen 对象字典与通信对象(01)→ CiA 402 伺服行规(02)→ 从站实现与负载率(03)。