4、CAN总线位定时与同步:位时间分段、同步段(SS)、传播段(PTS)、相位缓冲段(PBS1/PBS2)、采样点配置

好,咱们今天聊点硬核的——CAN总线的位定时与同步。

说实话,很多工程师写CAN驱动时,最头疼的就是这个位定时配置。寄存器里一堆TSEG1、TSEG2、SJW、BRP,看着就头大。我当年第一次调CAN总线时,也是对着数据手册翻来覆去看了好几遍,才搞明白这些参数到底在干嘛。

其实说白了,CAN的位定时就是把一个bit的时间,切成几段。为什么要切?因为总线上有多个节点,大家得同步着来。你想想看,如果每个节点都按自己的时钟跑,那数据肯定对不上。所以CAN协议设计了一套同步机制,让所有节点在采样点附近对齐。

4.1 位时间的基本分段

一个CAN bit的时间,被分成了四段:

  • 同步段(SS,Sync Segment):固定1个TQ(Time Quantum,时间量子)。用于检测边沿,完成硬同步。
  • 传播段(PTS,Propagation Time Segment):补偿物理延迟。包括总线传输延迟、收发器延迟、比较器延迟等。
  • 相位缓冲段1(PBS1,Phase Buffer Segment 1):用于重同步,可被延长。
  • 相位缓冲段2(PBS2,Phase Buffer Segment 2):用于重同步,可被缩短。

嗯,这里要注意:采样点位于PBS1和PBS2之间。也就是说,采样点之前是SS + PTS + PBS1,采样点之后是PBS2。

核心公式:

位时间(Tbit)= SS + PTS + PBS1 + PBS2 = 1 + PTS + PBS1 + PBS2(单位:TQ)

采样点位置 = (SS + PTS + PBS1) / Tbit × 100%

4.2 同步段(SS)—— 硬同步的起点

同步段是每个bit的开头。CAN控制器会在SS内检测总线上的下降沿(从隐性到显性)。如果检测到边沿,就认为这是一个帧起始(SOF)或者位流的同步点。

我记得有一次调试一个多节点系统,有个节点总是丢帧。查了半天,发现是它的晶振偏差太大,导致SS段内检测到的边沿位置飘了。后来换了高精度晶振,问题就解决了。

个人经验:SS段固定为1个TQ,不要试图去改它。这是协议规定的,改了就违反CAN规范了。

4.3 传播段(PTS)—— 补偿物理延迟

传播段用来补偿信号在总线上的往返时间。为什么是往返?因为CAN是“线与”机制,发送节点发出数据后,还要等总线上的信号稳定下来,才能去采样。

传播段的时间要大于等于:

  • 2 ×(总线传输延迟 + 收发器延迟 + 比较器延迟)

说白了,就是信号从A到B,再从B回到A的总时间。如果PTS设得太短,采样点可能采到不稳定的信号,导致位错误。

避坑指南:我曾经在一个长距离CAN总线项目(约500米)中,把PTS设成了默认值3个TQ,结果通信时好时坏。后来算了一下,总线延迟加上收发器延迟,至少需要5个TQ。改完之后,通信就稳定了。所以,PTS一定要根据实际物理长度来算,别偷懒。

4.4 相位缓冲段(PBS1 / PBS2)—— 重同步的弹性空间

这两个段是CAN同步机制的精髓。它们允许控制器在检测到边沿偏差时,动态调整采样点位置。

  • PBS1:可被延长。如果边沿来得晚了,控制器会延长PBS1,把采样点往后推。
  • PBS2:可被缩短。如果边沿来得早了,控制器会缩短PBS2,把采样点往前拉。

延长的最大值由SJW(同步跳转宽度)决定。SJW不能超过PBS1和PBS2中的较小值。

你想想看,如果SJW设得太大,虽然同步能力强,但抗干扰能力会下降。因为一个噪声引起的边沿,也可能被当成同步信号,导致采样点乱跳。

经验值参考:

参数 推荐范围 常用值
SS 固定1 TQ 1 TQ
PTS 1~8 TQ 3~5 TQ(视总线长度而定)
PBS1 1~8 TQ 4~6 TQ
PBS2 1~8 TQ 4~6 TQ
SJW 1~4 TQ 1~2 TQ(一般不超过PBS1/2的较小值)

