4. CAN总线协议详解(下):数据帧、错误帧与过载帧

上一讲我们把CAN总线的物理层和电气特性聊透了。这一讲,咱们深入协议的核心——数据帧的结构,以及总线出问题时的“急救机制”:错误帧和过载帧。

说实话,搞懂CAN的数据帧结构,你就能看懂90%的总线报文。我在调试台架时,经常看到工程师对着CANalyzer上的一串十六进制数发懵。其实,只要你把每一位拆开看,它就像一张表格一样清晰。

4.1 CAN数据帧结构——一张图看懂

一个标准的数据帧,总共包含7个场。我习惯把它比作一封信:信封上写地址(仲裁场),里面装内容(数据场),最后还要贴个防伪标签(CRC场)。

标准帧(11位ID) vs 扩展帧(29位ID)
日常车载网络里,动力总成和车身控制多用标准帧,因为ID短、实时性好。信息娱乐和ADAS系统因为节点多,常用扩展帧。我个人建议:能用标准帧就别用扩展帧,省带宽。
CAN标准数据帧结构(11位ID) SOF 仲裁场(11位ID+RTR) 控制场 数据场(0~8字节) CRC场 ACK场 EOF 各场说明: • SOF(1位):显性位,标志帧起始,同步所有节点 • 仲裁场(12位):11位ID + RTR位,决定总线访问优先级 • 控制场(6位):IDE + r0 + DLC(4位数据长度码) • 数据场(0~64位):实际传输的数据,最多8字节 • CRC场(16位):15位CRC校验 + 1位CRC界定符 • ACK场(2位):ACK槽 + ACK界定符,接收节点确认 • EOF(7位):隐性位,帧结束标志

4.2 逐场拆解——从SOF到EOF

4.2.1 SOF(帧起始)

SOF只有1位,必须是显性(逻辑0)。它干两件事:一是告诉所有节点“我要发数据了”,二是同步时钟。我记得有一次调试,某个节点SOF位总是发不出显性电平,查了半天发现是CAN收发器的TXD引脚虚焊了。嗯,这种问题示波器一看就露馅。

4.2.2 仲裁场——谁优先级高谁先走

这是CAN协议最巧妙的设计。标准帧的仲裁场包含11位ID和1位RTR。所有节点同时往总线上丢ID位,谁先发出隐性位(1)而别人发显性位(0),谁就退出竞争。

说白了,ID值越小,优先级越高。0x000是最高优先级,0x7FF是最低。

ID范围典型应用优先级
0x000 - 0x1FF动力总成(发动机、变速箱)最高
0x200 - 0x3FF底盘安全(ABS、ESP)高
0x400 - 0x5FF车身控制(门窗、灯光)中
0x600 - 0x7FF信息娱乐、诊断低
避坑指南: 我曾经在一个项目中,把两个节点的ID设成了相同的值。结果总线上一片混乱,两个节点同时发数据,CRC校验全错。记住:同一总线上,每个报文ID必须唯一!

4.2.3 控制场——告诉别人我带了多长的行李

控制场共6位:IDE(1位,标识是标准帧还是扩展帧)、r0(1位保留位,必须为显性)、DLC(4位,数据长度码)。

DLC的取值范围是0~8,对应数据场的字节数。注意:DLC可以设为0,这叫远程帧请求,只问数据不传数据。我见过有人把DLC设成大于8的值,结果接收节点直接报错——协议规定DLC最大就是8。

4.2.4 数据场——真正的干货

数据场长度0~8字节,MSB先发。这里有个小细节:如果DLC=3,但实际只用了2个字节,剩下的1个字节怎么办?协议没规定,但大多数实现会填充0x00或0x55。我个人习惯是严格按照DLC长度发送,不多不少。

4.2.5 CRC场——防篡改的“封条”

CRC场包含15位CRC校验码和1位CRC界定符(隐性)。发送节点对SOF到数据场的所有位做CRC计算,接收节点收到后重新算一遍。如果对不上,接收节点就发错误帧。

CRC多项式是固定的:x¹⁵ + x¹⁴ + x¹⁰ + x⁸ + x⁷ + x⁴ + x³ + 1。你不用背,但要知道它能检测出所有≤5位的突发错误,以及99.998%的其他错误。

4.2.6 ACK场——接收节点的“回执”

ACK场只有2位:ACK槽和ACK界定符。发送节点在ACK槽发隐性位,所有接收节点如果正确收到报文,就在ACK槽发显性位“覆盖”它。如果发送节点发现ACK槽还是隐性,就知道没人收到——重发!

注意: ACK是“线与”逻辑。只要有一个节点正确接收,ACK槽就是显性。我曾经遇到一个情况:总线上只有一个节点在收,但它的CAN控制器坏了,不发ACK。发送节点就一直重发,总线利用率直接拉满。排查时用CANscope看ACK槽,一眼就发现问题。

4.2.7 EOF(帧结束)

EOF是7个连续的隐性位。为什么是7位?因为位填充机制最多连续5个相同电平,7个隐性位足够让所有节点确认帧结束了。EOF之后,总线进入3位的ITM(帧间空间),然后才能发下一帧。

4.3 错误帧——总线出问题时的“警报”

CAN总线最牛的地方,就是它的错误检测和处理机制。错误帧是节点发现错误后主动发出的“求救信号”。

错误帧由两部分组成:

  • 错误标志(6个显性位): 主动错误节点发6个显性位,被动错误节点发6个隐性位
  • 错误界定符(8个隐性位): 结束错误帧

错误类型有5种:

错误类型检测时机说明
位错误发送时发送的位与总线电平不一致(仲裁期间除外)
填充错误接收时检测到连续6个相同电平
CRC错误接收时CRC校验不匹配
形式错误接收时固定格式位(如CRC界定符)不是隐性
ACK错误发送时ACK槽未被覆盖为显性
我的经验: 在实车调试中,最常见的错误是位错误和填充错误。位错误往往是因为收发器电平不匹配,填充错误则多半是某个节点时钟漂移太大。我曾经处理过一个案例:某Tier1的ECU在高温下频繁报填充错误,最后发现是晶振温漂超标。

4.4 过载帧——当节点“忙不过来”时

过载帧和错误帧长得有点像,但用途完全不同。过载帧是接收节点告诉发送节点:“我还没处理完上一帧,你慢点发。”

过载帧的结构:

  • 过载标志(6个显性位): 节点主动发6个显性位
  • 过载界定符(8个隐性位): 结束过载帧

触发过载帧的两种情况:

  1. 接收节点在帧间空间的前两位检测到显性位(本应是隐性)
  2. 接收节点内部缓冲区满,需要延迟下一帧

说实话,过载帧在实际车载网络中很少见。因为CAN的速率(最高1Mbps)和8字节数据场,大多数ECU的处理能力都够用。但如果你在做网关或数据记录仪,过载帧就可能出现——尤其是当CAN总线负载率超过80%时。

小技巧: 用CANalyzer或PCAN查看总线错误时,如果看到连续的错误帧,先别急着怀疑硬件。检查一下总线的终端电阻——两个120Ω电阻缺一个,信号反射就会导致位错误。我至少三次被这个问题坑过,后来养成习惯:上电前先拿万用表量CANH和CANL之间的电阻,60Ω左右才正常。

4.5 本章小结

数据帧的7个场,每个都有它的设计目的。SOF同步、仲裁场解决冲突、CRC保证完整性、ACK确认接收——这套机制让CAN在恶劣的车载环境下依然可靠。错误帧和过载帧则是总线的“自愈”能力,确保一个节点出问题不会拖垮整个网络。