4. CAN FD 总线仲裁原理
CSMA/CA,全称是「载波监听多路访问/冲突避免」。你把这个名字拆开看——
- 载波监听:每个节点发数据前,先听听总线上有没有其他节点正在发。
- 多路访问:所有节点共享同一根总线,谁想发都能发。
- 冲突避免:万一两个节点同时开抢,总线能靠硬件自动裁定谁赢,数据不会乱掉。
我在项目里遇到过几次,刚接触 CAN 的同事总问:「要是两个 ECU 同时发,位信号叠在一起不就短路了?」其实不会。
CAN 总线用的是「线与」逻辑。显性位(逻辑 0)能覆盖隐形位(逻辑 1)。想象一下——谁先往总线灌 0,总线就变成 0。这就给仲裁打下了地基。
一句话总结:CSMA/CA 让多个节点同时发送时,总线自动以「显性>隐性」的规则决定谁赢,并且赢的那个节点完整发送,不丢一帧。
仲裁规则:线与逻辑与位对比
CAN FD 的仲裁段和经典 CAN 完全一样。每个节点发送时,会同时回读总线电平。如果你发的位是隐性(1),而读到的是显性(0),说明有更高优先级的节点在与你竞争。此时你立刻退出,转为接收模式。
这一点我建议初学者用逻辑分析仪抓一下波形。我记得我第一次用示波器看两颗 STM32 同时发相同 ID 的帧,发现它们发送完后完全一样,因为 ID 相同则继续往下比后面的控制字段。嗯,这才是重点:比较过程是逐位进行的,从 MSB 到 LSB。
| 总线状态 | 逻辑值 | 谁赢 |
|---|---|---|
| 显性(Dominant) | 0 | 赢得仲裁 |
| 隐性(Recessive) | 1 | 失去仲裁 |
实战小技巧:你可以在发送端的 TX 脚和 RX 脚之间加一个 GPIO 触发的示波器通道,捕捉仲裁退出发送切换的瞬间。我曾经用这个方法查出某款收发器在仲裁位边缘的响应慢了 0.2 个位时间,导致丢失本该赢的仲裁。
ID 优先级决定机制
为什么说 ID 越小优先级越高?因为 ID 在仲裁场里从高位到低位依次发送。比如标准帧的 ID 是 11 位(ID10~ID0),先发 ID10。
假设节点 A 发送 ID = 0x100(二进制 001 0000 0000),节点 B 发送 ID = 0x0FF(000 1111 1111)。先对比最高位 ID10:
- A 发 0(显性),B 发 0(显性)——持平,继续。
- ID9:A 发 0,B 发 0——持平,继续。
- ID8:A 发 1(隐性),B 发 0(显性)——B 的 0 覆盖了 A 的 1,A 检测到总线与自己的输出不一致,A 退出。
结果是 B(ID 0x0FF)赢得总线。你看,ID 值越小,高位出现显性的机会越大,优先级自然就越高。
关键点:CAN FD 的仲裁过程不会破坏数据,只要节点按照 ID 递减的顺序设计,就能保证高优先级消息一直得到及时发送。我在车控项目中曾把紧急报警消息的 ID 设为 0x001,刹车消息用 0x002,底盘消息用 0x010……你想想看,最紧急的消息几乎永远不需要等。
| 帧类型 | ID 位数 | 起始位 | 优先级比较 |
|---|---|---|---|
| 标准 CAN / CAN FD(标准 ID) | 11 | ID28→ID18 | ID 值越小,优先级越高 |
| 扩展 CAN / CAN FD(扩展 ID) | 29 | ID28→ID0(先标准 11 位,再扩展 18 位) | 一样规则,逐位比较 |
注意:扩展帧与标准帧同发时,如果标准帧的 ID 高位与扩展帧的 ID 高位相同,SRR 位(扩展帧替代远程请求位)是隐性,而标准帧的 RTR 位如果是显性,标准帧会赢。实际项目中我建议:除非必要,不要在同一个网络上混用标准帧和扩展帧,否则优先级排名容易把人搞晕。
我习惯用这个图给团队科普,清晰说明为什么 ID 小的节点总能抢到总线。说白了,数字电路处理「线与」几乎不花时间,仲裁在一个位时间内就能完成。CAN FD 的仲裁段时序与经典 CAN 完全兼容,这也是它能沿用现有 CAN 网络的原因之一。
调试经验:如果发现低优先级消息长时间发不出去,别只怀疑软件栈。先拿 CAN 协议分析仪抓一下总线占用率,我曾经遇到一颗 500k 总线上普通消息被诊断报文(扩展帧 ID 0x00 开头)占满了,ID 设成 0x0000001 的诊断任务几乎不放总线。