协议超时标准:tType4、tReceive、tSinkTx 深度解析
PD 协商其实挺像两个人打电话。你等对方回话,总得有个耐心上限。这个上限就是超时。我早年踩过一个大坑——产品做兼容性测试,每次跟某款充电器配对都失败。抓 log 一看,是超时参数没对齐。说白了,PD 的时序就是靠这些时间参数卡死的。
为什么需要超时?
PD 是双端异步通信。Source 和 Sink 各自独立跑状态机,谁都不知道对方什么时候能处理完。如果没有超时,一旦某条消息丢了,整个状态机就卡死在 “等待” 里。我习惯把超时想象成 “保底”:超过这个时间没收到应答,就认为出问题了,该复位复位,该重试重试。
核心思想:超时不是用来精准计时的,它是容错的最后一道防线。
三大主角:tType4、tReceive、tSinkTx
这三个参数覆盖了 PD 协商中三个阶段:
- tType4:Source 等待 Sink 回复 Capabilities 的时间。
- tReceive:Sink 等待 Source 的 GoodCRC 响应的时间。
- tSinkTx:Sink 发送下一个消息前必须等待的最小间隔。
我给你画了张时序图,一看就明白它们各自蹲在什么位置。
逐个拆解
tType4
定义在 USB PD 规范 Table 6-49。Source 发出 Source_Capabilities 后,会启动 tType4 定时器。如果在这个时间内没收到 Sink 的 GoodCRC,Source 就认为 Sink 没听到,重新发或者复位。
| 参数 | 值范围 | 典型值 |
|---|---|---|
| tType4 | 4.0 ms – 5.5 ms | 4.5 ms |
嗯,这个时间很短。为什么?因为 Capabilities 是协商的起点,双方必须赶快对上暗号。我遇到过一个案子:Sink 端 MCU 启动慢,导致 GoodCRC 回得太晚,刚好超过 tType4 上限。Source 连续三次超时就直接硬复位了。后来我在 Sink 的初始化流程里加了提前唤醒,才解决这个问题。
注意:tType4 是 Source 侧的超时,Sink 看不到。如果你发现充电器反复“握手失败”,优先查 Sink 回复 GoodCRC 的实时性。
tReceive
这是 Sink 侧的等待窗口。Sink 发出任何消息(比如 Request、Soft_Reset)后,必须在 tReceive 内收到 Source 返回的 GoodCRC。时间范围:
| 参数 | 值范围 | 典型值 |
|---|---|---|
| tReceive | 14.5 ms – 15.5 ms | 15 ms |
这个时间比 tType4 长不少。为什么?因为 Source 可能正在处理其他事务,比如检测插入或者读取 E‑Marker。我见过一些设计把 tReceive 设成硬件定时器固定 15 ms,但软件层面如果被高优先级中断阻塞,容易超时。我个人习惯:在 Sink 代码里留一个计数器,超时后重试而不是直接复位,这样能容忍偶尔的抖动。
tSinkTx
这个参数比较特别,它不是超时,而是最小间隔。Sink 发送完一条消息后,必须至少等待 tSinkTx 才能发下一条。作用:防止 Sink 太快刷爆 Source 的接收缓冲区。
| 参数 | 值范围 | 典型值 |
|---|---|---|
| tSinkTx | 1.0 ms – 1.5 ms | 1.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” 功能。这能省掉很多因软件延迟引起的超时问题。
参数速查表
我把三个参数整理到一起,方便你对照查阅。
| 参数 | 测量方向 | 最小值 | 最大值 | 典型值 | 备注 |
|---|---|---|---|---|---|
| tType4 | Source→等待 Sink 响应 | 4.0 ms | 5.5 ms | 4.5 ms | Source_Capabilities 发出后等待 GoodCRC |
| tReceive | Sink→等待 Source 响应 | 14.5 ms | 15.5 ms | 15 ms | Sink 发出任何消息后等待 GoodCRC |
| tSinkTx | Sink→发送间隔 | 1.0 ms | 1.5 ms | 1.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 协议栈时,这三个参数要像盯住监控指标一样反复核对。