2. CAN报文结构:标准帧与扩展帧、数据帧、远程帧、错误帧、过载帧、帧间隔
好,咱们直接切入正题。CAN总线上的通信,说白了就是靠“报文”来传递信息。你想想看,一辆车上几十个ECU,大家怎么约定谁什么时候说话、说什么、说多久?全靠这套报文结构来管。
我个人习惯把CAN报文比作一封信。信封上得写清楚“谁发的”、“给谁的”、“多长内容”,还得有校验位防止中途被篡改。CAN 2.0时代定义了两种帧格式,到了CAN FD和CAN XL时代,虽然速率和长度变了,但底层的“信封结构”基本逻辑没变。今天咱们就把这六种帧类型掰开揉碎了讲清楚。
2.1 标准帧 vs 扩展帧:ID长度的博弈
先解决最基础的问题:标准帧和扩展帧到底差在哪?
标准帧的标识符(ID)是11位。什么意思?就是总线上最多能区分2048个不同的报文。早期够用,现在嘛……你想想看,一辆豪华车上的ECU数量早就超过这个数了。
扩展帧把ID扩展到了29位。这一下子,可用的标识符数量就变成了2^29,也就是5亿多个。嗯,短期内肯定够用了。
我在项目中遇到过一个问题:某供应商的网关只支持标准帧,结果我们新设计的域控制器发的是扩展帧。两边对不上,总线直接报错。后来我加了一个ID转换表,把扩展帧映射成标准帧才解决。所以啊,设计初期一定要确认好:你的网络用标准帧还是扩展帧?还是两者混用?
关键区别一览:
| 特性 | 标准帧 | 扩展帧 |
|---|---|---|
| ID长度 | 11位 | 29位 |
| 仲裁场长度 | 12位 | 32位 |
| 最大报文数 | 2048 | 5亿+ |
| 兼容性 | 所有CAN控制器 | 需支持扩展帧的控制器 |
这里有个小技巧:标准帧和扩展帧可以在同一条总线上共存。但要注意,标准帧的优先级永远高于扩展帧——因为标准帧的IDE位是显性(0),而扩展帧的IDE位是隐性(1)。仲裁的时候,显性位赢。嗯,这就是为什么有些关键报文(比如刹车、转向)我建议用标准帧,优先级更高。
3.2 数据帧:最常用的“快递员”
数据帧,顾名思义,就是用来装数据的。CAN FD和CAN XL的数据帧在结构上继承了经典CAN,但做了几个关键升级。
一个完整的数据帧包含以下几个部分:
- 帧起始(SOF):1位显性,告诉总线“我要开始说话了”
- 仲裁场:包含ID和RTR位(远程帧请求位)
- 控制场:包含IDE位、保留位、DLC(数据长度码)
- 数据场:0~8字节(经典CAN),CAN FD最多64字节,CAN XL最多2048字节
- CRC场:校验数据完整性
- ACK场:接收节点回应“收到了”
- 帧结束(EOF):7位隐性,表示帧结束
我个人觉得,数据帧里最容易出问题的是DLC。DLC是4位二进制,理论上能表示0~15,但经典CAN只用到0~8。CAN FD扩展到了0~15,但映射关系变了。举个例子:DLC=9在经典CAN里是非法的,但在CAN FD里表示12字节数据。我曾经见过一个工程师把DLC设成了9,结果老款CAN控制器直接报错,总线卡死。嗯,兼容性问题,永远要小心。
避坑指南: 我曾经在调试CAN FD时,发现数据长度总是对不上。查了半天,原来是DLC编码方式搞错了。经典CAN的DLC是线性映射(DLC=8表示8字节),但CAN FD的DLC是分段映射(DLC=9表示12字节,DLC=10表示16字节,以此类推)。记住这张表:
| DLC值 | 经典CAN数据长度 | CAN FD数据长度 |
|---|---|---|
| 0~8 | 0~8字节 | 0~8字节 |
| 9 | 非法 | 12字节 |
| 10 | 非法 | 16字节 |
| 11 | 非法 | 20字节 |
| 12 | 非法 | 24字节 |
| 13 | 非法 | 32字节 |
| 14 | 非法 | 48字节 |
| 15 | 非法 | 64字节 |
2.3 远程帧:问别人要数据
远程帧,说白了就是“请求帧”。一个节点发远程帧,意思就是:“谁有某某数据?请发给我。”
远程帧的结构和数据帧几乎一样,唯一的区别是RTR位。数据帧的RTR是显性(0),远程帧的RTR是隐性(1)。另外,远程帧没有数据场——你想想看,既然是请求别人发数据,你自己带数据干嘛?
我建议你少用远程帧。为什么?因为它在实际项目中容易引发“帧冲突”。举个例子:节点A发远程帧请求数据,节点B收到后回复数据帧。但如果节点B还没来得及回复,节点A又发了一个远程帧……总线就乱了。我在一个项目中见过这种“远程帧风暴”,最后不得不改成定时发送数据帧的方案。
注意: 远程帧在CAN FD和CAN XL中仍然存在,但使用场景越来越少。我个人建议,能用数据帧轮询就别用远程帧。除非你有非常明确的请求-响应模型,而且确保不会出现并发请求。
2.4 错误帧:总线的“警报器”
错误帧是CAN总线自我保护的机制。当任何一个节点检测到错误,就会立刻发送错误帧,通知所有节点:“刚才那帧数据有问题,大家别用。”
错误帧由两部分组成:
- 错误标志:6个连续显性位(主动错误)或6个连续隐性位(被动错误)
- 错误界定符:8个连续隐性位
为什么会这样设计?你想想看,6个显性位在CAN总线协议里是“非法”的——因为正常帧里不可能连续出现6个相同电平。所以一旦出现6个显性位,所有节点都知道“出错了”。
我在调试时最怕看到错误帧。有一次,一辆测试车的CAN总线每隔几秒就冒一个错误帧。查了三天,最后发现是一个ECU的CAN收发器焊脚虚焊,导致信号电平不稳定。嗯,硬件问题往往比软件问题更难排查。
2.5 过载帧:说“我忙,等会儿”
过载帧和错误帧长得很像,但用途完全不同。过载帧的意思是:“我太忙了,处理不过来,你慢点发。”
过载帧的结构:
- 过载标志:6个显性位
- 过载界定符:8个隐性位
你可能会问:这和错误帧有什么区别?区别在于触发条件。错误帧是检测到总线错误时触发,而过载帧是接收节点主动发出的——比如接收缓冲区满了,或者内部处理延迟。
说实话,我在实际项目中很少见到过载帧。因为现在的CAN控制器缓冲区都很大,而且CAN FD的速率足够快。但如果你在做低端MCU或者老旧硬件的兼容性测试,还是要注意一下——有些老芯片的接收缓冲区只有2帧,稍微一忙就发过载帧。
2.6 帧间隔:总线的“呼吸时间”
帧间隔,就是两帧报文之间的空闲时间。它让总线上的节点有时间处理上一帧数据,准备接收下一帧。
帧间隔包含:
- 间歇场:3个隐性位
- 总线空闲:任意长度的隐性位(直到有节点开始发送)
这里有个细节:被动错误节点在发送完一帧后,还需要额外发送8个隐性位的“挂起传输”场。为什么?因为被动错误节点不能主动发起传输,必须等总线空闲一段时间后才能尝试。这是为了防止错误节点一直霸占总线。
我记得有一次,一个同事问我:“为什么我的节点发完一帧后,要等好久才能发下一帧?”我一看,原来他的节点进入了被动错误状态,每次发完都要等8个位的挂起时间。嗯,这就是帧间隔在起作用。
总结一下:
- 标准帧 vs 扩展帧:ID长度不同,优先级不同,混用时注意兼容性
- 数据帧:最常用,注意DLC编码在CAN FD中的变化
- 远程帧:请求数据用,但建议少用,容易引发冲突
- 错误帧:总线自我保护,出现时说明有硬件或软件问题
- 过载帧:接收节点忙不过来时触发,现在很少见
- 帧间隔:总线休息时间,被动错误节点有额外等待
这六种帧类型,就是CAN总线通信的全部“语法”。你只要掌握了它们,就能读懂总线上的每一句话。下一章,咱们聊聊CAN FD和CAN XL的物理层——为什么它们能跑那么快?