3、位时序与同步:位时间、同步段、传播段、相位缓冲段、重同步

好,咱们今天聊点硬核的——位时序与同步。

说实话,很多工程师调CAN总线,遇到通信不稳定,第一反应就是换线、加终端电阻、降波特率。但你知道吗?很多时候问题出在时钟同步上。我早年做车载项目时,就吃过这个亏。两台ECU,波特率明明配的一样,可就是时不时丢帧。后来一查,是位时序参数没配好,采样点偏了。

所以,搞懂位时序,是解决通信延迟和误码的基础。咱们一步步拆开看。

3.1 位时间(Bit Time)

一个CAN数据位,在总线上待多久?这个时间就叫位时间。它由四个段组成:

  • 同步段(Sync_Seg)
  • 传播段(Prop_Seg)
  • 相位缓冲段1(Phase_Seg1)
  • 相位缓冲段2(Phase_Seg2)

说白了,一个位时间 = 同步段 + 传播段 + 相位缓冲段1 + 相位缓冲段2。每个段又由若干个时间量子(Tq)组成。Tq是CAN控制器的最小时间单位,由晶振分频得到。

核心公式:

位时间 = (Sync_Seg + Prop_Seg + Phase_Seg1 + Phase_Seg2) × Tq

波特率 = 1 / 位时间

举个例子。假设晶振16MHz,分频后Tq=125ns。如果四个段加起来是16个Tq,那一个位时间就是2μs,波特率就是500kbps。嗯,这个很常见。

3.2 同步段(Sync_Seg)

同步段是每个位的开头。它的作用只有一个:等待总线上的跳变沿。所有CAN节点,都靠这个沿来对齐自己的时钟。

同步段固定为1个Tq。为什么是1个?因为CAN协议规定死了,你不能改。我刚开始学的时候,总想调大它来增加容错,后来发现这是徒劳的——硬件根本不认。

我的经验: 同步段虽然只有1个Tq,但它是整个同步机制的基础。如果晶振精度不够,或者总线干扰大,跳变沿可能偏移。这时候,靠的就是后面的相位缓冲段来补偿了。

3.3 传播段(Prop_Seg)

传播段用来补偿物理延迟。包括:

  • 信号在总线上的传输延迟
  • 收发器的输入输出延迟
  • 比较器延迟等

传播段一般设置为1~8个Tq。怎么算?我有个笨办法:先估算总线最长距离,按5ns/m算传输延迟,再加上收发器手册里的延迟,然后翻个倍留余量。

注意: 传播段设得太小,远距离节点可能采样不到正确的电平。设得太大,又会压缩相位缓冲段的空间,影响同步能力。我曾经在一个50米长的总线项目上,把传播段从3个Tq调到5个Tq,误码率直接降了一个数量级。

3.4 相位缓冲段(Phase_Seg1 & Phase_Seg2)

这两个段,是位时序里最灵活的部分。它们的作用是:吸收时钟偏差,实现重同步。

  • Phase_Seg1: 位于采样点之前。重同步时,可以延长它(最多延长SJW个Tq)。
  • Phase_Seg2: 位于采样点之后。重同步时,可以缩短它(最多缩短SJW个Tq)。

采样点就在Phase_Seg1和Phase_Seg2之间。你想想看,如果总线上的节点时钟有微小差异,采样点就会漂移。相位缓冲段就是用来“追”这个漂移的。

采样点位置:

采样点 = (Sync_Seg + Prop_Seg + Phase_Seg1) / 位时间 × 100%

一般建议采样点在70%~90%之间。我习惯设在80%左右,兼顾了稳定性和容错。

3.5 重同步(Resynchronization)

重同步是CAN总线保持时钟一致性的关键机制。说白了,就是当检测到总线上的跳变沿不在同步段内时,控制器自动调整相位缓冲段的长度。

重同步有两种情况:

  • 硬同步: 只在总线空闲到帧起始时发生。所有节点强制对齐到同步段。
  • 重同步: 在帧内发生。根据跳变沿的偏差,延长Phase_Seg1或缩短Phase_Seg2。

这里有个关键参数——同步跳转宽度(SJW)。它决定了每次重同步最多能调整多少个Tq。SJW一般设为1~4个Tq,但不能超过Phase_Seg1和Phase_Seg2的最小值。

避坑指南: 我曾经在一个项目里,为了追求高波特率,把SJW设成了1个Tq。结果总线一有干扰,节点就失步,通信直接瘫痪。后来把SJW放宽到3个Tq,问题就解决了。所以,SJW别设得太死,留点余量。

3.6 实战配置示例

咱们拿一个实际案例来算算。假设晶振8MHz,目标波特率250kbps,总线长度20米。

  1. 计算Tq: 分频系数设为4,Tq = 1/(8MHz/4) = 500ns
  2. 计算位时间: 1/250kbps = 4μs,需要8个Tq
  3. 分配段:
    • Sync_Seg = 1 Tq
    • Prop_Seg = 3 Tq(补偿20米延迟+收发器延迟)
    • Phase_Seg1 = 2 Tq
    • Phase_Seg2 = 2 Tq
  4. 设置SJW: 设为2 Tq(不超过Phase_Seg1和Phase_Seg2)

采样点位置 = (1+3+2)/8 = 75%。嗯,这个值很理想。

配置代码示例(基于STM32 HAL库):

// 假设CAN1,250kbps,Tq=500ns
CAN_HandleTypeDef hcan1;
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 4;          // 分频系数
hcan1.Init.Mode = CAN_MODE_NORMAL;
hcan1.Init.SyncJumpWidth = CAN_SJW_2TQ;  // SJW=2
hcan1.Init.TimeSeg1 = CAN_BS1_5TQ;       // Sync+Prop+Phase1 = 1+3+2=6Tq
hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;       // Phase2 = 2Tq
hcan1.Init.TimeTriggeredMode = DISABLE;
hcan1.Init.AutoBusOff = ENABLE;
hcan1.Init.AutoWakeUp = ENABLE;
hcan1.Init.AutoRetransmission = ENABLE;
hcan1.Init.ReceiveFifoLocked = DISABLE;
hcan1.Init.TransmitFifoPriority = DISABLE;
HAL_CAN_Init(&hcan1);

3.7 常见问题与调试思路

现象 可能原因 解决方向
通信偶尔丢帧 采样点偏后,接近位末尾 增大Phase_Seg1,减小Phase_Seg2
远距离节点通信失败 传播段不足 增大Prop_Seg
总线干扰下频繁错误 SJW太小,无法跟上时钟漂移 增大SJW
所有节点都报错 位时序配置不一致 统一各节点的Tq和段分配

最后说一句。位时序这东西,看着是理论,其实全是实战。你调好一次,后面就顺了。但要是配错了,查错能查到你怀疑人生。所以,我建议你每次改参数,都先拿示波器看看总线波形,确认采样点位置对不对。嗯,这招很管用。