CAN总线协议详解:帧结构、仲裁机制与位时序

CAN总线,说白了就是车载电子系统的“神经”。我做了这么多年OTA升级,每次遇到通信问题,十有八九都得回到CAN帧结构上找原因。这一章,咱们就把CAN协议的底裤扒干净——帧结构长什么样、多节点抢总线怎么判输赢、位时序怎么调才能不丢数据。

2.1 CAN帧的四种类型

CAN总线上一共就四种帧。我刚开始接触时觉得种类少应该好记,结果发现每种帧的细节都不少。咱们一个一个过。

帧类型用途谁发的关键特征
数据帧携带数据从发送器到接收器发送节点有数据场,0~8字节
远程帧请求某个ID的数据接收节点无数据场,RTR位=1
错误帧通知总线出现错误检测到错误的节点6个显性位+8个隐性位
过载帧请求延迟下一帧发送接收节点与错误帧结构类似

2.2 数据帧结构——最常用的帧

数据帧分标准帧(11位ID)和扩展帧(29位ID)。我建议你先把标准帧吃透,因为大部分车载ECU通信用的都是它。

标准数据帧组成(从SOF到ACK共7个场)

  • SOF(帧起始):1个显性位,同步所有节点
  • 仲裁场:11位ID + RTR位(数据帧RTR=0)
  • 控制场:IDE位 + r0保留位 + DLC(4位数据长度)
  • 数据场:0~8字节,OTA升级的刷写数据就在这里
  • CRC场:15位CRC + 1位CRC界定符
  • ACK场:1位ACK槽 + 1位ACK界定符
  • EOF(帧结束):7个隐性位

我在做OTA升级日志分析时,经常看到DLC字段被填错。比如明明只发了4字节,DLC却写了8。接收节点会傻等后面4个字节,结果总线直接报错。嗯,这种低级错误在供应商代码里还挺常见的。

2.3 远程帧——只问不拿

远程帧跟数据帧长得几乎一样,唯一的区别是RTR位=1,而且没有数据场。它的作用就是“请求数据”。

举个例子:发动机ECU想请求车速信号,就发一个ID=0x123的远程帧。仪表ECU收到后,会发一个相同ID的数据帧回来。我曾经在分析一个CAN日志时,发现某个节点疯狂发远程帧,每秒几百次,直接把总线带宽吃光了。后来查出来是那个节点的接收缓冲区满了,它用远程帧“催”数据,结果越催越堵。

注意:远程帧的DLC必须与对应数据帧的DLC一致。如果远程帧DLC=8,但数据帧实际只发4字节,接收节点会认为数据不完整,触发错误帧。

2.4 错误帧与过载帧——总线的“急救信号”

错误帧由6个连续的显性位(错误标志)和8个隐性位(错误界定符)组成。任何节点检测到错误,就会主动拉低总线,通知所有人。

过载帧结构类似,但触发条件不同:

  • 接收节点内部缓冲区满,来不及处理
  • 接收节点在帧间隙检测到显性位

我调试过一个项目,OTA升级到一半总线突然“死”了。抓日志发现错误帧满天飞,最后定位到是某个ECU的晶振漂移,导致位时序错乱,它看谁都像错误。换了个晶振就好了。

2.5 CAN总线仲裁机制——谁优先级高谁先走

CAN总线是“多主”架构,所有节点都能同时发数据。那冲突了怎么办?靠仲裁。

仲裁的规则很简单:ID越小,优先级越高。发送器在仲裁场逐位比较ID,谁先发出隐性位(1)但总线上是显性位(0),谁就输,自动转为接收模式。

实战经验:我在设计OTA升级的CAN报文时,会把升级相关报文分配小ID(高优先级),比如0x100~0x1FF。普通诊断报文用0x700~0x7FF。这样升级过程中即使总线负载高,升级数据也不会被其他报文“挤掉”。

