第2章:CAN数据帧结构——标准帧与扩展帧

CAN总线上的数据,说白了就是靠一个个“帧”来传递的。我刚开始接触CAN协议栈时,总觉得帧结构就是一堆位段的堆砌,背下来就行了。直到有一次调试一个电机控制器,发现报文老是丢,查了三天才发现是ACK场理解不到位。嗯,从那以后我再也不敢小看这些“位段”了。

这一章,咱们就把CAN数据帧的每一块掰开揉碎,看看它到底长什么样,每个场在干什么活。

2.1 标准帧 vs 扩展帧:差在哪?

CAN 2.0规范定义了两种帧格式:标准帧(11位ID)和扩展帧(29位ID)。你想想看,为什么要有两种?说白了就是早期11位ID够用,后来节点多了,11位不够分,才搞出了29位。

对比项 标准帧 扩展帧
标识符长度 11位 29位
IDE位 显性(0) 隐性(1)
仲裁场长度 12位 32位
最大节点数 约2000个 约5亿个
兼容性 所有控制器支持 需扩展帧控制器
我个人的习惯:如果项目节点数不超过50个,我坚决用标准帧。扩展帧虽然ID多,但仲裁场长了20位,总线利用率会下降。我在一个车载网关项目里试过混用两种帧,结果仲裁逻辑差点把我搞疯。

2.2 数据帧的七段结构

一个完整的数据帧,从开始到结束,一共7个场。我习惯把它想象成一封信:

  • SOF —— 信封上的“开始”戳
  • 仲裁场 —— 收件人地址(谁优先级高谁先发)
  • 控制场 —— 信里装了几页纸
  • 数据场 —— 信的内容(0~8字节)
  • CRC场 —— 防伪码
  • ACK场 —— 收件人回执
  • EOF —— 信封封口
CAN数据帧结构(标准帧) SOF 1位 仲裁场 12位(ID+RTR+IDE) 控制场 6位 数据场 0~64位 CRC场 16位 ACK场 2位 EOF 7位 说明: • 标准帧总长度:1 + 12 + 6 + 0~64 + 16 + 2 + 7 = 44~108位 • 扩展帧仲裁场为32位(29位ID + SRR + IDE + RTR) • 数据场最多8字节,这是CAN协议的历史限制 • 帧结束(EOF)后还有3位ITM(帧间隔),才算一帧彻底完事

2.3 逐场拆解

2.3.1 SOF(帧起始)

SOF只有1位,必须是显性(逻辑0)。为什么?因为总线空闲时是隐性(逻辑1),SOF的显性跳变就是告诉所有节点:“我要开始发了!”

我曾经踩过的坑:有一次调试,示波器上看到SOF波形上升沿不够陡,结果总线上的节点有的识别到了,有的没识别到。后来发现是终端电阻匹配问题。SOF的边沿质量,直接决定了整个帧能不能被正确接收。

2.3.2 仲裁场

这是CAN协议最精彩的部分。标准帧的仲裁场包含:

  • 11位标识符:ID越小,优先级越高
  • RTR位:远程帧请求位(数据帧为显性,远程帧为隐性)
  • IDE位:标识符扩展位(标准帧为显性)

扩展帧的仲裁场更复杂:

  • 29位标识符:分基本ID(11位)和扩展ID(18位)
  • SRR位:替代远程请求位(总是隐性)
  • IDE位:隐性(表示后面还有18位扩展ID)
  • RTR位:同标准帧

仲裁的原理很简单:多个节点同时发送时,谁先发隐性(1)谁就退出。我习惯把ID理解成“数字越小,嗓门越大”。

2.3.3 控制场

控制场共6位:

  • r0、r1:保留位(必须为显性)
  • DLC[3:0]:数据长度码,表示数据场有多少字节

DLC的取值范围是0~8,对应数据场0~8字节。注意:DLC如果大于8,很多控制器会当作8处理,但有些会报错。我建议你严格遵守0~8。

DLC值 数据场长度(字节) 常见用途
0 0 远程帧、心跳包
1~4 1~4 传感器数据、状态字
5~8 5~8 诊断数据、固件升级块

2.3.4 数据场

数据场就是你要传的实际数据,最多8字节。为什么是8?这是上世纪80年代的设计,当时觉得8字节够用了。现在CAN FD已经把这个限制扩展到了64字节,但经典CAN还是8字节。

我的建议:能用4字节传完的数据,别用8字节。数据场越长,总线占用时间越长,低优先级帧的延迟就越大。我在一个48节点的BMS项目里,把所有报文都压缩到4字节以内,总线负载率从42%降到了28%。

2.3.5 CRC场

CRC场包含15位CRC校验码和1位CRC分隔符(隐性)。CRC的生成多项式是固定的:

// CRC-15 生成多项式
// x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1
// 对应二进制:1 1000 1001 1001 1001
#define CRC15_POLY 0x4599

CRC的计算范围是从SOF到数据场结束。接收节点会重新计算CRC,如果和发送的不一致,就丢弃这个帧。

2.3.6 ACK场

ACK场只有2位:

  • ACK槽:发送节点发隐性(1),接收节点如果正确接收,就拉成显性(0)
  • ACK分隔符:必须是隐性(1)

这里有个关键点:只要有一个节点正确接收,ACK槽就是显性。如果总线上没有任何节点响应(比如终端电阻没接好),发送节点就收不到显性ACK,会触发错误帧。

我曾经遇到的情况:一个设备单独测试时一切正常,挂到总线上就疯狂报错。抓波形发现ACK槽一直是隐性。查了半天,原来是该设备的CAN控制器在接收模式下没有使能ACK响应。有些低端CAN芯片默认是不发ACK的,需要手动配置。

2.3.7 EOF(帧结束)

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

2.4 位填充机制

CAN协议有个特殊规则:连续5个相同电平后,必须插入一个相反电平。这叫位填充。目的是保证时钟同步。

举个例子:

原始数据:11111 00000 11111
填充后:  11111 0 00000 1 11111 0
          ^     ^     ^
          填充位  填充位  填充位

注意:SOF到CRC场之间才做位填充,ACK场和EOF不做。为什么?因为ACK场需要多个节点同时驱动,位填充会干扰仲裁。

2.5 错误帧与过载帧

除了数据帧和远程帧,CAN总线上还有两种特殊帧:

  • 错误帧:6个显性位 + 8个隐性位。任何节点检测到错误,就发错误帧中断当前通信。
  • 过载帧:6个显性位 + 8个隐性位。接收节点来不及处理时,可以发过载帧要求发送方等一下。

这两种帧的优先级高于数据帧。所以如果你的总线上错误帧太多,正常数据帧就发不出去。我见过一个案例,总线错误率超过30%,整个系统直接瘫痪。

本章核心要点:

  • 标准帧11位ID,扩展帧29位ID,IDE位决定格式
  • 仲裁场决定优先级,ID越小优先级越高
  • 数据场最多8字节,DLC必须≤8
  • CRC场15位校验,覆盖SOF到数据场
  • ACK场需要至少一个节点响应,否则触发错误
  • EOF为7位隐性,之后有3位帧间隔
  • 位填充每5个相同位插入1个相反位