2、CAN总线基础:从起源到帧结构

大家好,我是你们的嵌入式讲师。今天我们来聊聊CAN总线。说实话,CAN总线是我在工业现场打交道最多的总线之一。记得我刚入行那会儿,调试一个电机驱动板,怎么都收不到数据,折腾了两天,最后发现是终端电阻没焊上——嗯,这种坑我踩过不少,今天一并分享给你们。

2.1 CAN总线的起源:为什么会有它?

CAN,全称Controller Area Network,控制器局域网。这玩意儿是1986年由德国博世公司发明的。你想想看,那时候汽车里全是点对点的铜线,一辆豪华车的线束能有好几十公斤重。我见过一张老照片,一辆奔驰的仪表台拆开,里面的线跟蜘蛛网似的。

博世的工程师们想:能不能搞一条总线,让所有节点都挂上去?于是CAN总线诞生了。1987年,Intel发布了第一颗CAN控制器芯片82526。从那以后,CAN就成了汽车电子的事实标准。

我个人习惯把CAN总线的发展分为三个阶段:

  • 第一阶段(1986-1990):博世提出CAN协议,主要用于汽车动力系统
  • 第二阶段(1991-2000):CAN 2.0A/B标准确立,开始进入工业自动化
  • 第三阶段(2000至今):CAN FD(灵活数据速率)出现,带宽提升到8Mbps以上

为什么CAN能活这么久?说白了,它解决了几个核心痛点:

  1. 多主通信:任何节点都可以主动发数据,不需要主机轮询
  2. 实时性高:优先级高的报文,延迟可以控制在微秒级
  3. 可靠性强:差分信号、CRC校验、错误重发,一套组合拳下来,误码率极低

核心观点:CAN总线不是最快的,也不是最便宜的,但它是在恶劣工业环境下最可靠的。我在一个振动台架上测过,CAN总线在100V/m的电磁干扰下依然能稳定通信——换成RS485早挂了。

2.2 物理层特性:差分信号的艺术

CAN总线的物理层,核心就是差分信号。什么是差分信号?简单说,就是用两根线的电压差来表示逻辑状态。

CAN总线有两根线:CAN_H(高线)和CAN_L(低线)。它们的状态定义如下:

总线状态 CAN_H电压 CAN_L电压 差分电压
显性(Dominant) 3.5V 1.5V 2.0V
隐性(Recessive) 2.5V 2.5V 0V

为什么会这样设计?我给你们讲个故事。有一次我在一个变频器旁边调试CAN总线,示波器一挂上去,单端信号全是毛刺。但切换到差分模式,波形干净得像教科书一样。这就是差分信号的魅力——共模噪声被完美抵消了。

CAN总线的物理层还有几个关键参数:

  • 总线电平:显性时CAN_H=3.5V,CAN_L=1.5V;隐性时两者都是2.5V
  • 终端电阻:总线两端各接一个120Ω电阻,用于阻抗匹配
  • 最大通信距离:1Mbps时约40米,125kbps时可达500米
  • 节点数:标准CAN最多110个节点,但实际建议不超过32个

⚠️ 避坑指南:我曾经在一个项目中,为了省成本,没加终端电阻。结果总线反射严重,数据包时不时就丢。后来加上120Ω电阻,问题立刻消失。记住:CAN总线两端必须各有一个120Ω终端电阻,这是铁律。

还有一个细节:CAN总线的物理层用的是CSMA/CA(载波监听多路访问/冲突避免)机制。和以太网的CSMA/CD不同,CAN不会在冲突后重发,而是通过仲裁机制让优先级高的报文先发。怎么仲裁的?看下一节。

2.3 CAN总线帧结构:数据帧与远程帧

CAN总线的帧结构,说白了就是数据怎么打包发送的。CAN 2.0A标准定义了11位标识符(标准帧),CAN 2.0B定义了29位标识符(扩展帧)。我们重点讲标准帧。

2.3.1 数据帧

