第四章:CAN总线仲裁——CSMA/CA机制、位仲裁原理、优先级判定、非破坏性仲裁
大家好,欢迎来到第四章。
上一章我们聊了CAN的帧结构,你可能会想:这么多节点挂在同一条总线上,要是两个节点同时发数据,不就打架了吗?
没错,这就是总线冲突问题。但CAN总线有个绝活——它不会像以太网那样把数据撞没了再重发。CAN用的是非破坏性仲裁,说白了就是:大家同时说话,谁优先级高谁继续说,优先级低的自动闭嘴。
这一章,我们就来拆解这个机制。
4.1 CSMA/CA:先听再说,边听边说
CAN总线采用的介质访问控制机制叫CSMA/CA,全称是Carrier Sense Multiple Access with Collision Avoidance。
翻译成人话就是:载波监听,多路访问,冲突避免。
我刚开始学的时候,总把它和以太网的CSMA/CD搞混。CD是Collision Detection,检测到冲突就停;CA是Collision Avoidance,想办法避免冲突。CAN用的是CA,不是CD。
CSMA/CA的核心行为分三步:
- 监听总线:每个节点在发送前,先看看总线上有没有信号。如果有,就等总线空闲再发。
- 多路访问:多个节点可以同时尝试发送,谁先抢到总线谁先发。
- 冲突避免:如果多个节点同时开始发送,通过位仲裁机制决定谁继续,谁退出。
关键点:CAN的CSMA/CA不是完全避免冲突,而是把冲突的解决过程变成了优先级判定过程。冲突本身不会破坏数据,这就是非破坏性仲裁的精髓。
我在项目中遇到过一个问题:有个节点总是发不出数据,后来发现它一直在监听总线,但总线其实一直有信号。查了半天,原来是它的CAN控制器配置成了只听模式,根本没参与仲裁。嗯,这种低级错误,犯过一次就记住了。
4.2 位仲裁原理:谁先发0,谁赢
位仲裁是CAN总线最精彩的设计之一。它利用了CAN总线的显性位(0)和隐性位(1)特性。
简单说:显性位覆盖隐性位。
如果A节点发0,B节点发1,总线上实际看到的是0。因为0的电平更强。
仲裁过程是这样的:
- 多个节点同时开始发送仲裁段(也就是ID段)。
- 每个节点逐位发送自己的ID,同时监听总线电平。
- 如果自己发的是1(隐性),但总线上读到的是0(显性),说明有更高优先级的节点在发0。
- 这个节点立刻退出仲裁,转为接收模式。
- 剩下的节点继续逐位比较,直到只剩一个节点胜出。
个人经验:我建议你在调试时用示波器同时抓CAN_H和CAN_L的差分信号,再配合CAN协议分析仪看仲裁过程。你会看到仲裁失败的节点在ID段中间突然停止发送,总线波形上只剩一个节点的信号继续。那个画面,很直观。
4.3 优先级判定:ID越小,优先级越高
优先级判定规则只有一句话:标识符(ID)数值越小,优先级越高。
为什么?因为ID的高位先发送。ID数值小,意味着高位有更多的0。0是显性位,能压住1。
举个例子:
| 节点 | ID(二进制) | ID(十进制) | 优先级 |
|---|---|---|---|
| 节点A | 0000 1010 | 10 | 最高 |
| 节点B | 0001 0011 | 19 | 中等 |
| 节点C | 0010 0101 | 37 | 最低 |
你看,节点A的ID是10,节点C的ID是37。A的ID更小,所以A赢。
从二进制角度看:A的第5位是0,C的第5位是1。仲裁到这一位时,A的0直接覆盖了C的1,C立刻退出。
避坑指南:我曾经在项目里把两个节点的ID设成了0x100和0x0FF。你想想看,0x0FF比0x100小,按理说0x0FF优先级更高。但实际测试时发现0x100总是抢不过0x0FF。后来一查,原来0x0FF是扩展帧ID,0x100是标准帧ID。标准帧和扩展帧的仲裁规则不一样,标准帧的优先级默认高于扩展帧。嗯,这个坑,我替你们踩过了。
4.4 非破坏性仲裁:赢了继续,输了闭嘴
非破坏性仲裁,是CAN总线最引以为傲的特性。
什么叫非破坏性?
传统总线(比如以太网)发生冲突时,数据会被破坏,所有节点都得重发。这叫破坏性仲裁。
CAN总线不一样。仲裁过程中,胜出的节点继续发送完整的数据帧,没有任何数据丢失。失败的节点只是停止发送,但不会破坏总线上已有的数据。
为什么能做到?因为仲裁发生在仲裁段,数据还没开始传呢。仲裁段只包含ID和RTR位,不包含数据。等仲裁结束,只有一个节点在发,后面的数据段、CRC段都是它一个人说了算。
非破坏性仲裁的好处很明显:
- 实时性好:高优先级消息不会被低优先级消息阻塞太久。
- 带宽利用率高:没有数据被浪费,每次仲裁都有有效数据被传输。
- 确定性:你可以精确计算出最坏情况下的响应时间。
核心理解:非破坏性仲裁的本质,是把总线冲突从「物理层问题」变成了「数据链路层的优先级调度」。你想想看,这相当于在硬件层面实现了一个实时调度器。很多实时操作系统还在软件层面做优先级调度呢,CAN直接在硬件层面就搞定了。
我记得有一次调试一个多节点系统,总线上挂了12个节点,数据量很大。我用CAN分析仪抓包,发现总线利用率到了80%以上,但高优先级的消息延迟始终在1ms以内。这就是非破坏性仲裁的威力——高优先级消息永远插队成功。
4.5 仲裁失败的节点怎么办?
仲裁失败的节点,不会一直沉默。它会:
- 立刻停止发送,转为接收模式。
- 接收当前正在传输的完整帧。
- 等总线空闲后,重新尝试发送。
注意:仲裁失败的节点不会立即重发。它要等当前帧传输结束,总线回到空闲状态,然后重新参与下一次仲裁。
如果多个低优先级节点同时等待,它们会在下一次仲裁中再次竞争。优先级高的那个会再次胜出。这就是为什么低优先级消息可能会被多次推迟——但不会被无限推迟,因为高优先级消息总有发完的时候。
个人建议:设计系统时,不要把关键消息的ID设得太大。我一般把最紧急的消息放在ID 0x001到0x07F之间,中等紧急的放在0x080到0x1FF,非紧急的放在0x200以上。这样能保证关键消息的实时性。
4.6 本章小结
这一章我们聊了CAN总线仲裁的核心机制:
- CSMA/CA:先监听,再发送,冲突靠仲裁解决。
- 位仲裁:逐位比较ID,谁先发0谁赢。
- 优先级判定:ID数值越小,优先级越高。
- 非破坏性仲裁:仲裁过程不破坏数据,胜者继续,败者闭嘴。
下一章,我们会深入CAN的位时序和同步机制。你会知道为什么CAN总线能在没有统一时钟的情况下,让所有节点步调一致地收发数据。那个机制,比仲裁还巧妙。
好,这一章就到这儿。有问题欢迎留言讨论。