第1章:物理层详解(二)——CAN总线物理层,CAN_H/CAN_L电平特性

好,咱们接着聊物理层。上一章我讲了CAN总线的总体架构,这一章咱们深入到底层——CAN_H和CAN_L这两根线到底是怎么工作的。

说实话,我最早接触CAN总线时,总觉得物理层没啥好学的。不就是两根线嘛,差分信号嘛,能有多复杂?直到有一次在实验室调板子,通信死活不稳定,示波器一抓波形,才发现我对电平的理解完全不够。嗯,从那以后我再也不敢小看物理层了。

1. CAN_H和CAN_L的电平特性

CAN总线用两根线传输信号:CAN_H(高线)和CAN_L(低线)。它俩不是独立的,是一对差分对。

什么叫差分?说白了就是:信号不是用一根线对地测量的电压来表示,而是用两根线之间的电压差来表示。你想想看,如果外界有电磁干扰,两根线上会同时感应出相同的噪声,但它们的差值基本不变。这就是CAN总线抗干扰能力强的根本原因。

CAN总线有两种逻辑状态:

  • 显性(Dominant):CAN_H比CAN_L高,电压差大约2V。逻辑上代表"0"。
  • 隐性(Recessive):CAN_H和CAN_L电压几乎相等,电压差接近0V。逻辑上代表"1"。

具体数值我列个表,方便你对照:

参数 显性(逻辑0) 隐性(逻辑1)
CAN_H电压 3.5V(典型) 2.5V(典型)
CAN_L电压 1.5V(典型) 2.5V(典型)
差分电压(Vdiff) 约2.0V 约0V

这里有个细节要注意:隐性时两根线都是2.5V,差分电压为0。但实际电路中,由于收发器内部偏置电阻的差异,可能会有几十毫伏的偏差。我个人习惯在设计时留出至少200mV的噪声裕量,别卡着理论值走。

关键点:显性电平会"覆盖"隐性电平。也就是说,只要有一个节点发送显性位,总线就是显性状态。这就是CAN总线仲裁机制的基础——谁先发0,谁就赢。

2. 位时序与采样点

好,电平搞清楚了,接下来是位时序。这部分我当年踩过坑,得好好说说。

CAN总线的每一位时间(Bit Time)被分成了四个段:

  1. 同步段(Sync_Seg):固定为1个时间量子(Tq)。用于同步各个节点的时钟。
  2. 传播段(Prop_Seg):补偿物理延迟,包括总线传输延迟和收发器延迟。
  3. 相位缓冲段1(Phase_Seg1):用于补偿边沿相位误差,可被延长。
  4. 相位缓冲段2(Phase_Seg2):用于补偿边沿相位误差,可被缩短。

采样点就落在Phase_Seg1和Phase_Seg2之间。说白了,就是在这个时刻,收发器去读取总线上的电平值。

采样点的位置用百分比表示:

采样点位置 = (Sync_Seg + Prop_Seg + Phase_Seg1) / (总Tq数) × 100%

举个例子,假设总Tq数为16,Sync_Seg=1,Prop_Seg=3,Phase_Seg1=4,Phase_Seg2=8:

采样点位置 = (1 + 3 + 4) / 16 × 100% = 50%

50%的采样点?我告诉你,这基本等于找死。

警告:采样点位置建议设置在75%~87.5%之间。太靠前容易采到信号跳变沿,太靠后抗干扰能力差。

3. 我曾经因为采样点设置不当导致通信丢帧

说到这个,我得分享一个真实教训。

几年前我做太阳翼驱动系统的通信板卡,CAN总线速率设的是500kbps。当时图省事,直接用开发板的默认配置,采样点大概在60%左右。板子焊好一测,单节点通信没问题,但挂上两个节点后,偶尔会出现丢帧现象。

我一开始怀疑是终端电阻没匹配,量了一下,120Ω,没问题。又怀疑是线缆太长,但实际只有不到2米。折腾了两天,最后用示波器抓波形,才发现问题——采样点太靠前了。

为什么?因为总线上的信号有上升时间和下降时间。在500kbps下,一个位的时间是2μs。如果采样点设在60%,也就是1.2μs处,而此时信号可能还没完全稳定。尤其是多个节点同时发送时,总线电平的建立时间会更长。结果就是:采样到的电平值可能是错的。

后来我把采样点调整到80%,丢帧问题立刻消失。嗯,从那以后,我每次做CAN设计,第一件事就是算采样点。

个人经验:对于1Mbps以下的CAN总线,我习惯把采样点设在80%~85%之间。对于更高速度(如CAN FD),建议用87.5%。具体值可以用CAN计算器工具算一下,别凭感觉设。

4. 位时序配置实战

咱们以STM32的bxCAN为例,看看怎么配位时序。假设系统时钟为36MHz,目标波特率为500kbps:

// 目标:500kbps,采样点约80%
// 系统时钟:36MHz
// 预分频器:4 → 时间量子频率 = 36MHz / 4 = 9MHz → Tq = 111ns
// 每位Tq数:36MHz / (4 × 500kbps) = 18 Tq

// 段分配:
// Sync_Seg = 1 Tq
// Prop_Seg = 3 Tq
// Phase_Seg1 = 10 Tq
// Phase_Seg2 = 4 Tq
// 采样点 = (1 + 3 + 10) / 18 = 77.8%

CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;      // 同步跳转宽度
CAN_InitStructure.CAN_BS1 = CAN_BS1_10tq;     // Phase_Seg1 + Prop_Seg
CAN_InitStructure.CAN_BS2 = CAN_BS2_4tq;      // Phase_Seg2
CAN_InitStructure.CAN_Prescaler = 4;          // 预分频器

你看,77.8%的采样点,在75%~87.5%的范围内,实际使用效果不错。

这里有个小技巧:如果总线长度较长(比如超过10米),可以适当增加Prop_Seg的值,补偿传输延迟。我一般按每米5.5ns的延迟估算,再加上收发器的延迟(大约50ns~100ns),算出总延迟后折算成Tq数。

5. 总结一下

物理层这东西,看着简单,但坑不少。我总结三条:

  • 电平要稳:CAN_H和CAN_L的共模电压要控制在2.5V左右,差分电压要足够大。
  • 采样点要准:别低于75%,也别高于90%。80%是个好起点。
  • 时序要算:别偷懒用默认配置,根据你的时钟和总线长度重新算一遍。

下一章咱们聊数据链路层,讲讲CAN帧的结构和ID仲裁。到时候你会发现,物理层搞明白了,上层协议理解起来就顺多了。

好,这一章就到这儿。有问题欢迎交流。