第2章:CAN报文基础:CAN帧结构、ID与DLC解析、数据场解读、物理层要点

大家好,我是老周。今天咱们来啃一块硬骨头——CAN报文的基础知识。说实话,很多工程师干了三五年,报文抓了一大堆,但真要问他标准帧和扩展帧到底差在哪,ID的优先级怎么算,他可能还得翻手册。嗯,这章咱们就把这些基础打扎实了。

2.1 CAN帧结构:标准帧 vs 扩展帧

CAN总线上的报文,说白了就是一串高低电平的组合。但为了让所有节点都能读懂,我们得有个统一的格式。这就是CAN帧结构。

CAN帧分两种:标准帧(CAN 2.0A)和扩展帧(CAN 2.0B)。我个人的习惯是,能用标准帧就别用扩展帧,除非ID真的不够用了。

2.1.1 标准帧结构

标准帧的ID是11位的。它的帧结构长这样:

SOF + 11位ID + RTR + IDE + r0 + 4位DLC + 0~8字节数据 + CRC + ACK + EOF

关键字段说明:

  • SOF(Start of Frame):1位显性位,表示帧开始。说白了就是告诉所有节点:“我要发数据了,你们听着。”
  • ID(Identifier):11位,决定优先级。数值越小,优先级越高。我在项目中遇到过有人把ID设成0x7FF,结果这报文永远发不出去,因为优先级太低了。
  • RTR(Remote Transmission Request):1位。显性表示数据帧,隐性表示远程帧。远程帧这东西,说实话我用了十年CAN,真正用到的场景很少。
  • IDE(Identifier Extension):1位。标准帧里IDE为显性(0),扩展帧里IDE为隐性(1)。这是区分两种帧的关键位。
  • DLC(Data Length Code):4位。表示数据场有多少字节。注意,DLC可以设0~8,但如果你设了5,实际数据场只有5个字节有效,后面3个字节是垃圾数据。我曾经踩过这个坑。
  • Data Field:0~8字节。这就是我们要传的实际数据。
  • CRC:15位校验码。保证数据没被干扰。
  • ACK:应答位。接收节点如果正确收到,会拉低这个位告诉发送方:“我收到了。”
  • EOF:7位隐性位,表示帧结束。

2.1.2 扩展帧结构

扩展帧的ID是29位的。结构上比标准帧多了18位:

SOF + 11位基本ID + SRR + IDE + 18位扩展ID + RTR + r1 + r0 + 4位DLC + 0~8字节数据 + CRC + ACK + EOF

这里有个容易混淆的地方:扩展帧的IDE位是隐性的(1),而标准帧是显性的(0)。所以CAN控制器看到IDE位是1,就知道后面还有18位扩展ID要读。

重要区别:

  • 标准帧:11位ID,帧长度最短44位(无数据时)
  • 扩展帧:29位ID,帧长度最短64位(无数据时)
  • 扩展帧的优先级计算:基本ID(11位)优先,相同再看扩展ID(18位)

2.2 ID与DLC解析

ID和DLC是CAN报文里最常用的两个字段。你想想看,每次抓报文,第一眼看的就是ID,第二眼就是DLC和数据。

2.2.1 ID的优先级机制

CAN总线的仲裁机制,说白了就是“谁的数字小,谁先走”。为什么?因为显性位(0)会覆盖隐性位(1)。

举个例子:

  • 节点A发ID=0x100(二进制:001 0000 0000)
  • 节点B发ID=0x200(二进制:010 0000 0000)

在仲裁场,A和B同时发送。第一位,A发0,B发0,没问题。第二位,A发0,B发1。这时候B的1被A的0覆盖了,B检测到总线上的电平和自己发的不一样,就知道自己仲裁失败了,乖乖退出。A继续发送。

所以,ID越小,优先级越高。0x000是最高优先级,0x7FF是最低(标准帧)。

我的经验:设计CAN网络时,我会把关键报文(如刹车、转向)分配小ID,把非关键报文(如车窗、空调)分配大ID。这样即使总线负载高,关键报文也能优先通过。

2.2.2 DLC的陷阱

DLC是4位,理论上可以表示0~15。但CAN协议规定,数据场最多8字节。所以:

  • DLC=0~8:数据场长度就是DLC的值
  • DLC=9~15:数据场长度仍然是8字节

嗯,这里要注意。有些控制器会把DLC>8当成错误处理。我曾经在项目里遇到过,一个供应商的ECU发送DLC=12的报文,结果接收方的CAN控制器直接报错,丢掉了所有数据。排查了两天才发现是DLC的问题。

