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米。
- 计算Tq: 分频系数设为4,Tq = 1/(8MHz/4) = 500ns
- 计算位时间: 1/250kbps = 4μs,需要8个Tq
- 分配段:
- Sync_Seg = 1 Tq
- Prop_Seg = 3 Tq(补偿20米延迟+收发器延迟)
- Phase_Seg1 = 2 Tq
- Phase_Seg2 = 2 Tq
- 设置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和段分配 |
最后说一句。位时序这东西,看着是理论,其实全是实战。你调好一次,后面就顺了。但要是配错了,查错能查到你怀疑人生。所以,我建议你每次改参数,都先拿示波器看看总线波形,确认采样点位置对不对。嗯,这招很管用。