2. CAN数据帧结构:标准帧与扩展帧、ID仲裁机制、数据场与CRC校验
好,咱们今天聊点硬核的。CAN总线上的数据到底长什么样?
很多人学CAN,上来就背帧格式,什么SOF、RTR、DLC……背完就忘。我刚开始也是这样。后来在项目里调了一周的bug,才发现问题出在ID仲裁上。从那以后,我对帧结构的理解就再也不是死记硬背了。
咱们今天就把CAN数据帧拆开,看看每个字段到底在干什么。
2.1 标准帧 vs 扩展帧:到底差在哪?
CAN 2.0规范定义了两种帧格式:标准帧和扩展帧。说白了,区别就在ID的长度上。
| 对比项 | 标准帧 | 扩展帧 |
|---|---|---|
| ID长度 | 11位 | 29位 |
| 最大节点数(理论) | 2^11 = 2048 | 2^29 = 5.3亿 |
| 帧头开销 | 更小,实时性好 | 稍大,但灵活 |
| IDE位 | 0(显性) | 1(隐性) |
| 常见应用 | J1939、CANopen | 车载诊断、私有协议 |
我个人的习惯是:能用标准帧就别用扩展帧。为什么?因为11位ID在仲裁时更快,总线利用率更高。但如果你需要标识很多节点,比如在网关里做路由过滤,29位ID就很有用了。
2.2 ID仲裁机制:谁先说话?
CAN总线最牛的地方,就是它的仲裁机制。多个节点同时发数据,不会冲突,而是按优先级来。
怎么仲裁的?看ID。ID越小,优先级越高。
举个例子:
- 节点A发ID=0x100(二进制:0001 0000 0000)
- 节点B发ID=0x200(二进制:0010 0000 0000)
两个节点同时开始发。第一位都是0,没问题。第二位,A发0,B发0,也没问题。第三位,A发0,B发1。这时候,A的0是显性位,B的1是隐性位。总线电平被A拉低,B检测到电平跟自己发的不一样,就知道自己输了,立刻停止发送,转为接收模式。
整个过程,没有数据丢失,没有重传。这就是CAN的“无损逐位仲裁”。
我曾经在一个项目中,把两个节点的ID设反了。结果一个高优先级的报文总是被低优先级的堵住,导致控制指令延迟。查了两天才发现是ID分配的问题。嗯,从那以后,我每次分配ID都会画一张优先级表。
2.3 数据场:你到底要传什么?
数据场是CAN帧里真正装数据的地方。长度从0到8字节。
为什么是8字节?这是由CAN的位填充机制和实时性要求决定的。8字节以内,可以保证一帧数据在短时间内传完,不会阻塞总线。
数据场的结构很简单:
- DLC(数据长度码): 4位,表示数据场有多少字节。范围0~8。
- 数据字节: 0~8个字节,按MSB(最高有效位)优先发送。
我建议你在设计协议时,尽量把数据场填满。比如一个传感器值占2字节,那就把4个传感器的值拼在一帧里发。这样可以减少总线负载。
2.4 CRC校验:数据到底对不对?
CAN的CRC校验用的是15位CRC,生成多项式是:
x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1
这个多项式能检测出:
- 所有单比特错误
- 所有双比特错误
- 所有奇数个错误
- 所有长度≤15的突发错误
- 99.997%的其他错误
CRC的计算范围是从SOF到数据场结束。接收方收到后,自己也算一遍CRC,跟收到的CRC字段比对。如果不一样,就说明数据出错了,接收方会发一个错误帧。
我记得有一次,在实验室里用CAN分析仪抓数据,发现偶尔会有CRC错误。排查了半天,发现是线缆太长,信号反射导致的。换了双绞线并加了终端电阻,问题就解决了。
2.5 完整帧结构一览
咱们把标准帧和扩展帧的完整结构画出来,你一看就明白了。
2.6 避坑指南:我踩过的那些坑
最后,分享几个我亲身经历过的坑,希望能帮你少走弯路。
- ID分配不合理: 我曾经把高优先级的控制报文ID设得很大,结果总是被低优先级的诊断报文抢了总线。后来重新规划了ID分配,把控制类报文放在0x000~0x0FF,诊断类放在0x100~0x1FF,问题解决。
- DLC设置错误: 有一次,我发了一帧数据,DLC设成2,但数据场里填了4个字节。接收方只取了前2个字节,后2个字节丢了。查了半天才发现是DLC没更新。
- CRC校验不过: 在实验室里,线缆太长,信号反射导致CRC错误。加了终端电阻后,错误率从5%降到了0.01%。
- 扩展帧和标准帧混用: 有些CAN控制器不支持扩展帧,或者需要特殊配置。如果你在项目中混用,一定要确认所有节点都支持。