2、CAN网络基础:CAN报文结构详解
各位工程师朋友,今天我们来聊聊CAN报文的结构。说实话,我刚入行那会儿,觉得CAN报文就是一串二进制数据,没什么好看的。直到有一次,我在调试一个ECU通信故障时,拿着示波器一帧一帧地看波形,才真正体会到——报文里的每一个bit,都有它的使命。
CAN报文的标准结构,我习惯把它拆成7个部分:SOF、仲裁场、控制场、数据场、CRC场、ACK场、EOF。咱们一个一个来啃。
2.1 SOF(帧起始)
SOF就是一个显性位(逻辑0)。它告诉总线上的所有节点:“嘿,我要开始发数据了!”
你想想看,总线在空闲时是隐性电平(逻辑1)。SOF这个下降沿,就是整个报文的发令枪。我在项目中遇到过一个问题:某个节点发送的SOF信号上升沿不够陡,导致其他节点同步失败。嗯,这里要注意——SOF的边沿质量,直接影响整个网络的同步精度。
2.2 仲裁场
仲裁场是CAN协议最巧妙的设计之一。它包含两部分:11位标识符(标准帧)或29位标识符(扩展帧),再加上一个RTR位(远程传输请求位)。
说白了,仲裁场就是用来抢总线的。多个节点同时发送时,谁发的标识符数值小(显性位多),谁就赢。我刚开始做CAN开发时,总觉得仲裁是个很复杂的过程。后来用示波器抓了几次波形,发现其实就是“谁先发0,谁就赢”——简单粗暴,但极其高效。
2.3 控制场
控制场有6个位,结构如下:
| 位位置 | 名称 | 说明 |
|---|---|---|
| Bit 0-3 | DLC(数据长度码) | 表示数据场有多少个字节(0-8) |
| Bit 4 | IDE(标识符扩展位) | 0=标准帧,1=扩展帧 |
| Bit 5 | r0(保留位) | 显性位,固定为0 |
我个人习惯在解析报文时,先看DLC。为什么?因为DLC错了,后面的数据场、CRC全都会解析错。我曾经遇到一个案例:某个ECU发送的DLC明明是8,但实际数据只有4个字节有效。结果接收方按照8个字节去解析,把后面的CRC和ACK都当成了数据——整个通信就乱套了。
2.4 数据场
数据场就是报文的“肉”。最多8个字节,最少0个字节。你想想看,一个报文总共也就100多个bit,数据场占了64个bit,超过一半。
数据场的排列方式,我建议遵循Intel格式(小端序)或Motorola格式(大端序)。举个例子:
// 假设要发送一个16位数值 0x1234
// Intel格式(小端序):
Byte0 = 0x34 // 低字节在前
Byte1 = 0x12 // 高字节在后
// Motorola格式(大端序):
Byte0 = 0x12 // 高字节在前
Byte1 = 0x34 // 低字节在后
我在项目中遇到过最坑的事:两个ECU来自不同供应商,一个用Intel格式,一个用Motorola格式。结果车速信号解析出来差了256倍。嗯,从那以后,我每次做系统集成,第一件事就是确认字节序。
2.5 CRC场
CRC场包含15位CRC校验码和1位CRC界定符(隐性位)。CRC算法用的是CRC-15-CAN,生成多项式是:
x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1
说白了,CRC就是给报文加了一把“锁”。接收方收到报文后,用同样的算法算一遍CRC。如果算出来的结果和发送方的不一致,说明数据在传输过程中被干扰了。
我建议大家在测试时,可以故意制造一些CRC错误,看看ECU的容错机制是否正常。我曾经在实验室里用CANstress工具注入CRC错误,结果发现某个ECU连续收到3个CRC错误帧后,直接进入了Bus-Off状态——这其实是不合理的,因为CAN协议本身有重发机制。
2.6 ACK场
ACK场只有2个位:ACK Slot(应答槽)和ACK界定符。
ACK Slot的设计很有意思:发送方发送隐性位(1),接收方如果正确收到报文,就把这个位拉成显性(0)。你想想看,这相当于所有接收节点一起举手说:“我收到了!”
如果没有任何节点应答,ACK Slot就会保持隐性。发送方就知道——没人理我。我在调试时经常用这个特性来判断总线连接是否正常。如果某个节点发送报文后,ACK Slot一直是隐性,那多半是总线断开了,或者终端电阻出了问题。
2.7 EOF(帧结束)
EOF是7个连续的隐性位(1)。它告诉总线:“报文发完了,大家该干嘛干嘛。”
为什么是7个?因为CAN协议规定,连续6个相同位就会触发位填充机制。7个隐性位可以确保EOF不会被误判为填充错误。嗯,这里要注意——EOF之后还有3个ITM(帧间空间)的隐性位,才算真正的总线空闲。
2.8 完整报文结构一览
我把标准帧的完整结构整理成了一张表,方便大家查阅:
| 字段 | 位数 | 说明 |
|---|---|---|
| SOF | 1 | 帧起始,显性位 |
| 标识符 | 11 | 标准帧ID,决定优先级 |
| RTR | 1 | 远程帧标志,0=数据帧,1=远程帧 |
| IDE | 1 | 扩展标志,0=标准帧 |
| r0 | 1 | 保留位 |
| DLC | 4 | 数据长度(0-8) |
| 数据场 | 0-64 | 实际数据,最多8字节 |
| CRC | 15 | CRC校验码 |
| CRC界定符 | 1 | 隐性位 |
| ACK Slot | 1 | 应答位,接收方拉低 |
| ACK界定符 | 1 | 隐性位 |
| EOF | 7 | 帧结束,隐性位 |
| ITM | 3 | 帧间空间 |
最后说一句:CAN报文的结构,是经过几十年验证的经典设计。每一个字段都有它的历史背景和工程考量。我建议大家不要死记硬背,而是拿着示波器去抓几帧实际波形,对照着看一遍——比看十遍书都管用。
下一章,我们会聊CAN的位填充机制和错误处理。到时候见。