2. CAN协议帧结构:标准帧与扩展帧、数据帧、远程帧、错误帧、过载帧、帧间隔
好,咱们今天来啃一块硬骨头——CAN协议的帧结构。说实话,我刚入行那会儿,看着协议栈里一堆ID、DLC、CRC,头都大了。但后来我发现,只要搞懂了这几种帧是干什么的,CAN总线在你眼里就不再是黑盒子了。
CAN总线上跑的数据,说白了就五种帧:数据帧、远程帧、错误帧、过载帧,以及帧间隔。其中数据帧又分标准帧和扩展帧。嗯,咱们一个一个来拆解。
2.1 数据帧:最常用的“快递包裹”
数据帧是CAN通信的主角。你想发一个传感器值、一个车速信号,都得用它。它就像快递包裹,把数据从发送节点送到接收节点。
数据帧的结构,我习惯把它分成七段:
- 帧起始(SOF):一个显性位,告诉总线“我要开始发数据了”。
- 仲裁场:包含ID和RTR位。ID决定了优先级,数字越小优先级越高。
- 控制场:包含IDE位、保留位和DLC(数据长度码)。DLC告诉你这个帧里装了多少字节数据。
- 数据场:0~8字节,真正的数据内容。
- CRC场:15位CRC校验码+1位CRC界定符。用来检查数据有没有传错。
- 应答场(ACK):接收节点如果正确收到数据,会在这里拉一个显性位作为回应。
- 帧结束(EOF):7个隐性位,表示帧传输完毕。
重点记忆:数据帧的RTR位必须是显性(0),这是它和远程帧最核心的区别。
2.2 标准帧 vs 扩展帧:ID长度的区别
标准帧的ID是11位,扩展帧的ID是29位。为什么要有两种?
我举个例子。早期CAN总线用在汽车上,节点不多,11位ID(最多2048个不同ID)完全够用。后来车上的ECU越来越多,11位ID不够分了,于是扩展帧应运而生。
它们的区别主要体现在仲裁场:
| 特性 | 标准帧 | 扩展帧 |
|---|---|---|
| ID长度 | 11位 | 29位 |
| IDE位 | 显性(0) | 隐性(1) |
| SRR位 | 无 | 隐性(1),替代RTR位 |
| 最大节点数 | 2048 | 5.3亿 |
| 兼容性 | 所有控制器都支持 | 需要控制器支持扩展帧 |
我在项目中遇到过一个问题:一个老款ECU只支持标准帧,但新设计的网关用了扩展帧。结果两者无法通信。后来我加了一个ID转换层,把扩展帧的高18位映射成标准帧的虚拟ID,才解决了问题。嗯,这里要注意:标准帧和扩展帧不能在同一总线上混用,除非你做了协议转换。
2.3 远程帧:请求数据的“挂号信”
远程帧,说白了就是“我要数据,你发给我”。它和数据帧长得几乎一样,唯一的区别是RTR位是隐性(1),而且没有数据场。
你想想看,一个节点想读取另一个节点的传感器值,它不需要自己发数据,只需要发一个远程帧,目标节点收到后就会自动回复一个数据帧。
远程帧的典型应用场景:
- 诊断工具请求ECU的故障码
- 主节点轮询从节点的状态
- 网关请求特定信号值
我的经验:远程帧虽然方便,但不要滥用。我曾经在一个项目中,主节点每10ms发一次远程帧轮询所有从节点,结果总线负载率直接飙到80%。后来改成了事件触发+周期上报的混合模式,负载率降到了30%。
2.4 错误帧:总线的“交警”
错误帧是CAN总线自我纠错的核心机制。当任何节点检测到总线错误时,它会立即发送错误帧,通知所有节点“刚才那帧数据有问题,大家别用了”。
错误帧由两部分组成:
- 错误标志:6个连续相同位(显性或隐性,取决于错误类型)
- 错误界定符:8个隐性位
错误类型有五种:
- 位错误:发送节点监控总线,发现发送的位和总线上的位不一致。
- 填充错误:连续5个相同位后没有插入填充位。
- CRC错误:接收节点计算的CRC和发送的CRC不匹配。
- 形式错误:固定格式的位(如EOF、ACK界定符)出现错误电平。
- 应答错误:发送节点没有收到ACK应答。
避坑指南:我曾经调试过一个项目,总线频繁出现错误帧。查了半天,发现是一个节点的终端电阻焊错了,120欧姆焊成了12欧姆。结果信号反射严重,CRC错误频发。所以,物理层的问题往往比协议层更隐蔽。
2.5 过载帧:总线的“请稍等”
过载帧的作用很简单:接收节点告诉发送节点“我忙不过来,你慢点发”。它和错误帧结构类似,但触发条件不同。
过载帧的触发条件:
- 接收节点内部缓冲区满了,无法处理新数据
- 接收节点检测到总线上的位填充规则被破坏(但这不是错误,只是过载)
过载帧由过载标志(6个显性位)和过载界定符(8个隐性位)组成。嗯,这里要注意:过载帧不能连续发送,两次过载帧之间必须有至少3个隐性位间隔。
2.6 帧间隔:总线的“呼吸时间”
帧间隔,就是两帧数据之间的空闲时间。它让总线上的节点有时间处理上一帧数据,并为下一帧做准备。
帧间隔由三部分组成:
- 间歇场:3个隐性位
- 总线空闲:任意长度的隐性位,直到有节点开始发送
- 暂停发送场(仅被动错误节点):8个隐性位,让被动错误节点有更多时间恢复
为什么要有帧间隔?你想想看,如果节点连续发帧,接收节点可能来不及处理。帧间隔就像给总线一个“喘息”的机会。
实战要点:在OBD通信中,诊断请求和响应之间必须满足帧间隔要求。我见过一个案例,诊断仪连续发送请求帧,没有留帧间隔,结果ECU直接进入了bus-off状态。所以,写代码时一定要在发送循环中加入帧间隔延时。
2.7 总结:五种帧的对比
| 帧类型 | 用途 | 数据场 | RTR位 | 优先级 |
|---|---|---|---|---|
| 数据帧 | 发送数据 | 0~8字节 | 显性(0) | 高 |
| 远程帧 | 请求数据 | 无 | 隐性(1) | 中 |
| 错误帧 | 报告错误 | 无 | N/A | 最高 |
| 过载帧 | 请求延迟 | 无 | N/A | 高 |
| 帧间隔 | 总线空闲 | 无 | N/A | 最低 |
我个人习惯,在调试CAN总线时,先用逻辑分析仪抓一帧数据,看看是标准帧还是扩展帧,RTR位是0还是1。这能快速判断通信是否正常。记住一句话:数据帧干活,远程帧要活,错误帧报警,过载帧喊停,帧间隔休息。搞懂了这五种帧,CAN协议你就入门了。