3. CAN总线仲裁机制:CSMA/CA与位仲裁原理、优先级判定

各位,咱们今天聊一个硬核话题——CAN总线怎么解决“多个人同时抢着说话”的问题。

你想想看,CAN总线上一堆节点,谁想发数据就发。要是两个节点同时往总线上丢数据,那不就乱套了?

我刚开始接触CAN的时候,也觉得这问题挺棘手。后来看了它的仲裁机制,不得不服——这设计确实巧妙。

3.1 CSMA/CA:先听再说,边说边听

CAN用的不是CSMA/CD(碰撞检测),而是CSMA/CA(碰撞避免)。这两个词就差一个字母,但思路完全不同。

  • CSMA/CD:先听总线,没人用就发。发完发现撞了,退避重发。以太网就是这套路。
  • CSMA/CA:先听总线,没人用就发。但发的时候还在听,发现“撞了”立刻用优先级压住对方。

说白了,CAN的仲裁不是“撞了再处理”,而是“在撞的一瞬间就分出胜负”。

核心要点:CAN的仲裁是无损位仲裁。赢的节点继续发,输的节点自动退出发送。整个过程不丢数据,不浪费带宽。

我记得第一次在示波器上看到仲裁波形时,确实挺震撼的。两个节点同时发,波形在某个位突然“合并”成一条线——那就是赢家诞生了。

3.2 位仲裁原理:显性位“吃掉”隐性位

要理解仲裁,先得搞清楚CAN总线的两个电平状态:

电平状态 逻辑值 物理表现
显性(Dominant) 0 CAN_H高,CAN_L低,差分电压约2V
隐性(Recessive) 1 CAN_H和CAN_L都约2.5V,差分电压0V

关键规则就一句话:显性位(0)会覆盖隐性位(1)。

怎么理解?你想象一下:总线就像一根绳子,谁拉它就往哪边偏。显性位是“用力拉”,隐性位是“松手”。两个人同时拉,绳子肯定往用力的人那边偏。

所以,当两个节点同时发送时:

  • 如果一个发0,另一个发1,总线最终呈现0(显性胜出)
  • 发1的那个节点发现“我发的明明是1,怎么总线上是0?”——立刻知道自己输了

我的经验:调试时经常用逻辑分析仪抓仲裁段。你看到ID位上有“毛刺”或“竞争”波形,别慌——那是正常仲裁过程。只要最终数据帧完整,就没问题。

3.3 仲裁过程:逐位PK,ID小的赢

仲裁发生在帧起始(SOF)之后的仲裁段,也就是11位(或29位)标识符的位置。

流程是这样的:

  1. 总线空闲时,多个节点同时发送SOF(显性位)
  2. 从ID的最高位(bit 10)开始,逐位比较
  3. 每个节点发送一位,同时读取总线电平
  4. 如果发送的位与读取的一致,继续下一位
  5. 如果发送的是1(隐性),但读到的是0(显性),立刻退出

举个例子:

  • 节点A:ID = 0x321(二进制:011 0010 0001)
  • 节点B:ID = 0x123(二进制:001 0010 0011)

从最高位开始比:

位位置 节点A 节点B 总线结果 说明
bit 10 0 0 0 相同,继续
bit 9 1 0 0 节点A发1但读到0,A退出
... ... ... ... 节点B继续发送

结果:节点B(ID=0x123)胜出,节点A自动转为接收状态。

注意:ID值越小,优先级越高。0x000是最高优先级,0x7FF是最低(标准帧)。我见过有人把ID设成0x7FF,结果每次仲裁都输,数据死活发不出去——这就是典型的“优先级饥饿”。

3.4 扩展帧与标准帧的仲裁差异

CAN 2.0B支持两种帧格式:

  • 标准帧:11位ID,仲裁段共12位(SOF + 11位ID + RTR)
  • 扩展帧:29位ID,仲裁段共32位(SOF + 11位基本ID + SRR + IDE + 18位扩展ID + RTR)

两种帧混用时,仲裁规则有讲究:

比较位置 标准帧 扩展帧 仲裁结果
SOF 0 0 相同
ID bit 10-0 11位ID 11位基本ID 按位比较
SRR位 (无此位) 1(隐性) 标准帧RTR位(0)胜出
IDE位 1(隐性) 0(显性) 扩展帧IDE(0)胜出

简单说:标准帧的优先级高于扩展帧。因为标准帧在SRR位位置上是RTR位(显性),而扩展帧的SRR是隐性。所以标准帧先赢一局。

但IDE位又让扩展帧扳回一城——扩展帧的IDE是显性,标准帧的IDE是隐性。不过这时候标准帧已经赢了,IDE位根本轮不到比。

嗯,这里要注意:实际项目中,我建议不要混用标准帧和扩展帧。除非你有明确的兼容性需求,否则统一用一种格式,省得给自己挖坑。

3.5 仲裁失败后的行为:自动重发机制

节点仲裁失败后,会做什么?

  1. 立即停止发送,转为接收模式
  2. 记录当前发送失败的帧
  3. 等待总线空闲后,自动重发该帧

这个重发是硬件自动完成的,不需要软件干预。你只管把数据往发送缓冲区一丢,硬件会帮你搞定仲裁和重发。

避坑指南:我曾经遇到一个情况——某个节点频繁仲裁失败,导致数据延迟很大。查了半天,发现是ID设得太大了(优先级低),再加上总线负载高,它几乎每次都要重发好几次才能成功。解决方案:要么提高它的ID优先级,要么降低发送频率。

另外,重发次数不是无限的。CAN控制器有发送错误计数器(TEC),每次发送失败都会累加。如果TEC超过255,节点会进入总线关闭(Bus-Off)状态,彻底停止通信。这个后面讲错误处理时会细说。

3.6 仲裁机制的知识图谱

下面这张图把仲裁机制的核心逻辑串起来了:

CAN总线仲裁机制知识图谱 CAN总线仲裁机制知识图谱 CSMA/CA 碰撞避免 先听后说(载波监听) 边说边听(位监测) 无损仲裁(位覆盖) 位仲裁原理 显性位(0)覆盖隐性位(1) 优先级判定规则 ID值越小,优先级越高 仲裁过程:SOF → 逐位比较ID → 显性胜出 → 输者退出 → 赢者继续 仲裁失败处理 自动转为接收 → 等待重发 标准帧 vs 扩展帧 标准帧优先级高于扩展帧 核心:仲裁不丢数据,不浪费带宽,硬件自动完成

3.7 实际调试中的仲裁问题

最后聊几个我在现场踩过的坑:

  • 总线负载过高导致仲裁频繁:当总线利用率超过70%时,仲裁冲突概率急剧上升。我建议把负载控制在50%以下,留出余量。
  • ID分配不合理:实时性要求高的节点(如电机控制)用低ID,非实时节点(如温度采集)用高ID。别搞反了。
  • 隐性位干扰:如果总线终端电阻或电平有问题,隐性位可能读成显性位,导致误判。检查CAN收发器和终端电阻是第一步。

重要提醒:仲裁机制依赖所有节点的位时序严格同步。如果某个节点的时钟偏差太大,仲裁会出错。CAN的位同步机制(硬同步+软同步)就是为了解决这个问题。下一章我们会详细讲。

好了,仲裁机制就聊到这儿。记住一句话:CAN的仲裁,就是一场“谁更低调(ID更小)谁赢”的比赛。理解了这个,后面CANopen的优先级管理就好办了。