2. CAN帧结构:数据帧、远程帧、错误帧、过载帧、帧间隔的详细解析
好,咱们今天聊聊CAN总线的帧结构。说实话,搞懂这五种帧,你排查总线故障就成功了一半。我刚开始接触CAN时,总觉得帧结构就是一堆协议规范,背下来就行。直到有一次在项目现场,示波器上波形乱成一团,我才意识到——不理解帧结构,你连问题出在哪都不知道。
2.1 数据帧:最常用的“快递员”
数据帧,说白了就是发数据的。一个节点想告诉另一个节点“我现在电压是3.3V”,就用数据帧。它分标准帧和扩展帧,区别在于ID长度。
标准帧结构(11位ID):
- 帧起始(SOF):1个显性位。告诉总线“我要开始说话了”。
- 仲裁场:11位ID + RTR位(数据帧为显性)。这里决定了谁有优先权。
- 控制场:IDE位 + r0保留位 + DLC(4位数据长度)。DLC范围0-8,表示后面跟几个字节。
- 数据场:0-8字节。你真正想传的数据。
- CRC场:15位CRC + 1位CRC界定符。校验数据有没有传错。
- ACK场:1位ACK槽 + 1位ACK界定符。接收节点会在这里回应“我收到了”。
- 帧结束(EOF):7个隐性位。表示帧结束了。
关键点:数据帧的RTR位必须是显性(0)。这是它和远程帧最明显的区别。
我在项目中遇到过一件事:有个ECU老是丢数据,查了半天发现是DLC设置错了。发送方说我要发8字节,实际只填了3个有效字节,接收方读到的全是垃圾。嗯,DLC一定要和实际数据长度匹配。
2.2 远程帧:只问不拿的“侦察兵”
远程帧很有意思。它不携带数据,只发一个请求。比如节点A想知道节点B的温度,就发一个远程帧,ID填温度传感器的ID。节点B收到后,再发一个数据帧回来。
远程帧结构:
- 帧结构基本和数据帧一样,但有两个关键区别:
- RTR位为隐性(1):这是远程帧的标志。
- 数据场为空:DLC虽然可以写,但实际不传数据字节。
避坑指南:我曾经见过一个工程师,远程帧的DLC写的是8,结果接收方以为要等8字节数据,一直等不到,直接超时报错。远程帧的DLC建议写0,或者和请求的数据帧DLC一致,但别指望它带数据。
你想想看,远程帧的优先级怎么算?它和数据帧用同一个ID竞争总线。如果ID相同,数据帧的RTR是显性,远程帧是隐性,显性胜出。所以数据帧永远比同ID的远程帧优先。这设计挺合理——你总得先让有数据的人说话吧?
2.3 错误帧:总线的“警报器”
错误帧是CAN总线自己发的。只要检测到错误,任何节点都可以立刻打断当前通信,发出错误帧。它分两种:主动错误帧和被动错误帧。
主动错误帧:
- 6个连续显性位(错误标志)+ 8个隐性位(错误界定符)。
- 主动错误状态的节点会发这个。它很强势,直接覆盖总线。
被动错误帧:
- 6个连续隐性位(错误标志)+ 8个隐性位(错误界定符)。
- 被动错误状态的节点只能发隐性位,不敢干扰总线。
注意:错误帧不是用来传数据的,它是用来破坏数据的。一旦出现错误帧,当前正在传输的帧会被强制终止,所有节点都会丢弃这个帧,然后重发。
我记得有一次调试,示波器上全是错误帧,总线利用率直接飙到90%以上。查到最后发现是一个节点的CAN收发器坏了,一直在发显性位。嗯,错误帧频繁出现,基本就是硬件问题,别老盯着软件看。
2.4 过载帧:总线的“稍等一下”
过载帧用的不多,但很重要。它表示“我太忙了,你慢点发”。比如一个节点CPU负载过高,来不及处理接收到的数据,就会发过载帧,让发送方等一等。
过载帧结构:
- 6个显性位(过载标志)+ 8个隐性位(过载界定符)。
- 和主动错误帧长得一模一样,但触发条件不同。
触发过载帧的条件有两个:
- 接收节点在帧间隔期间检测到显性位(本该是隐性的)。
- 接收节点来不及处理上一帧数据,内部缓冲区满了。
实战经验:我曾经在一个多节点系统中,发现某个节点频繁发过载帧。一开始以为是总线负载太高,后来发现是这个节点的中断优先级设置太低,数据来了它不处理,缓冲区一直满。调整中断优先级后,过载帧就消失了。
2.5 帧间隔:总线的“休息时间”
帧间隔不是帧,它是帧与帧之间的空闲时间。就像人说话,说完一句总要喘口气吧?CAN总线也一样。
帧间隔结构:
- 间歇场:3个隐性位。这是强制性的,任何节点都不能在这期间发显性位。
- 总线空闲:任意长度的隐性位。直到有节点开始发SOF。
- 暂停场(仅被动错误节点):8个隐性位。被动错误的节点在发完帧后,要多等8个位才能继续发。
你想想看,为什么要有帧间隔?如果没有它,一个节点刚发完帧,另一个节点立刻抢总线,很容易造成冲突。这3个隐性位给了所有节点一个缓冲时间,让总线状态稳定下来。
关键点:帧间隔期间,任何节点都可以开始发送。但如果有节点在间歇场发了显性位,其他节点就会认为这是过载帧,然后发过载标志。所以,严格遵守3个隐性位很重要。
2.6 五种帧的对比总结
| 帧类型 | 用途 | 数据场 | RTR位 | 触发方式 |
|---|---|---|---|---|
| 数据帧 | 发送数据 | 0-8字节 | 显性(0) | 节点主动发送 |
| 远程帧 | 请求数据 | 无 | 隐性(1) | 节点主动发送 |
| 错误帧 | 报告错误 | 无 | N/A | 检测到错误时自动触发 |
| 过载帧 | 请求延迟 | 无 | N/A | 接收节点忙时触发 |
| 帧间隔 | 帧间分隔 | 无 | N/A | 每帧结束后自动出现 |
我个人习惯在排查故障时,先看示波器上有没有错误帧或过载帧。如果有,八成是硬件问题或节点处理不过来。如果全是数据帧和远程帧,但通信还是不正常,那就去查ID和DLC。嗯,帧结构这东西,你越熟悉,排查问题就越快。
最后提醒:别小看帧间隔。我见过一个项目,总线偶尔丢帧,查了三天,最后发现是一个节点的晶振频率偏了,导致帧间隔时间不对,其他节点误判为过载帧。时钟精度,真的很重要。