PD协议基础:版本演进、基本架构与协商概念

做PD调试这么多年,我见过最多的问题就是协商超时。说白了,就是Source和Sink在“谈价钱”的时候,一方迟迟不回复,或者回复格式不对,最后总线挂在那没人管。要定位这类问题,得先把PD协议的地基打牢。这一章我们就从版本演进、系统架构、协商流程三个角度,把基础捋清楚。

USB PD 版本演进:从15W到240W,中间经历了什么

PD协议最早是2012年出的,那时候叫USB Power Delivery 1.0。能力很有限,只支持三档电压:5V、12V、20V,电流最大3A,合计也就60W。说实话,当时我就觉得这玩意迟早得变——手机都开始快充了,笔记本还是靠圆头电源,太乱了。

2014年的PD 2.0是一次大跃进。它引入了“协商”的概念,不再是死板的固定档位,而是让两端通过消息包动态商量。电流上限提到5A,功率拉到100W。我第一次在示波器上抓到完整的Source-Sink握手序列时,心里踏实了很多——协议开始有血有肉了。

PD 3.0(2015年)又补了几个重要特性:

  • 快速角色交换(FR Swap)——不需要断开USB就能互换Source/Sink角色
  • 电池充电扩展(BC 1.2兼容)——向下兼容老设备
  • 电源供应类型扩展——把APDO(可编程电源)加了进来

嗯,PD 3.0直到今天还是主力。但2021年发布的PD 3.1又把规格推到240W(48V/5A)。为什么要这么高?你看看现在那些高性能游戏本和雷电坞站就知道了。

版本 推出年份 最大功率 关键变化
PD 1.0 2012 60W 固定档位 5/12/20V,无协商
PD 2.0 2014 100W 引入消息协商,支持5A
PD 3.0 2015 100W FR Swap,APDO
PD 3.1 2021 240W 48V PDO,扩展电压范围

你想想看,如果项目里用的是老PD控制器,却跑去跟48V的新设备握手,不超时才怪。

基本架构:CC线、VCONN和物理层角色

PD通信走的是CC线(Configuration Channel)。Type-C口里有两条CC线:CC1和CC2,但同一时刻只有一条用来传PD消息,另一条拿来检测插拔方向或者做VCONN供电。

一个典型的PD系统分成三层:

  • 物理层:负责BMC编码、电平转换、线缆补偿
  • 协议层:打包/解包消息,做CRC校验和重传
  • 策略层:决定接下来回什么消息,是同意、拒绝还是等待

我去年调试过一个案子,Source老是发GoodCRC之后就没下文了。查了好久才发现是物理层的BMC信号在长线缆上衰减太大,协议层都收不全。你看,物理层出问题,策略层再聪明也白搭。

核心概念:PD协商中,所有消息都以“发起-应答-确认”的三步握手走。超时一般发生在某一方没响应,或者响应消息的MessageID对不上。

再说说角色。PD里定义了五种角色:

  • Source:供电方
  • Sink:受电方
  • DRP:双角色端口,插上后自动决定
  • VConn Power:给线缆芯片供电的端口
  • VConn Consumer:需要VCONN供电的设备

这些角色在协商开始前就固定了。DRP会先检测对方是Source还是Sink,然后自己变成相反的。我碰到过因为DRP策略没配置好,两端都当Source,结果CC线一直没电,仪器上连个波形都看不到。

协商概念:从PDO到RDO,一次完整的定价谈判

PD协商的核心是两样东西:PDO(Power Data Object)和RDO(Request Data Object)。

Source端会广播一份它支持的PDO列表。比如:

PDO #1: Fixed 5V/3A  (15W)
PDO #2: Fixed 9V/3A  (27W)
PDO #3: Fixed 15V/3A (45W)
PDO #4: Fixed 20V/2.25A (45W)
PDO #5: PPS 3.3V-21V/3A (可编程)

Sink收到后,选一个最合适的RDO发回去。这个RDO里会包含电压、电流请求,以及可选的PPS电压档位。Source收到RDO后,要么回Accept,要么回Reject,然后双方切到新电压。

整个流程大概是:

  1. Source检测到Sink接入→发送SourceCable和SourceCapabilities
  2. Sink接收PDO列表→发送Request(含选择的RDO)
  3. Source回Accept→发送PS_RDY
  4. 电压切换完成,Sink开始按新规格取电

每一步都有超时定时器。Source发完Capabilities后会等1.5秒;Sink必须在SenderResponseTimer(典型值500ms)内回复。我从逻辑分析仪上抓到的超时案例,十有八九都是Sink端没收到Capabilities,一直卡在初始状态。

避坑指南:我曾经遇到一个案子,Sink端的PD协议栈只支持PDO索引0~4,但Source发了一个PPS PDO(索引5),Sink直接忽略掉了。结果Source等不到Request,超时断开。解决办法很简单:把Sink的PDO过滤逻辑改成支持所有有效索引,或者Source端关闭PPS,用固定PDO做兼容。

核心逻辑图:USB PD协商状态机

下面这张图浓缩了PD协商从接入到稳定供电的完整状态。每个方框代表一次状态切换,箭头上的标记是触发条件。

USB PD 协商状态机(Source侧简化版) 检测到CC连接 发 Source_Cap 等 Sink 的 Request 发 Accept 电压切换 → PS_RDY 超时 / Reject 说明:超时可发生在任何等待应答的节点。常见原因包括 Sink 未收到 Cap、消息ID翻转、CRC错误等。

我个人习惯在调试时先把这张图的每个状态点对应的寄存器值列出来,然后抓波形往上套。哪个状态卡住了,就优先查那个状态的超时定时器有没有触发。你会发现,大部分超时问题都出在「等 Request」那个环节——要么Sink没发,要么发的RDO格式不对。

注意:PD 2.0和3.0对超时计数器的要求有差异。PD 2.0用的是“nTimes”重试机制,PD 3.0则加入了“Wait”和“Soft Reset”阶段。如果你用的是老代码库,记得确认tSenderResponse和tSinkTx这两个定时器的设置是否符合新规范。

最后再提一句:PD协商超时并不总是协议栈的锅。CC线上的共模噪声、VCONN供电不足、连接器插拔瞬间的弹跳——这些硬件问题都能模仿成“协商超时”的症状。所以每次做根因分析,我都会先拿示波器看CC线波形,确认物理层没毛病,再往协议层面查。这个习惯帮我省了不少冤枉路。