协议超时标准:tType4、tReceive、tSinkTx 深度解析

PD 协商其实挺像两个人打电话。你等对方回话,总得有个耐心上限。这个上限就是超时。我早年踩过一个大坑——产品做兼容性测试,每次跟某款充电器配对都失败。抓 log 一看,是超时参数没对齐。说白了,PD 的时序就是靠这些时间参数卡死的。

为什么需要超时?

PD 是双端异步通信。Source 和 Sink 各自独立跑状态机,谁都不知道对方什么时候能处理完。如果没有超时,一旦某条消息丢了,整个状态机就卡死在 “等待” 里。我习惯把超时想象成 “保底”:超过这个时间没收到应答,就认为出问题了,该复位复位,该重试重试。

核心思想:超时不是用来精准计时的,它是容错的最后一道防线。

三大主角:tType4、tReceive、tSinkTx

这三个参数覆盖了 PD 协商中三个阶段:

  • tType4:Source 等待 Sink 回复 Capabilities 的时间。
  • tReceive:Sink 等待 Source 的 GoodCRC 响应的时间。
  • tSinkTx:Sink 发送下一个消息前必须等待的最小间隔。

我给你画了张时序图,一看就明白它们各自蹲在什么位置。

PD 协商时序中三大超时参数位置 Source Sink Source_Capabilities tReceive:Sink 等 GoodCRC tSinkTx:Sink 发送前等待 tType4:Source 等 Sink 响应 时间 说明 tReceive 和 tType4 是“等待超时”上限,tSinkTx 是“发送间隔”下限。

逐个拆解

tType4

定义在 USB PD 规范 Table 6-49。Source 发出 Source_Capabilities 后,会启动 tType4 定时器。如果在这个时间内没收到 Sink 的 GoodCRC,Source 就认为 Sink 没听到,重新发或者复位。

参数值范围典型值
tType44.0 ms – 5.5 ms4.5 ms

嗯,这个时间很短。为什么?因为 Capabilities 是协商的起点,双方必须赶快对上暗号。我遇到过一个案子:Sink 端 MCU 启动慢,导致 GoodCRC 回得太晚,刚好超过 tType4 上限。Source 连续三次超时就直接硬复位了。后来我在 Sink 的初始化流程里加了提前唤醒,才解决这个问题。

注意:tType4 是 Source 侧的超时,Sink 看不到。如果你发现充电器反复“握手失败”,优先查 Sink 回复 GoodCRC 的实时性。

tReceive

这是 Sink 侧的等待窗口。Sink 发出任何消息(比如 Request、Soft_Reset)后,必须在 tReceive 内收到 Source 返回的 GoodCRC。时间范围:

参数值范围典型值
tReceive14.5 ms – 15.5 ms15 ms

这个时间比 tType4 长不少。为什么?因为 Source 可能正在处理其他事务,比如检测插入或者读取 E‑Marker。我见过一些设计把 tReceive 设成硬件定时器固定 15 ms,但软件层面如果被高优先级中断阻塞,容易超时。我个人习惯:在 Sink 代码里留一个计数器,超时后重试而不是直接复位,这样能容忍偶尔的抖动。

tSinkTx

这个参数比较特别,它不是超时,而是最小间隔。Sink 发送完一条消息后,必须至少等待 tSinkTx 才能发下一条。作用:防止 Sink 太快刷爆 Source 的接收缓冲区。

参数值范围典型值
tSinkTx1.0 ms – 1.5 ms1.2 ms

我曾经在测试脚本里把一个循环发 Request 的间隔设成 0.5 ms,结果 Source 直接不理我了——它认为这是无效序列,触发了协议错误。查了规范才知道有 tSinkTx 这个限制。所以说,不要觉得“快就是好”。该等的时候就得等。

实战经验:一次因 tType4 太苛刻导致的掉电问题

以前做一款快充移动电源,Sink 芯片是某款第三方 PD PHY。样机阶段发现:插入某些品牌手机时,偶尔会重复“充电-停止-充电”循环。抓取 CC 线波形发现:Source 发了 Capabilities,Sink 的 PHY 在 5ms 后才返回 GoodCRC,刚好超过 tType4 上限(4.5 ms)。Source 认为无响应,重发 Capabilities,Sink 又回一次,形成乒乓。

为什么会这样?因为 Sink 的 PHY 在收到 Capabilities 后,需要先通过 I²C 通知 MCU,MCU 解析后再决定是否回 GoodCRC。I²C 总线上如果有其他器件占用(比如电池电量计),延迟就会变长。

解决方案:把 PHY 配置为“自动响应 GoodCRC”模式。大部分 PD PHY 都支持这个功能——收到有效消息后硬件自动回 GoodCRC,不经过 MCU。这样一来,tType4 再也不是瓶颈了。调完之后再也没出现充电反复现象。

小技巧:如果项目中使用了 PD PHY,务必开启 “Auto GoodCRC” 功能。这能省掉很多因软件延迟引起的超时问题。

参数速查表

我把三个参数整理到一起,方便你对照查阅。

参数测量方向最小值最大值典型值备注
tType4Source→等待 Sink 响应4.0 ms5.5 ms4.5 msSource_Capabilities 发出后等待 GoodCRC
tReceiveSink→等待 Source 响应14.5 ms15.5 ms15 msSink 发出任何消息后等待 GoodCRC
tSinkTxSink→发送间隔1.0 ms1.5 ms1.2 ms两次发送之间的最小间隔

如何用代码检查超时?

虽然超时逻辑通常在硬件定时器里实现,但在调试阶段,我喜欢写一个小 Python 脚本来模拟校验。下面是一个简化版的逻辑,帮你理解参数交互。

# PD 超时检查模拟(Python 伪代码)
import time

def check_timeout(source_send_time_ms, sink_reply_time_ms):
    """检查 tType4 是否满足"""
    delta = sink_reply_time_ms - source_send_time_ms
    if delta < 4.0 or delta > 5.5:
        print(f"FAIL: tType4={delta:.2f} ms 不在 [4.0, 5.5] 范围内")
    else:
        print(f"PASS: tType4={delta:.2f} ms OK")

# 示例
check_timeout(0, 4.5)  # 正常范围
check_timeout(0, 6.0)  # 超时失败

当然,实际产品里你没法在 PC 上抓毫秒级时间差。这个脚本更多是用来辅助理解:你能改改数值,看看超时边界在哪里。

总结一句话:tType4 和 tReceive 是“等不到就翻脸”,tSinkTx 是“别发太急”。做 PD 协议栈时,这三个参数要像盯住监控指标一样反复核对。