2. CAN总线基础
CAN总线上跑的数据,说白了就是几种固定的“信封”。你只要搞懂这几种信封长什么样,排查故障时一眼就能看出问题出在哪。我刚开始接触CAN的时候,总觉得帧结构太枯燥,直到有一次在产线上抓到一个“幽灵帧”,折腾了两天,最后发现是远程帧的RTR位被误配了。从那以后,我再也不敢小看这些基础结构。
一、标准帧 vs 扩展帧
CAN 2.0规范定义了两种帧格式:标准帧(11位ID)和扩展帧(29位ID)。它们最大的区别就是仲裁场的长度。
| 对比项 | 标准帧 | 扩展帧 |
|---|---|---|
| 标识符长度 | 11位 | 29位 |
| IDE位 | 显性(0) | 隐性(1) |
| 帧长度(数据场0字节) | 47位 | 67位 |
| 兼容性 | 所有CAN控制器都支持 | 需控制器支持CAN 2.0B |
我个人习惯在项目初期就定好:能用标准帧就别用扩展帧。为什么?因为扩展帧多出来的18位ID,在总线上就是实打实的传输时间。你想想看,如果一条线上有几百条报文,每帧多20位,总线负载率直接飙升。
二、数据帧格式详解
数据帧是CAN总线最常用的帧类型。它负责把数据从发送节点送到接收节点。一个完整的数据帧由7个场组成:
- 帧起始(SOF):1位显性,同步所有节点的时钟
- 仲裁场:11位ID(标准帧)或29位ID(扩展帧)+ RTR位
- 控制场:IDE位、保留位r0/r1、DLC(4位数据长度码)
- 数据场:0~8字节,实际要传输的数据
- CRC场:15位CRC校验码 + 1位CRC界定符
- ACK场:1位ACK槽 + 1位ACK界定符
- 帧结束(EOF):7位隐性
这里有个关键点:DLC虽然只有4位,但它能表示0~15。但CAN协议规定数据场最多8字节。所以DLC值大于8时,控制器怎么处理?嗯,不同厂家做法不一样。有的直接发8字节,有的报错。我建议你写代码时,DLC一定要和实际数据长度严格对应,别偷懒。
三、远程帧
远程帧说白了就是“请求帧”。发送节点发一个远程帧,告诉目标节点:“把你那个ID的数据发给我”。远程帧和数据帧的结构几乎一样,唯一的区别是RTR位:数据帧的RTR是显性(0),远程帧的RTR是隐性(1)。
远程帧没有数据场。它的DLC字段表示请求的数据长度。举个例子:你发一个ID=0x123、DLC=4的远程帧,收到这个帧的节点就应该回复一个ID=0x123、数据场4字节的数据帧。
四、错误帧
错误帧是CAN总线最“暴力”的机制。只要任何一个节点检测到错误,它就会立即发送错误帧,把当前传输的报文“撕碎”。错误帧由两部分组成:
- 错误标志:6个连续显性位(主动错误)或6个连续隐性位(被动错误)
- 错误界定符:8个连续隐性位
为什么是6个显性位?因为CAN的位填充规则规定,连续5个相同电平后必须插入一个反相电平。6个连续显性位直接违反了位填充规则,所有节点都会检测到这个错误,然后各自发送错误帧。这就是所谓的“错误扩散”——一个节点犯错,全总线跟着遭殃。
我记得有一次在台架上调试,总线上突然出现大量错误帧。用CANalyzer一看,错误计数器蹭蹭往上涨。最后定位到是一个节点的晶振偏差太大,导致位时序错位。换了个晶振,问题解决。
五、过载帧
过载帧和错误帧长得有点像,但用途完全不同。过载帧是接收节点主动发出的,意思是:“我太忙了,处理不过来,你慢点发”。过载帧的结构:
- 过载标志:6个连续显性位
- 过载界定符:8个连续隐性位
过载帧只在两种情况下产生:
- 接收节点在帧间隔期间检测到显性位(误以为有新帧开始)
- 接收节点的内部缓冲区满了,需要额外时间处理
说实话,我在实际项目中很少见到过载帧。现在的CAN控制器处理能力都很强,除非你设计得太极限——比如把中断优先级设得比CAN接收中断还高,导致CAN数据来不及处理。嗯,这种坑我踩过。
六、帧间隔
帧间隔是两帧之间的“休息时间”。它由3个隐性位组成。任何节点在发送完一帧后,都必须等待这3个隐性位才能发送下一帧。这个机制保证了总线上的节点不会“抢跑”。
帧间隔还有一个特殊作用:错误被动节点在发送完一帧后,需要等待8个隐性位(而不是3个)才能继续发送。这是协议对“问题节点”的一种惩罚机制——你越容易出错,就越少给你说话的机会。
七、帧结构关系图
下面这张图把CAN总线上所有帧类型的关系梳理了一遍。你排查故障时,可以对照这张图快速定位问题出在哪个环节。
八、实战总结
搞懂这几种帧结构,你排查CAN故障时就能做到“看帧识问题”:
- 总线上全是错误帧?先查位时序和终端电阻
- 收不到数据但错误计数器没涨?检查ID格式(标准/扩展)是否匹配
- 数据偶尔丢帧?看看是不是远程帧的RTR位配错了
- 总线负载率异常高?检查帧间隔是否被破坏,或者有过载帧在捣乱