第1章:CAN总线协议详解

1.1 物理层特性:差分信号与总线电平

各位同学,咱们先从最底层聊起。CAN总线的物理层,说白了就是信号怎么在线上跑。

为什么CAN要用差分信号?我刚开始接触时也觉得奇怪,单根线多省成本啊。后来在实验室里吃过亏——有一次调试制动系统,线束稍微长了点,信号就开始跳变,ECU时不时报错。换成差分对之后,问题立马消失。

差分信号的核心原理很简单:两根线(CAN_H和CAN_L)同时传输,但电平相反。接收端只看它们的差值,不看对地电压。这样一来,外部电磁干扰对两根线的影响是相同的,差值不变——这就是共模抑制。

具体到总线电平,CAN定义了两种状态:

  • 显性电平(Dominant):CAN_H ≈ 3.5V,CAN_L ≈ 1.5V,差分电压约2V。逻辑上代表"0"。
  • 隐性电平(Recessive):CAN_H ≈ 2.5V,CAN_L ≈ 2.5V,差分电压约0V。逻辑上代表"1"。

关键点:显性电平会"覆盖"隐性电平。也就是说,只要有一个节点发送显性,总线就是显性。这个特性是后面仲裁机制的基础。

我记得第一次画CAN节点电路时,终端电阻差点搞错。120Ω的电阻必须挂在总线两端,不能少也不能多。少了信号反射严重,多了驱动能力不够。嗯,这里要注意——电阻的精度最好选1%,别用5%的,否则长距离通信时波形会变形。

1.2 数据链路层:帧格式

物理层搞定后,咱们看看数据是怎么打包的。CAN总线有四种帧:数据帧、远程帧、错误帧、过载帧。制动系统里最常用的是数据帧,我重点讲这个。

标准CAN(2.0A)的数据帧长这样:

SOF + 11位ID + RTR + IDE + r0 + DLC + 数据(0-8字节) + CRC + ACK + EOF

扩展CAN(2.0B)的ID是29位,结构类似。我个人习惯在制动系统中用标准帧,因为11位ID够用了,帧短一点,总线负载低一些。

这里有个坑:DLC(数据长度码)虽然可以设0到8,但制动相关的报文我建议固定为8字节。为什么?因为很多MCU的CAN控制器在DLC小于8时,不会自动填充剩余字节,导致接收方读到的是随机值。我曾经排查过一个偶发性的制动异常,最后发现是DLC设成了4,但发送方只更新了前3个字节——第4个字节是上次的残留数据。

个人技巧:发送制动报文时,即使有效数据只有2个字节,也把DLC设成8,剩余字节填0x00或0xFF。这样接收方处理起来更安全,也方便后续扩展。

1.3 仲裁机制:谁先说话?

CAN总线是多主从结构,任何节点都可以随时发消息。那冲突了怎么办?靠仲裁。

仲裁的原理就是"线与"——显性位覆盖隐性位。多个节点同时发送时,从SOF开始逐位比较ID。谁的ID更小(显性位更多),谁就赢。输的节点自动转为接收模式,不破坏总线。

你想想看,这个设计有多巧妙。不需要中央调度,不需要令牌传递,完全靠硬件自动完成。我在做制动系统时,把紧急制动报文的ID设成了0x001,普通状态报文设成0x100。这样紧急制动永远优先,不会因为总线拥堵而延迟。

注意:ID越小优先级越高。但别把ID设成0x000,那是留给特殊用途的。我见过有人把制动报文ID设成0x000,结果和系统管理报文冲突,导致ECU死机。

1.4 错误处理:容错机制

制动系统对可靠性要求极高,CAN的错误处理机制正好派上用场。CAN有五种错误类型:

  • 位错误:发送时监控总线,发现电平不一致
  • 填充错误:连续5个相同位后缺少填充位
  • CRC错误:接收方计算的CRC与发送方不一致
  • 格式错误:帧格式不符合规范
  • ACK错误:没有节点应答

每个节点都有发送错误计数器和接收错误计数器。错误多了,节点会先进入"错误主动"状态,再变成"错误被动",最后"总线关闭"。我曾经在测试制动系统时,故意用信号发生器注入干扰,观察节点的容错表现。结果发现,有些便宜的CAN收发器在错误被动状态下恢复得很慢,差点导致制动信号丢失。

避坑指南:制动系统的CAN节点,建议选用支持"快速恢复"模式的收发器。错误被动状态下,尽快清零计数器,别让节点轻易进入总线关闭状态。

1.5 制动系统中的应用实例

好了,理论讲完了,咱们看看实际怎么用。我参与过的一个商用车EBS(电子制动系统)项目,CAN总线是这样设计的:

报文名称 ID(标准帧) 周期 数据内容
制动踏板位置 0x100 10ms Byte0-1: 踏板行程(0-100%)
Byte2: 冗余校验
轮速信号 0x200 20ms Byte0-3: 左前轮速
Byte4-7: 右前轮速
制动压力 0x300 10ms Byte0-1: 前桥压力
Byte2-3: 后桥压力
故障状态 0x700 事件触发 Byte0: 故障码
Byte1: 严重等级

这里有个设计细节:制动踏板位置报文周期是10ms,轮速是20ms。为什么?因为踏板信号变化快,需要更频繁地采样。而轮速在正常行驶时变化相对平缓,20ms足够了。但紧急制动时,轮速变化剧烈——所以我们在故障状态报文里加了一个"紧急标志位",一旦触发,所有报文周期缩短到5ms。

嗯,这个设计是我从一次实车测试中学到的。当时在冰雪路面上做紧急制动测试,ABS介入时轮速信号更新太慢,导致制动压力调节滞后。后来改成动态周期,问题就解决了。

经验之谈:制动系统的CAN总线负载率建议控制在30%以下。我见过一个项目,为了省成本把所有信号塞到一条总线上,负载率飙到70%。结果紧急制动时,高优先级的报文被低优先级报文阻塞,制动响应延迟了20ms——这在制动系统里是不可接受的。

最后说一句,CAN总线在制动系统中的应用远不止这些。后面章节我会详细讲诊断协议(UDS on CAN)、时间触发通信(TTCAN)以及功能安全(ISO 26262)对CAN的要求。咱们一步步来。