第3章:CAN FD 位时间与采样点
做 CAN 或者 CAN FD 开发,说白了就是在跟时间做游戏。你想想看,一根总线上挂了好几个节点,大家得在同一个节拍里说话,谁都不能抢拍子或拖拍子。这个节拍怎么定义的?就是位时间。
3.1 位时间的组成
CAN FD 的位时间跟经典 CAN 很像,分成四个段。我拿我调试的一个 500 kbps 项目来举例:
- 同步段 (Sync Seg):固定 1 个 TQ(Time Quantum,时间量子)。用来让所有节点对齐时钟边沿。简单讲,总线上的跳变沿必须落在这个段里。
- 传播段 (Prop Seg):补偿物理延迟,包括驱动器、线缆、接收器。长度一般在 1~8 个 TQ。我个人习惯先取 2~3 个 TQ,然后根据实测再调。
- 相位缓冲段 1 (Phase Seg 1):采样点之前,用于补偿边沿相位误差。可重同步时延长。长度 1~8 个 TQ。
- 相位缓冲段 2 (Phase Seg 2):采样点之后,用于补偿边沿相位误差。可重同步时缩短。长度 1~8 个 TQ。
- 采样点 (Sample Point):位于 Phase Seg1 和 Phase Seg2 之间。
整个位时间 Tbit = (Sync + Prop + Phase1 + Phase2) × TQ。每个 TQ 的时间由系统时钟分频得到。
3.2 采样点设置与计算
采样点位置 = (Sync + Prop + Phase1) / (Sync + Prop + Phase1 + Phase2) × 100%。对 CAN FD 来说,官方推荐的数据段采样点靠近 70%~80%,仲裁段保持一致。
我在项目里踩过一个坑:某次把采样点设到了 85%,结果低速时没问题,换了个温湿度环境就开始丢帧。后来发现线缆长了 3 米,相位裕量不够。嗯,所以我现在都老老实实卡在 75%~80% 之间。
3.3 时间量子 TQ 的计算
TQ 由 CAN 控制器内部的晶振分频得到。公式很简单:TQ = 1 / (fclock / (BRP+1)),其中 BRP 是波特率预分频系数。
举个例子,系统时钟 80 MHz,目标位时间 1 µs(对应 1 Mbps),那我需要总 TQ 个数 = 位时间 / TQ。假如每个 TQ 是 12.5 ns,那么 1 µs / 12.5 ns = 80 个 TQ。80 TQ 够分给四个段,很舒服。
| 时钟频率 | BRP | TQ 时间 | 总 TQ 数 | 典型位时间 |
|---|---|---|---|---|
| 80 MHz | 0 | 12.5 ns | 80 | 1 µs (1 Mbps) |
| 40 MHz | 0 | 25 ns | 40 | 1 µs (1 Mbps) |
| 80 MHz | 1 | 25 ns | 40 | 1 µs (1 Mbps) |
注意:BRP 分频系数越小,TQ 越精细,但分频器抖动也小一些。我一般习惯用 BRP=0 或 1,这样 TQ 数目多,段分配更灵活。
3.4 位时间配置的避坑指南
- 同步跳转宽度 (SJW) 别太大:SJW 决定了一次重同步能调整多少个 TQ。设得太大会把采样点拉偏,我见过有人设成 4 TQ,结果总线抖动一大就直接超时。一般 SJW 取 1~2 TQ 就够了。
- 不要忽略传播段:有次我同事把传播段设为 0,想着省 TQ。结果线长 10 米,信号来回延迟刚好卡在同步段边上,导致丢帧。最后加了 2 TQ 传播段才稳定。
- CAN FD 数据段要单独设置:仲裁段 1 Mbps,数据段 5 Mbps,不能共用一套位时间参数。很多初学者一个配置通吃两段,会出大问题。
3.5 知识脉络图
下面这张图把位时间、采样点、TQ 计算、段分配串了起来。你可以把它存下来,以后配置位时间时当速查表用。
3.6 实际配置示例(伪代码)
下面是我在一个 STM32G4 项目上用过的 CAN FD 初始化配置片段,仲裁段 500 kbps,数据段 2 Mbps:
// 仲裁段:500kbps = 2 us位时间,TQ=50ns => 40 TQ
// Sync=1, Prop=3, Phase1=12, Phase2=24 -> 采样点 (1+3+12)/40=40% → 不对,改为:
// 重新算:若采样点75%,需要总TQ=40,T_{sync+prop+phase1}=30,所以Phase2=10
// 配置为:Sync=1, Prop=3, Phase1=26, Phase2=10 → 采样点=30/40=75%
CAN_FD_InitTypeDef canfd_conf = {
.Prescaler = 1, // BRP = 1 => TQ = 2 * 50 ns = 100 ns (时钟80MHz)
.SyncJumpWidth = 2,
.Seg1 = 26, // Prop+Phase1
.Seg2 = 10,
.DataPrescaler = 0, // 数据段分频
.DataSeg1 = 12,
.DataSeg2 = 5, // 数据段采样点 ≈ 77%
.Mode = CAN_FD_MODE
};
注意:Seg1 寄存器一般把 Prop 和 Phase1 合并。我每次配置完都会用示波器抓一下 CAN 总线上的显性位宽度,看是否跟理论值一致。别全信计算,实测最靠谱。
3.7 总结
位时间是个细活,四个段各有各的使命。Sync 负责对齐,Prop 补偿物理延迟,Phase1 和 Phase2 做相位缓冲。采样点卡在 Phase1 和 Phase2 之间,位置不能瞎设。我建议新手先照着参考设计配,跑通了再微调。好了,这一章就到这里,后面我们会用这些参数去算实际的负载率。