2、CAN协议基础:CAN协议分层结构、数据帧格式、远程帧、错误帧、过载帧、帧间隔
好,咱们正式开始啃CAN协议这块硬骨头。说实话,我刚入行那会儿,觉得CAN协议就是一堆帧啊、位啊,枯燥得很。直到有一次调试一个电机控制器,总线死活不通,我拿着示波器一帧一帧地看,才真正理解了这些帧结构有多重要。今天我就把压箱底的经验掰开揉碎了讲给你听。
2.1 CAN协议的分层结构
CAN协议其实只定义了物理层和数据链路层。你想想看,它不像TCP/IP那么复杂,没有会话层、表示层那些花里胡哨的东西。为什么?因为CAN最初就是为汽车设计的,讲究的就是实时和可靠。
物理层:说白了就是信号怎么在线上跑。CAN总线用两根线——CAN_H和CAN_L,靠差分电压传输。我习惯叫它“隐形”和“显性”电平。显性位(逻辑0)会把总线拉低,隐形位(逻辑1)则让总线保持高阻。嗯,这里要注意:多个节点同时发送显性位时,总线还是显性,这就是CAN的“线与”特性。
数据链路层:这一层才是重头戏。它又拆成两个子层:
- LLC(逻辑链路控制):负责报文滤波、过载通知、恢复管理。说白了就是决定“这帧数据我该不该收”。
- MAC(介质访问控制):这才是CAN协议的核心。帧的封装、拆装、错误检测、应答、仲裁,全在这里。我在项目中遇到过一个问题:两个节点同时发数据,为什么不会冲突?答案就在MAC层的仲裁机制里。
核心要点:CAN的分层结构虽然简单,但每一层都干得漂亮。物理层保证信号可靠,数据链路层保证数据不丢、不乱。你写驱动时,其实主要就是在跟MAC层打交道。
2.2 数据帧格式
数据帧是CAN总线用得最多的帧类型。它负责把数据从发送节点传到接收节点。标准帧有11位ID,扩展帧有29位ID。我个人习惯用标准帧,因为简单、实时性好。但如果你需要挂很多节点,扩展帧更合适。
来看标准数据帧的结构:
SOF | 11位ID | RTR | IDE | r0 | DLC | 0-8字节数据 | CRC | ACK | EOF
咱们拆开说:
- SOF(帧起始):1个显性位。告诉总线“我要开始发了”。
- 仲裁场:11位ID + RTR位。ID越小优先级越高。RTR位在数据帧里是显性(0),远程帧里是隐性(1)。
- 控制场:IDE + r0 + DLC。IDE用来区分标准帧和扩展帧,DLC表示数据长度(0-8字节)。
- 数据场:0-8字节。注意,CAN一次最多传8字节,别想着传大包。我曾经有个同事想传一个结构体,结果拆成好几帧,最后还得自己拼回去。
- CRC场:15位CRC校验 + 1位CRC界定符。保证数据没被干扰。
- ACK场:2位。发送节点发隐性位,接收节点如果正确收到,就拉成显性位。这就是“应答”。
- EOF(帧结束):7个隐性位。告诉总线“我发完了”。
避坑指南:我曾经在CRC计算上栽过跟头。有些MCU的CAN控制器会自动计算CRC,但如果你用软件模拟CAN协议,CRC必须自己算。别偷懒,用查表法,效率高很多。
2.3 远程帧
远程帧,说白了就是“请求数据”。一个节点发远程帧,另一个节点收到后,就会发对应的数据帧。远程帧的结构和数据帧几乎一样,唯一的区别是RTR位是隐性(1)。
你可能会问:为什么不直接发数据帧?嗯,远程帧的好处是:你不需要知道数据在哪,只需要告诉总线“我要ID=0x123的数据”,自然会有节点响应。我在做电池管理系统时,就用远程帧来轮询各个电池模组的电压。
注意:远程帧没有数据场。DLC虽然可以设置,但实际不传数据。有些新手会犯这个错,以为远程帧也能带数据。
2.4 错误帧
错误帧是CAN总线的“纠错机制”。当节点检测到错误时,就会发错误帧。错误帧由两部分组成:
- 错误标志:6个连续相同位(显性或隐性)。
- 错误界定符:8个隐性位。
CAN协议定义了5种错误:
| 错误类型 | 说明 | 我遇到的坑 |
|---|---|---|
| 位错误 | 发送节点监控总线,发现发送的位和总线上的位不一致 | 总线短路时最常见 |
| 填充错误 | 连续6个相同位出现(违反位填充规则) | 干扰导致位翻转 |
| CRC错误 | 接收节点计算的CRC和发送的不一致 | 波特率不匹配时必出 |
| 格式错误 | 帧格式不符合规范 | 软件bug导致 |
| 应答错误 | 发送节点没收到ACK | 节点离线或总线断开 |
警告:错误帧发多了,节点会进入“总线关闭”状态。我曾经调试一个设备,发现它每隔几秒就离线一次,查了半天,原来是CAN收发器的共模电压出了问题。记住:错误帧是信号,不是解决方案。
2.5 过载帧
过载帧,说白了就是“我忙,别发了”。当接收节点来不及处理数据时,就发过载帧,让发送节点等一等。过载帧的结构和错误帧很像,也是6个显性位 + 8个隐性位。
但过载帧和错误帧有本质区别:
- 错误帧是“出错了”,过载帧是“我忙”。
- 错误帧会导致发送节点重发,过载帧只是延迟。
- 过载帧不会增加节点的错误计数器。
我在实际项目中很少见到过载帧。为什么?因为CAN总线的速率足够快,而且数据帧最多8字节,一般不会出现接收缓冲区溢出。除非你用的是低速CAN(比如125kbps),或者节点处理能力太弱。
2.6 帧间隔
帧间隔,就是两帧之间的“休息时间”。CAN协议规定,每发完一帧,必须等3个隐性位才能发下一帧。这3个隐性位叫“间歇场”。
为什么要有帧间隔?两个原因:
- 给接收节点时间处理:接收节点需要把数据从CAN控制器搬到内存里。
- 避免总线冲突:如果连续发帧,总线可能被一个节点独占。
注意:帧间隔是强制性的,不能省略。我见过有人为了提升总线利用率,把帧间隔改成2位,结果总线乱成一锅粥。别干这种傻事。
总结一下:CAN协议的这五种帧,就像交通规则。数据帧是正常行驶,远程帧是问路,错误帧是事故报警,过载帧是堵车提醒,帧间隔是红绿灯。你把这些搞懂了,CAN总线调试就成功了一半。
好了,这一章的内容就到这儿。下一章咱们聊聊CAN的仲裁机制和位时序,那才是真正体现CAN协议精髓的地方。到时候我会拿一个实际案例——两个节点同时发数据,看看总线怎么“判案”。