4.5 采样点配置 —— 黄金位置在哪里?

采样点位置是位定时配置中最关键的一环。我个人习惯把采样点放在87.5%左右。为什么是这个值?

因为CAN协议规定,采样点应该在位时间的后半段,但又不能太靠后。太靠前,可能采到信号振荡;太靠后,留给PBS2的时间不够,重同步能力会变差。

举个例子,假设位时间 = 16 TQ,SS=1,PTS=5,PBS1=6,PBS2=4:

采样点位置 = (1 + 5 + 6) / 16 × 100% = 12/16 = 75%

这个位置偏低了。我建议把PBS1调大一点,比如PBS1=7,PBS2=3:

采样点位置 = (1 + 5 + 7) / 16 × 100% = 13/16 = 81.25%

嗯,这样就好多了。对于高速CAN(1Mbps),我一般把采样点设在85%~90%之间。

小技巧:如果你不确定采样点设多少合适,可以先用CAN分析仪抓一下波形,看看实际信号的眼图。眼图张开最大的位置,就是最佳采样点。我在项目中经常这么干,比纯理论计算靠谱多了。

4.6 位定时配置的实战步骤

好了,理论讲完了,咱们来点实际的。假设你要配置一个500kbps的CAN总线,系统时钟是40MHz,你该怎么做?

  1. 确定TQ长度:先选一个分频系数(BRP),让TQ长度合适。比如BRP=2,则TQ = 2 / 40MHz = 50ns。
  2. 计算位时间TQ数:位时间 = 1/500kbps = 2000ns。TQ数 = 2000 / 50 = 40 TQ。
  3. 分配各段:SS=1,剩下39 TQ分给PTS、PBS1、PBS2。我建议PTS=10,PBS1=15,PBS2=14。
  4. 计算采样点:(1+10+15)/40 = 26/40 = 65%。嗯,这个位置太低了。调整一下:PTS=8,PBS1=18,PBS2=13,采样点=(1+8+18)/40=27/40=67.5%。还是偏低。
  5. 再调整:把BRP改成1,TQ=25ns,位时间TQ数=80。SS=1,PTS=15,PBS1=35,PBS2=29。采样点=(1+15+35)/80=51/80=63.75%。还是不行。
  6. 最终方案:BRP=1,TQ=25ns,位时间TQ数=80。SS=1,PTS=10,PBS1=45,PBS2=24。采样点=(1+10+45)/80=56/80=70%。嗯,勉强可以,但最好再优化一下。

你看,配置位定时其实是个迭代的过程。我建议你写个小脚本,把各种组合算一遍,挑出采样点最接近理想值的那个配置。

Python示例:

def calc_sample_point(brp, pts, pbs1, pbs2):
    ss = 1
    tq_count = ss + pts + pbs1 + pbs2
    sample_point = (ss + pts + pbs1) / tq_count * 100
    return sample_point

# 遍历可能的组合
for pts in range(1, 9):
    for pbs1 in range(1, 9):
        for pbs2 in range(1, 9):
            sp = calc_sample_point(1, pts, pbs1, pbs2)
            if 85 < sp < 90:  # 理想范围
                print(f"PTS={pts}, PBS1={pbs1}, PBS2={pbs2}, 采样点={sp:.1f}%")

这个脚本我当年写驱动时经常用。你把它跑一遍,所有可行的组合就都出来了,省得手动算。

4.7 同步机制:硬同步与重同步

最后简单提一下同步机制。CAN有两种同步方式:

  • 硬同步:发生在帧起始(SOF)的下降沿。所有节点强制将当前位时间对齐到SS段。
  • 重同步:发生在帧内其他边沿。如果边沿落在SS段之外,控制器会调整PBS1或PBS2的长度,让采样点重新对齐。

重同步的能力由SJW决定。SJW越大,能纠正的偏差就越大,但抗干扰能力也越差。我一般设SJW=1或2,除非总线特别长或者晶振精度特别差,才会设到3或4。

注意:硬同步和重同步不能同时发生。如果已经做了硬同步,帧内的第一个边沿就不会再触发硬同步,而是触发重同步。这个细节很容易被忽略,但很重要。

好了,关于CAN位定时与同步,今天就聊这么多。下一章咱们会讲CAN的错误处理机制,那也是个容易踩坑的地方。到时候再细聊。