3、CAN总线基础(下):数据链路层与报文帧格式
好,咱们接着聊CAN总线。上一节我们把物理层的那些事儿捋了一遍,什么差分电平、终端电阻,说白了就是让信号能在线上跑起来。但光能跑还不够,车上的节点那么多,谁先说话、谁后说话、说错了怎么办?这些规矩,就归数据链路层管。
我个人习惯把数据链路层理解成「交通规则」。物理层是路,数据链路层就是红绿灯和交警。今天咱们重点看报文帧格式和仲裁机制,这两个是CAN总线的灵魂。
3.1 数据链路层的核心职责
数据链路层在CAN协议里干三件事:
- 帧封装:把要发的数据打包成标准格式
- 总线访问:决定什么时候能发、发不了怎么办
- 错误检测:发现传输过程中有没有被干扰
你想想看,如果没这层,所有节点同时往总线上怼数据,那不乱套了?
重要概念:CAN总线是「多主从」结构。任何节点都可以主动发消息,不需要等主机轮询。这跟LIN总线完全不一样,LIN是主从模式,从机只能等主机叫它。
3.2 报文帧的四种类型
CAN协议定义了四种帧。我在项目里调试时,90%的时间都在跟数据帧和错误帧打交道。远程帧偶尔用,过载帧说实话我干了十年也就见过几次。
| 帧类型 | 用途 | 常见场景 |
|---|---|---|
| 数据帧 | 发送数据 | ECU之间交换信号 |
| 远程帧 | 请求数据 | 某个节点主动要数据 |
| 错误帧 | 报告总线错误 | 节点发现数据异常 |
| 过载帧 | 请求延迟 | 接收节点忙不过来 |
3.3 数据帧的完整结构
数据帧是咱们最常用的。它分标准帧和扩展帧两种。标准帧11位ID,扩展帧29位ID。嗯,这里要注意:虽然扩展帧ID更长,但优先级反而可能更低,为什么?后面讲仲裁你就明白了。
一个标准数据帧长这样:
SOF + 11位ID + RTR + IDE + r0 + DLC + 数据段(0-8字节) + CRC + ACK + EOF
拆开来看:
- SOF(Start of Frame):1位显性位,告诉所有人「我要发消息了」
- ID(Identifier):11位,决定优先级。数值越小优先级越高
- RTR(Remote Transmission Request):数据帧里是显性0,远程帧里是隐性1
- IDE(Identifier Extension):标准帧里是显性0,扩展帧里是隐性1
- r0:保留位,必须为显性
- DLC(Data Length Code):4位,表示数据段有多少字节。范围0-8
- 数据段:0到8字节,真正要传的内容
- CRC:15位校验码+1位定界符
- ACK:2位,发送方发隐性,接收方拉显性表示收到
- EOF:7位隐性位,表示帧结束
我的经验:调试时最容易忽略的是DLC。有一次客户说某个信号收不到,我抓了半天波形,最后发现是DLC设成了2,但实际发了4个字节。接收方只读了前两个字节,后面的全丢了。所以DLC一定要跟发送方匹配。
3.4 远程帧:我要,你给
远程帧说白了就是「请求帧」。一个节点发远程帧,另一个节点收到后,就会发对应的数据帧。远程帧的结构跟数据帧几乎一样,唯一的区别是RTR位是隐性1,而且没有数据段。
我记得有一次做BCM(车身控制器)调试,发现某个传感器数据一直没更新。抓波形一看,原来是网关发了远程帧,但传感器没响应。后来查手册才发现,那个传感器只支持数据帧主动上报,不支持远程帧请求。所以用远程帧之前,一定要确认对方支不支持。
3.5 错误帧:出事了
错误帧是CAN总线自我保护的机制。任何一个节点发现错误,就会立刻发错误帧,把当前传输打断。
错误帧分两种:
- 主动错误帧:6个连续显性位,把总线拉低
- 被动错误帧:6个连续隐性位,比较温和
节点内部有两个计数器:发送错误计数器(TEC)和接收错误计数器(REC)。
- TEC或REC > 127:节点进入被动错误状态
- TEC > 255:节点进入总线关闭状态,彻底离线
避坑指南:我曾经遇到一个案例,某ECU频繁发错误帧,导致整个CAN网络瘫痪。查了半天发现是它的晶振偏差太大,位时序对不上。换了个晶振就好了。所以做硬件设计时,晶振精度一定要选够,别省那几毛钱。
3.6 过载帧:慢点,我跟不上
过载帧用得很少。它表示接收节点忙不过来了,请求发送方等一等。结构跟错误帧有点像,但触发条件不同。过载帧只在帧间隔期间发,不会打断正在传输的帧。
说实话,我在实际项目中几乎没见过过载帧。现在的MCU处理能力都够,很少出现来不及收的情况。但如果你在做一些老平台的项目,或者总线负载率特别高(超过80%),还是有可能遇到的。
3.7 仲裁机制:谁优先级高谁先走
这是CAN总线最巧妙的设计。多个节点同时发消息怎么办?靠ID仲裁。
原理很简单:总线是「线与」逻辑。显性位(0)会覆盖隐性位(1)。所以ID数值越小,显性位越多,优先级越高。
举个例子:
- 节点A发ID=0x100(二进制:001 0000 0000)
- 节点B发ID=0x200(二进制:010 0000 0000)
从第一位开始比。A的第一位是0(显性),B的第一位是1(隐性)。总线被A拉成显性,B检测到总线跟自己发的不一样,就知道自己输了,立刻停止发送,转为接收模式。
整个过程在微秒级完成,完全不影响正常通信。这就是为什么CAN总线适合实时控制——优先级高的消息永远能抢到总线。
关键点:仲裁不丢数据。输了的节点自动重发,赢了的不受影响。所以CAN总线的实时性是有保障的,不像以太网那样会有碰撞丢包。
3.8 位填充机制
还有一个细节:位填充。CAN协议规定,连续发送5个相同电平后,必须插入一个相反电平。比如连续发了5个显性位,第6位必须发隐性位。
为什么这么做?两个原因:
- 保证时钟同步:接收方靠电平跳变来同步时钟,如果长时间没跳变,时钟会漂移
- 防止误判:连续相同电平太长,可能被误认为是帧结束或错误标志
位填充的范围是从SOF到CRC段结束。EOF和ACK段不填充。
我刚开始学CAN的时候,总觉得位填充是个小细节。直到有一次用示波器抓波形,发现数据段里多了一个位,怎么都对不上。后来才想起来是位填充搞的鬼。所以做协议分析时,一定要把位填充考虑进去。
3.9 小结
今天的内容有点多,但都是硬核知识。总结一下:
- 数据链路层管帧封装、总线访问和错误检测
- 四种帧:数据帧、远程帧、错误帧、过载帧
- 数据帧分标准帧和扩展帧,结构要记熟
- 仲裁靠ID,数值越小优先级越高
- 位填充保证时钟同步,别忽略它
下一节咱们讲CAN总线的错误处理机制和故障诊断,那才是真正考验工程师功底的地方。到时候我会分享几个我亲手解决的疑难杂症,保证让你少走弯路。