你想想看,如果两个节点同时发数据:

  • 节点A:ID = 0x123(二进制 0001 0010 0011)
  • 节点B:ID = 0x456(二进制 0100 0101 0110)

从最高位开始比,节点A的第2位是0,节点B的第2位是1。节点B看到总线上是0(显性),自己发的是1(隐性),立刻闭嘴。节点A继续发送,完胜。

2.6 位时序与同步——为什么CAN能跑1Mbps

CAN不是靠时钟线同步的,它靠的是“位时序”。每个位被分成4个段:

段名称作用典型长度(Tq)
同步段(SS)检测跳变沿,同步所有节点1 Tq
传播段(PTS)补偿物理延迟(线缆、收发器)1~8 Tq
相位缓冲段1(PBS1)采样点前调整,补偿误差1~8 Tq
相位缓冲段2(PBS2)采样点后调整,补偿误差1~8 Tq

采样点位置 = (SS + PTS + PBS1) / 总Tq。我一般把采样点设在87.5%左右,也就是位时间的7/8处。这样即使有晶振误差或线缆延迟,采样也能落在稳定区域。

避坑指南:我曾经在一个项目里,两个ECU的采样点设置差了10%。一个设在80%,一个设在90%。结果在总线负载高时,后者的采样点正好落在信号跳变沿上,频繁读到错误位。后来统一了采样点配置,问题解决。

2.7 同步机制——硬同步与重同步

CAN的同步分两种:

  • 硬同步:每个帧的SOF下降沿触发,所有节点强制对齐到SS段起始
  • 重同步:帧内遇到隐性到显性的跳变沿,通过调整PBS1/PBS2的长度来“追赶”或“等待”

重同步的调整量受SJW(同步跳转宽度)限制。SJW一般设为1~4 Tq。设太大容易误跳,设太小跟不上时钟漂移。我习惯设2 Tq,兼顾稳定性和容错性。

2.8 本章知识结构图

CAN总线协议知识结构 CAN协议核心 四种帧类型 总线仲裁机制 位时序与同步 错误检测与处理 数据帧 远程帧 错误帧 过载帧 ID越小优先级越高 逐位比较 显性位“赢” SS/PTS/PBS1/PBS2 采样点设置 硬同步/重同步 CRC校验 位填充规则 错误状态机 💡 掌握CAN帧结构和仲裁机制,是分析OTA升级日志的基础 位时序配置不当会导致通信不稳定,升级失败率飙升

2.9 位填充规则——防止“隐形太久”

CAN协议规定:连续发送5个相同电平后,必须插入一个相反电平。这叫位填充。目的是给接收节点提供足够的跳变沿来同步时钟。

数据帧从SOF到CRC场都要遵守位填充规则。ACK场和EOF不填充。我见过一个案例,某ECU的CAN控制器位填充逻辑有bug,连续发了6个显性位没插入隐性位,结果总线上所有节点都认为这是错误帧,集体报错。

2.10 实战建议:如何用CAN帧知识排查OTA升级问题

做OTA升级日志分析时,我一般按这个顺序查:

  1. 看DLC:数据长度对不对?有没有多写或少写?
  2. 看ID优先级:升级报文是不是被低优先级报文“饿死”了?
  3. 看错误帧频率:如果错误帧占比超过1%,先查物理层(终端电阻、线缆、接插件)
  4. 看采样点:所有ECU的采样点是否一致?不一致的话,高速通信必出问题

小技巧:用CANalyzer或PCAN抓日志时,打开“错误帧统计”视图。如果看到连续的错误帧,十有八九是位时序或终端电阻的问题。如果错误帧是零星出现的,多半是干扰或接触不良。

CAN协议看起来复杂,但说白了就是一套“谁先抢到总线谁说话”的规则。帧结构是它的语法,仲裁是它的竞争机制,位时序是它的时钟同步手段。把这三点吃透了,车载通信的底子就算打牢了。