第2章 CAN协议基础:物理层、帧结构与仲裁机制

各位同学,今天我们来聊聊CAN协议的基础。说实话,CAN协议在车载领域已经用了三十多年了,但直到今天,它依然是车身网络的主力。我刚开始接触CAN的时候,总觉得这东西挺简单的——不就是两根线嘛。后来踩了不少坑,才发现里面的门道真不少。

2.1 CAN总线物理层特性

CAN总线的物理层,说白了就是一对双绞线,我们通常叫它CAN_H和CAN_L。它采用差分信号传输,这有什么好处呢?抗干扰能力强。你想想看,车上的电磁环境多复杂,电机、点火线圈、各种开关,要是用单端信号,早就被干扰得不成样子了。

CAN总线有两种电平状态:显性(Dominant)隐性(Recessive)。显性电平对应逻辑0,隐性电平对应逻辑1。这里有个关键点:显性电平会覆盖隐性电平。什么意思呢?就是只要有一个节点发送显性电平,总线就是显性的。这个特性是后面仲裁机制的基础。

关键参数速查表

参数 高速CAN(ISO 11898-2) 低速CAN(ISO 11898-3)
最大速率 1 Mbps 125 kbps
总线长度(@1Mbps) 约40米 约500米
显性电平(CAN_H) 3.5V 约4V
隐性电平(CAN_H) 2.5V 约1.75V
终端电阻 120Ω(两端) 不固定

我记得有一次做台架测试,总线老是出现莫名其妙的错误帧。查了半天,发现是终端电阻没接对。高速CAN必须在总线两端各接一个120Ω电阻,少一个都不行。嗯,这个坑我替你们踩过了。

避坑指南:我曾经遇到过一个问题——总线长度超过40米时,速率必须降下来。1Mbps下跑50米,波形已经惨不忍睹了。实际项目中,我建议留20%的余量。

2.2 CAN报文帧结构

CAN协议定义了四种帧类型:数据帧、远程帧、错误帧和过载帧。咱们一个一个说。

2.2.1 数据帧

数据帧是最常用的,用来传输实际数据。它分为标准帧(11位ID)和扩展帧(29位ID)。结构上,数据帧包含以下几个部分:

  • 帧起始(SOF):1个显性位,标志一帧的开始
  • 仲裁场:包含ID和RTR位(远程帧请求位)
  • 控制场:包含IDE位(扩展标识符位)和数据长度码(DLC)
  • 数据场:0-8字节的数据
  • CRC场:15位CRC校验 + 1位CRC界定符
  • ACK场:2位,用于接收节点确认
  • 帧结束(EOF):7个隐性位

这里我特别想强调一下DLC。DLC只有4位,理论上可以表示0-15,但CAN协议规定数据场最多8字节。为什么是8字节?我个人理解,这是为了平衡实时性和数据量。8字节对于大多数控制信号来说足够了,而且能保证总线响应速度。

小技巧:实际项目中,我习惯把DLC写死,比如某个报文固定发8字节。这样接收方解析起来简单,不容易出错。当然,如果你做的是诊断报文,DLC可能会变化,那就另当别论了。

2.2.2 远程帧

远程帧用来请求数据。它的结构和数据帧很像,区别在于RTR位是显性的(数据帧的RTR是隐性的),而且没有数据场。说白了,远程帧就是问对方:「嘿,把你的数据发给我!」

不过说实话,我在实际项目中很少用远程帧。为什么?因为远程帧的响应时间不可控。你发一个远程帧,对方什么时候回?如果对方正在忙,可能延迟很久。我建议用周期性发送代替远程帧请求,这样时序更可控。

2.2.3 错误帧

错误帧是节点检测到总线错误时主动发送的。它由两部分组成:错误标志和错误界定符。错误标志可以是6个连续的显性位(主动错误)或6个连续的隐性位(被动错误)。

CAN协议定义了五种错误类型:

  1. 位错误:发送节点监控总线,发现发送的电平和总线实际电平不一致
  2. 填充错误:连续5个相同电平后没有插入相反电平
  3. CRC错误:接收节点计算的CRC和发送的不一致
  4. 形式错误:固定格式的位段出现非法电平
  5. ACK错误:发送节点没有收到ACK应答

我记得有一次调试,发现某个节点老是发错误帧。用示波器一看,原来是它的晶振偏差太大,导致位时序不对。嗯,这个问题排查了整整两天。

2.2.4 过载帧

过载帧用得比较少。它表示节点还没准备好接收下一帧数据,需要总线等一下。结构和错误帧类似,但触发条件不同。过载帧通常发生在节点内部缓冲区满的时候。

2.3 CAN总线仲裁机制

仲裁机制是CAN协议的精髓。它解决了多个节点同时发送时的冲突问题。原理很简单:ID越小,优先级越高

具体怎么工作的呢?假设节点A发ID=0x123,节点B发ID=0x456。在仲裁场,两个节点逐位比较ID。当某一位出现显性(0)和隐性(1)冲突时,发送隐性的节点会自动退出。因为0x123的二进制是0001 0010 0011,0x456是0100 0101 0110,从高位开始比,第2位就分出胜负了——0x123的第2位是0(显性),0x456的第2位是1(隐性),所以节点B退出,节点A继续发送。

仲裁规则总结:

  • 显性位(0)优先级高于隐性位(1)
  • ID值越小,优先级越高
  • 仲裁失败的节点自动转为接收模式
  • 仲裁过程不破坏数据,也不浪费带宽

这里有个容易忽略的点:扩展帧(29位ID)和标准帧(11位ID)同时发送时,谁赢?答案是标准帧赢。因为标准帧的IDE位是显性,扩展帧的IDE位是隐性。所以标准帧的优先级天然高于扩展帧。我建议在同一个网络中,尽量统一使用一种帧格式,避免这种隐性的优先级差异。

避坑指南:我曾经遇到过一个项目,设计人员把ECU的ID设成了0x000。你想想看,0x000是所有ID里最小的,优先级最高。结果这个ECU一启动,其他节点全都被它「压制」了,总线几乎被它独占。后来我们不得不改ID。所以,我建议保留一些低优先级ID给紧急报文,不要把ID用得太满。

最后说一句,仲裁机制虽然巧妙,但它也有代价。当总线负载率超过30%时,仲裁冲突的概率会显著上升,导致总线利用率下降。我一般建议把总线负载率控制在30%以下,最多不超过50%。超过这个值,你就得考虑优化报文周期或者升级到CAN FD了。

好了,这一章的内容就到这里。下一章我们讲CAN FD,看看它和经典CAN有什么区别。