2. CAN协议帧结构:数据帧、远程帧、错误帧、过载帧、帧间隔详解
CAN总线上跑的数据,说白了就是五种帧。我刚开始接触CAN时,总觉得帧结构很复杂,各种场、位、CRC算来算去。后来调了几个项目才发现,真正需要死磕的其实就两种:数据帧和远程帧。剩下的错误帧、过载帧、帧间隔,更多是总线自己维护用的,你理解它们的触发条件就够了。
咱们一个一个来拆。
2.1 数据帧 —— 最常用的那个
数据帧就是“谁谁谁,我给你带了点东西”。它负责把数据从发送节点送到接收节点。CAN 2.0规范里,数据帧分两种格式:标准帧(11位ID)和扩展帧(29位ID)。
一个完整的数据帧长这样:
帧起始(SOF) → 仲裁场 → 控制场 → 数据场 → CRC场 → ACK场 → 帧结束(EOF)
我画个图,你一眼就能看明白:
每个场都有它的作用。我挑几个容易踩坑的说说。
仲裁场 —— 谁优先级高谁先说话
CAN总线没有主从,所有节点都能发。那冲突了怎么办?靠仲裁。说白了就是:ID越小,优先级越高。发送时每个节点逐位比较,遇到显性位(0)就赢,隐性位(1)就退。
我的经验:设计ID分配时,一定要把实时性要求高的报文放在小ID。比如刹车信号给0x001,车窗控制给0x100。我曾经见过一个项目,把诊断报文ID设得比控制报文还小,结果一跑诊断,车身控制就卡顿。嗯,后来改ID才解决。
控制场 —— DLC别搞错
控制场里的DLC(Data Length Code)告诉别人你带了多少数据。注意:DLC可以设0~8,但如果你设了5,数据场就必须发5个字节,接收方按5个字节收。少了会丢数据,多了会报错。
CRC场 —— 校验不对直接扔
CRC是15位,加上1位界定符。发送节点根据数据场和控制场算出一个CRC值,接收节点自己也算一遍。对不上?直接丢弃,不ACK。发送节点收不到ACK就会重发。
2.2 远程帧 —— 只要数据,不给数据
远程帧很有意思。它跟数据帧长得几乎一样,唯一的区别是:RTR位=1(隐性)。
远程帧的作用是:“喂,谁有ID=0x123的数据?给我发一份。” 它自己不携带数据场,但DLC要填对,告诉别人你想要几个字节。
| 对比项 | 数据帧 | 远程帧 |
|---|---|---|
| RTR位 | 0(显性) | 1(隐性) |
| 数据场 | 0~8字节 | 无 |
| 用途 | 主动发送数据 | 请求数据 |
| 仲裁优先级 | 高于同ID远程帧 | 低于同ID数据帧 |
注意:远程帧的ID如果跟某个数据帧的ID相同,数据帧的优先级更高(因为RTR=0是显性)。所以远程帧不会“抢”数据帧的发送机会。但如果你设计不当,远程帧发得太频繁,会占用总线带宽。
2.3 错误帧 —— 总线自己纠错
错误帧不是人发的,是硬件检测到错误后自动发的。它由两部分组成:错误标志(6个连续显性位) + 错误界定符(8个隐性位)。
CAN总线有5种错误检测机制:
- 位错误:发送节点监控总线,发现发出去的跟总线上的不一样。
- 填充错误:连续5个相同电平后,第6位必须是相反电平,否则报错。
- CRC错误:接收节点算出来的CRC跟发送的不一致。
- 形式错误:固定格式的位(如EOF、界定符)出现错误电平。
- ACK错误:发送节点没检测到ACK应答。
一旦某个节点检测到错误,它会立刻拉出6个显性位,把当前帧破坏掉。其他节点收到这个错误标志后,也会跟着发错误标志。这就是所谓的错误扩散。
我曾经踩过的坑:有次调试电机控制器,CAN总线时不时丢帧。用示波器一抓,发现总线上频繁出现错误帧。查了半天,原来是电机驱动器的高频噪声耦合到了CAN差分线上,导致位错误。后来加了共模扼流圈和终端电阻匹配,问题才解决。所以,错误帧出现时,先查物理层。
2.4 过载帧 —— 我忙,等会儿
过载帧是接收节点发的。它告诉发送节点:“我处理不过来了,你慢点发。” 过载帧的结构跟错误帧很像,也是6个显性位 + 8个隐性位。
触发过载帧的条件有两个:
- 接收节点内部缓冲区满了,来不及处理。
- 接收节点在帧间隔期间检测到显性位(异常情况)。
实际项目中,过载帧出现得不多。如果频繁出现,说明你的接收节点处理能力不够,或者总线负载太高了。
2.5 帧间隔 —— 喘口气再发
帧间隔不是帧,是帧与帧之间的空闲时间。它让总线上的节点有时间切换状态。
帧间隔分三部分:
- 间歇场:3个隐性位。所有节点在这段时间内不能发数据。
- 总线空闲:隐性电平,节点可以开始发送。
- 暂停发送场:只对被动错误节点有效,8个隐性位,让它们多等一会儿。
你想想看,如果没有帧间隔,前一个帧刚结束,下一个帧立刻开始,接收节点可能还没准备好。这3个隐性位就是给总线一个缓冲。
2.6 五种帧的关系 —— 一张图看懂
我把五种帧的交互关系画了个流程图,方便你理解:
2.7 避坑总结
最后,我把自己这些年调CAN总线踩过的坑,浓缩成几条:
- ID分配要留余量:别把ID排得太满,后期加功能时你会感谢自己。
- 远程帧慎用:能不用就不用。远程帧多了,总线负载会飙升,而且容易造成“请求风暴”。
- 错误帧出现先查物理层:终端电阻、线缆长度、共模干扰,这三个是头号嫌疑犯。
- 过载帧频繁说明设计有问题:要么接收节点处理太慢,要么发送频率太高,要么缓冲区太小。
- 帧间隔别忽略:有些低端CAN控制器对帧间隔时间敏感,太短了会丢帧。
嗯,帧结构这块就这些。你把这些搞清楚了,后面调协议栈、写驱动,心里就有底了。