PD协商的角色与拓扑(Source / Sink / DRP)

搞 USB PD 的人,最先要搞清楚的就是角色。说白了,就是谁给电,谁用电,以及谁既能给又能用。我自己刚入行那阵子,总觉得 PD 协议的各种状态机复杂,后来发现绕来绕去其实就三种角色——Source、Sink 和 DRP。弄明白它们的拓扑关系,后面看报文才不晕。

1. Source —— 供电方

Source 就是功率的提供端。典型的 Source 有电源适配器、扩展坞、充电宝的“输出口”。它的 CC 脚上会拉一个 Rp(上拉电阻),告诉对面我最多能出多少功率。我见过很多调试问题,就是 Rp 阻值焊错了,导致 Source 只能出 5V 而不是 20V。

经验提示: 我自己调板子时,先测 CC 对地的电压。如果稳定在 0.4V 到 0.9V 之间,基本可以判断是 Source 侧,并且电阻值对应着广播的电流能力。具体对应关系查表就好,别靠猜。

2. Sink —— 用电方

Sink 就是吸电的,比如手机、笔记本、小风扇。Sink 的 CC 脚上拉的是 Rd(下拉电阻),它通过 Rd 的阻值告诉 Source 自己需要多少电流。注意,Sink 也可以同时请求电压变化——发一个 Request 报文,Source 那边 Accept 后切电压。

我之前遇到一个怪现象:Sink 端明明请求了 9V,Source 也 Accept 了,但电压就是没升上去。后来发现是 Sink 端的 VBUS 路径上有个保险丝内阻太大,9V 时电流掉得厉害。所以,别光看报文,要测实物。

3. DRP —— 双角色端口

DRP(Dual Role Port)是个灵活的中间派。它既不固定当 Source,也不固定当 Sink。典型的 DRP 设备:笔记本的 USB-C 口(可以给手机充电,也可以插适配器自己充电)、某些扩展坞、甚至一些奇葩的“双向充电宝”。DRP 的工作原理是在 CC 线上周期性地切换 Rp 和 Rd,就像在喊:“我能不能给你电?我要不要吃电?”

曾经踩坑: 有一个项目,DRP 设备连上另一个 DRP 设备,两边都以为自己是 Source,结果 CC 上两个 Rp 碰在一起,状态机卡死,谁也不供电。后来加了硬件消抖和软件超时才能收敛。如果两端都是 DRP,一定要做 Try.Source / Try.Sink 的优先级判断,否则你会见到各种离奇死锁。

4. 角色决定的底层逻辑

真正决定角色的是 CC 引脚上的上下拉电阻。看这张表就一目了然:

端口类型CC 上拉/下拉初始角色能否切换
SourceRp(上拉)Source否(除非 DRP 切换)
SinkRd(下拉)Sink否
DRP交替 Rp / Rd取决于首次检测是(通过 DRP_Swap)

那为什么两个 Source 直接连起来会有问题?因为两边都输出 Rp,没有 Rd 让它检测到“对面有设备”,所以 VBUS 都不会打开。反过来,两个 Sink 连也一样。只有 Source + Sink 才能直接握手。DRP 则像“万能转接头”,总能找到一种方式匹配。

5. 典型拓扑结构

实际系统中,最常见的是这几种:

  • 点对点:一个 Source 直接连一个 Sink,比如充电器接手机。
  • 链式:Source → 扩展坞(DRP)→ Sink,扩展坞内部会做一个角色管理。
  • 多端共享:比如一个 Source 通过 USB Hub 接多个 Sink,但 PD 在物理层其实还是点对点,Hub 会做策略路由。

你想想看,如果拓扑里出现一个 DRP,它可能在链路的哪个位置?多数情况下 DRP 是中间设备,比如笔记本接充电器时是 Sink,接手机时是 Source。我调试过一个多口充电器,每个口都是 DRP,结果四个口之间乱抢角色,VBUS 一会开一会关。后来每个口固定为 Source 或者加硬件优先级才解决。

6. 角色转换与 Try.Source / Try.Sink

DRP 设备决定角色的过程叫“角色确定”。它会先尝试作 Source(Try.Source),如果几秒内没检测到 Sink 的 Rd,再切换到 Sink 试试(Try.Sink)。这样做是为了避免两端同时当 Source 或同时当 Sink。具体状态机见下面的图。

Kernel insight: 在 USB PD 标准中,角色的切换是通过软件发送 DR_Swap 或 PR_Swap 报文完成的。不要以为仅仅是硬件上下拉的事情,软件层也要同步修改 CC 上的电阻状态。我见过一个方案,软件改了角色,但硬件上下拉没同步,结果 CC 信号错乱,设备疯狂重枚举。
PD协商角色与拓扑 PD 角色与拓扑示意图 Source Rp 上拉 Sink Rd 下拉 VBUS + CC 线路 DRP Rp ↔ Rd 切换 角色确定流程: 1. DRP 先试做 Source(Rp),等待 Rd 出现。 2. 若超时未检测到 Sink,切换为 Sink(Rd),等待 Rp。 3. 若仍然失败,进入 ErrorRecovery 并轮询。 4. 两端都是 DRP 时可能反复跳跃,需硬件消抖。

你看,角色和拓扑其实不复杂,但实际硬件连线上 CC 脚只有一根(CC1/CC2 两根线,一根用于通信,另一根用于检测正反插)。我刚开始学的时候总把 CC 和 D+ D- 弄混,结果一直调不通。记住一点:CC 是控制通道,不是数据通道。

7. 实际调试中常见的角色问题

  • CC 电平不对:Source 侧 Rp 阻值选错,比如本该 10kΩ 却用了 5kΩ,电压超出范围,Sink 识别不到。
  • DRP 切换频率太慢:我见过某个方案的 DRP 周期设为 300ms,结果设备插入时刚好在切换间隙,错过握手,需要用户重插。
  • 两端都是 DRP 且优先级相同:会造成振荡,两边都在 Try.Source → Try.Sink 之间来回。解决办法是硬件上让一边先占住,或者软件增加随机退避。
  • 忘记在软件里同步 CC 状态:硬件改电阻了,但软件还在按旧角色发送 PR_Swap,导致 PD 逻辑混乱。
避坑指南: 我曾经验证一个 DRP 设备时,发现连接某些第三方的充电器后,VBUS 一直没上来。用协议分析仪抓包,看到 Source 侧发了 Source_Capabilities,但 DRP 设备没有回 GoodCRC。排查后发现 DRP 处于 Try.Sink 状态,CC 上拉电阻是 Rp,但软件认为自己是 Sink,拒绝处理 Source 的报文。这是个典型的软硬状态不同步,修了三天。

嗯,到这里你应该能分清这三兄弟了。Source、Sink、DRP 的拓扑关系直接影响你的 PD 产品能不能正常握手。下一项准备就是深入学习 PD 报文的结构,但先把角色和拓扑刻在脑子里,后面才不会跑偏。