避坑指南:我建议你永远不要设DLC大于8。虽然协议允许,但很多控制器实现不规范。老老实实用0~8,兼容性最好。

2.3 数据场(Data Field)解读

数据场是CAN报文的灵魂。前面那些ID、DLC都是“信封”,数据场才是“信”。

2.3.1 字节序:Motorola vs Intel

这是新手最容易搞混的地方。同一个信号,用Motorola格式和Intel格式解析,结果完全不同。

特性 Motorola(大端) Intel(小端)
高字节位置 低地址(起始字节) 高地址(结束字节)
低字节位置 高地址(结束字节) 低地址(起始字节)
常见场景 德国车系(奔驰、宝马) 美系、日系、大部分中国车

举个例子,数据场是 [0x12, 0x34, 0x56, 0x78]:

  • Motorola解析一个16位信号:0x1234(高字节在前)
  • Intel解析同一个16位信号:0x3412(低字节在前)

你看,差很多吧?我刚开始做CAN测试时,就因为搞混了字节序,把一个车速信号从120km/h解析成了48km/h,差点把测试报告交上去。还好同事看了一眼说“这车速不对吧”,才避免了一次乌龙。

2.3.2 信号缩放与偏移

原始数据场里的值,通常不是物理量。需要经过缩放和偏移才能变成我们看得懂的值。

公式很简单:

物理值 = 原始值 × 缩放因子 + 偏移量

举个例子:

  • 原始值:0x1A4(十进制420)
  • 缩放因子:0.1
  • 偏移量:-40
  • 物理值:420 × 0.1 + (-40) = 2°C

嗯,这就是为什么你看到CAN报文里全是0x1A4这种“奇怪”的数字,但实际温度只有2度。

我的习惯:在CANoe里,我会用CAPL脚本或者Signal Mapping功能,自动做缩放和偏移。手动算太容易出错了,尤其是信号多了以后。

2.4 CAN总线物理层要点

物理层这东西,很多做应用层的工程师不太关心。但我觉得,了解一点物理层,对排查问题很有帮助。

2.4.1 差分信号

CAN总线用两条线:CAN_H和CAN_L。信号通过这两条线的电压差来传输。

  • 显性位(逻辑0):CAN_H=3.5V,CAN_L=1.5V,差分电压=2V
  • 隐性位(逻辑1):CAN_H=2.5V,CAN_L=2.5V,差分电压=0V

为什么用差分?说白了就是抗干扰。外界噪声会同时影响CAN_H和CAN_L,但差分电压不变。你想想看,在汽车这种电磁环境恶劣的地方,差分信号简直是救命稻草。

2.4.2 终端电阻

CAN总线两端必须各接一个120Ω的终端电阻。为什么?

  • 匹配阻抗,防止信号反射
  • 保证总线在隐性状态时,差分电压为0V

我遇到过好几次,测试时发现CAN通信时好时坏,波形乱七八糟。最后发现是终端电阻没接,或者接了一个120Ω但位置不对。嗯,这个坑我踩过不止一次。

避坑指南:用万用表量CAN_H和CAN_L之间的电阻。如果总线没上电,应该量到60Ω(两个120Ω并联)。如果量到120Ω,说明只接了一个终端电阻。如果量到0Ω或无穷大,说明短路或断路了。

2.4.3 波特率与总线长度

CAN总线的波特率越高,总线长度越短。这是物理限制,没办法。

波特率 最大总线长度 常见场景
125 kbps 500米 工业控制、建筑自动化
250 kbps 250米 J1939商用车
500 kbps 100米 乘用车动力CAN
1 Mbps 40米 测试台架、短距离通信

我记得有一次,客户说他们的CAN通信在实验室好好的,装到车上就不行了。我过去一看,总线长度超过了80米,但波特率设的是1Mbps。嗯,这能稳定才怪。后来降到500kbps,问题解决了。

2.5 小结

这一章我们聊了CAN帧的结构、ID和DLC的解析、数据场的解读方法,还有物理层的几个关键点。说实话,这些内容看起来基础,但真正用好了,能帮你省下不少排查问题的时间。

下一章,我们会讲如何在CANoe里配置CAN通道和DBC文件。到时候我会分享一些我自己的配置模板,让你少走弯路。

好了,今天就到这里。有什么问题,欢迎在评论区留言。咱们下章见。