第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文件。到时候我会分享一些我自己的配置模板,让你少走弯路。
好了,今天就到这里。有什么问题,欢迎在评论区留言。咱们下章见。