2. CAN协议基础:物理层、数据链路层与报文格式

各位同学,今天我们来聊聊CAN协议的基础。说实话,CAN协议在汽车电子里太常见了,几乎每个ECU都离不开它。我刚开始做AUTOSAR通信栈时,第一个接触的就是CAN。那时候踩了不少坑,今天把这些经验分享给你们。

2.1 CAN物理层:信号怎么在线上跑?

CAN总线用两根线——CAN_H和CAN_L。它靠差分电压传输信号。什么意思呢?就是两根线的电压差决定逻辑值。

  • 显性电平(Dominant):CAN_H比CAN_L高约2V。逻辑值为0。
  • 隐性电平(Recessive):CAN_H和CAN_L电压接近。逻辑值为1。

我做过一个项目,总线莫名其妙丢帧。查了半天,发现是CAN_H和CAN_L接反了。嗯,这种低级错误,新手很容易犯。记住:CAN_H接CAN_H,CAN_L接CAN_L,别搞混。

关键参数:CAN总线终端电阻通常为120Ω。没有它,信号反射会让你怀疑人生。

物理层还有个重要概念——位时序。CAN的每个位被分成4段:同步段、传播段、相位缓冲段1、相位缓冲段2。采样点就在相位缓冲段1和2之间。我习惯把采样点设在87.5%左右,这样抗干扰能力最强。

2.2 CAN数据链路层:帧是怎么组织的?

数据链路层定义了5种帧类型。但日常开发中,我们最常打交道的是数据帧远程帧。其他三种(错误帧、过载帧、帧间隔)由硬件自动处理,你基本不用操心。

数据帧长这样:

SOF + 仲裁场 + 控制场 + 数据场 + CRC场 + ACK场 + EOF

每个字段都有讲究。比如SOF(帧起始)是一个显性位,告诉总线上所有节点:「我要发数据了,大家听着。」

我的经验:调试时,用示波器抓SOF信号,能快速判断总线是否活跃。如果看不到SOF,八成是硬件问题。

2.3 CAN报文格式:标准帧 vs 扩展帧

CAN 2.0A定义了标准帧,CAN 2.0B定义了扩展帧。区别就在ID长度上。

特性 标准帧 扩展帧
ID长度 11位 29位
最大ID值 0x7FF 0x1FFFFFFF
IDE位 0(显性) 1(隐性)
数据长度 0-8字节 0-8字节

你可能会问:「什么时候用扩展帧?」

说白了,如果网络节点少,标准帧就够了。但现代汽车动辄几十个ECU,标准帧的ID空间(2048个)根本不够用。扩展帧有5亿多个ID,随便用。

我记得有个项目,客户要求所有报文用扩展帧。结果配置时忘了设置IDE位,导致标准帧和扩展帧混在一起,总线乱成一锅粥。后来我养成了习惯:先确认CAN控制器的工作模式,再配置报文。

2.4 CAN总线仲裁机制:谁先发?

这是CAN协议最巧妙的地方。多个节点同时发送时,不会冲突,而是通过仲裁决定谁先发。

仲裁规则很简单:ID越小,优先级越高。显性位(0)会覆盖隐性位(1)。所以ID=0x100的报文,永远比ID=0x200的报文先发。

具体过程是这样的:

  1. 所有节点同时发送SOF和ID位。
  2. 每个节点发送一位,同时监听总线电平。
  3. 如果发送的是隐性位(1),但总线上是显性位(0),说明有更高优先级的节点在发。自己立刻停止发送,转为接收。
  4. 仲裁失败的节点,会在总线空闲后自动重发。

避坑指南:我曾经把两个节点的ID设成一样。结果它们同时发送时,CRC校验永远过不了。因为仲裁机制认为它们是同一个节点,但数据内容不同。记住:每个节点的发送ID必须唯一

还有一点要注意:远程帧的仲裁和普通数据帧一样。但远程帧没有数据场,它只是请求其他节点发送数据。我见过有人用远程帧做心跳检测,结果因为优先级太低,总是被数据帧挤掉。后来改用单独的心跳报文,问题才解决。

2.5 实战中的小技巧

最后分享几个我常用的调试方法:

  • 看波形:用示波器抓CAN_H和CAN_L的差分信号。显性位时,波形会有一个明显的跳变。
  • 算波特率:CAN的波特率由晶振、分频器和位时序决定。我习惯用公式:波特率 = 晶振频率 / (分频系数 × 位时间)
  • 测总线负载:总线负载超过80%时,低优先级报文可能永远发不出去。这时候需要重新分配ID或增加总线。

好了,CAN协议基础就讲到这里。下一节我们会深入CAN驱动配置,到时候手把手教你们在AUTOSAR里配CAN。有什么问题,咱们课上聊。