数据帧是CAN总线最常用的帧类型。它的结构如下:

帧起始(1bit) | 仲裁场(12bit) | 控制场(6bit) | 数据场(0-8字节) | CRC场(16bit) | 应答场(2bit) | 帧结束(7bit)

我来拆解一下每个部分:

  • 帧起始(SOF):1位显性电平,表示总线开始传输
  • 仲裁场:11位标识符 + 1位RTR位(数据帧RTR=0)
  • 控制场:1位IDE + 1位保留位 + 4位DLC(数据长度码)
  • 数据场:0-8字节数据,DLC决定长度
  • CRC场:15位CRC校验 + 1位CRC界定符
  • 应答场:1位ACK槽 + 1位ACK界定符
  • 帧结束(EOF):7位隐性电平

这里有个关键点:仲裁机制。当多个节点同时发送时,标识符越小,优先级越高。为什么?因为显性电平(0)会覆盖隐性电平(1)。我举个例子:

节点A发送ID=0x100(二进制:00100000000)
节点B发送ID=0x200(二进制:01000000000)

在仲裁过程中,第2位(从高位开始):
- 节点A发送0(显性)
- 节点B发送1(隐性)
- 总线状态为0(显性)
- 节点B检测到总线与自身发送不一致,退出仲裁

嗯,这就是CAN总线的"霸道"之处——谁优先级高谁先走,低优先级的自动退让,等总线空闲再重发。

💡 实战技巧:我建议在设计CAN网络时,把紧急报文(如刹车、急停)的ID设小一些,比如0x001-0x01F。普通状态报文用大ID,比如0x100-0x7FF。这样能保证关键数据永远优先通过。

2.3.2 远程帧

远程帧,说白了就是"请求数据"的帧。它的结构和数据帧很像,但有两个区别:

  • RTR位:远程帧的RTR=1(隐性),数据帧的RTR=0(显性)
  • 数据场:远程帧没有数据场,DLC表示请求的数据长度

远程帧的典型应用场景:一个节点想获取另一个节点的数据,就发一个远程帧。比如:

// 节点A发送远程帧,请求ID=0x200的节点发送4字节数据
// 帧结构:
// SOF | ID=0x200 | RTR=1 | DLC=4 | CRC | ACK | EOF

收到远程帧的节点,会检查ID是否匹配。如果匹配,就发送一个数据帧作为响应。注意:远程帧本身不携带数据,它只是"喊一嗓子"。

我记得有一次调试,一个传感器节点死活不响应远程帧。查了半天,发现是DLC设置错了——我设了DLC=8,但传感器只支持4字节。CAN协议规定,远程帧的DLC必须和响应数据帧的DLC一致,否则节点会忽略请求。这个坑我记了好几年。

2.3.3 数据帧 vs 远程帧:一张表说清楚

对比项 数据帧 远程帧
RTR位 0(显性) 1(隐性)
数据场 0-8字节
用途 发送数据 请求数据
仲裁优先级 高于同ID的远程帧 低于同ID的数据帧
典型场景 传感器上报数据 主站轮询从站

这里有个细节:如果数据帧和远程帧的ID相同,数据帧的优先级更高。因为数据帧的RTR=0(显性),远程帧的RTR=1(隐性),在仲裁时数据帧会胜出。所以,我建议不要给数据帧和远程帧分配相同的ID,否则远程帧可能永远抢不到总线。

2.4 小结

今天的内容就到这里。我们讲了CAN总线的起源、物理层的差分信号特性,以及数据帧和远程帧的结构。说实话,CAN总线的知识点很多,但掌握了这些基础,你就能看懂大部分CAN通信的问题了。

下一章,我们会深入CAN总线的错误处理机制和位时序。到时候我会分享一个我在汽车ECU调试中遇到的"总线关闭"故障案例——那才叫刺激。

课后思考:为什么CAN总线的数据场最多只有8字节?如果我要传输超过8字节的数据,该怎么办?提示:想想CAN FD或者分包传输。