4. USB PD协议简介:电源协商与角色定义
USB PD(Power Delivery)协议,说白了就是让 USB Type‑C 接口不只是传数据,还能商量到底怎么送电。我刚开始接触 PD 的时候,觉得它不过是一个“我要电,你给电”的简单过程。但实际做项目时才发现,里面的角色定义、层叠协商、策略切换,任何一个细节都会让整块板子硬生生卡死。
这一节,我们只抓最关键的两条线:电源协商(Power Negotiation) 和 角色定义(Role Definition)。搞懂它们,你手上那把逻辑分析仪才能真正用起来。
4.1 电源协商:谁说多少电就多少电?
电源协商不是一句“我要 20V/5A”就完了。它是两层结构加四个状态的严格流程。我个人习惯把这套流程分成下面这张图来记:
你看,图上分了三层和五个状态。为什么会这样分层?因为 PD 需要兼容老设备,还要保证拔插不断电,复杂度就上来了。
电源协商的关键消息对(Message Pair):
- Source_Capabilities:源端(Source)告诉耗电端(Sink)自己能给什么电压电流组合。注意,这个报文里只包含 PDO,不包含任何“OK/不OK”的语义。
- Request:Sink 从 Source_Capabilities 里选一个 PDO,然后发回给 Source。我见过很多初学者在这步居然修改了 PDO 的电压值——千万别改,只能选,不能改。
- Accept / Reject:Source 决定是否接受这个请求。如果 Accept,就进入过渡;如果 Reject,Sink 得重新选或者保持默认的 5V。
有一次调试,示波器抓 Source_Capabilities 报文发现电压值比实际低了 0.2V。后来查出来是 MUX 路径上的一颗电阻分压,直接把 CC 线上 BMC 信号给衰减了。所以你看,协议层再对,物理层信号不对照样白搭。
4.2 角色定义:谁是老板谁是员工?
每次 Type‑C 插拔,PD 都有个“角色发现”的过程。它决定两件事:Data Role 和 Power Role。
| 角色类型 | 定义 | 典型设备 |
|---|---|---|
| DFP (Down‑Facing Port) | 数据主机角色,提供 VBUS 的初态 Source | 笔记本电脑、充电器 |
| UFP (Up‑Facing Port) | 数据设备角色,初态为 Sink | 手机、U盘 |
| DRP (Dual‑Role Port) | 可以动态切换两种角色 | 移动电源、扩展坞 |
| Try.SRC / Try.SNK | DRP 的偏好模式 | 某些快充头、OTG 设备 |
这里有个容易踩坑的地方:角色不是一成不变的。PD 协议允许在显式契约建立之后,通过 PR_Swap(电源角色交换)或 DR_Swap(数据角色交换)来翻转角色。
我曾经掉进的一个坑:
做一款便携式显示器,它一开始是 UFP / Sink,需要笔记本给它供电。但用户想拔下笔记本,插到另一台手机上反过来给手机充电。这个场景需要 PR_Swap。我没有在固件里处理 PS_RDY 之后的角色翻转,结果是设备直接重启。后来翻 PD 规范 2.0 里才有详细的状态机描述。
4.3 电源协商的四个状态机跳转
有了角色之后,协商才正式开始。我总结了一个四步骤小口诀:“连、发、选、定”。
- 连(Attach):CC 上检测到有效下拉/上拉电阻,物理层建立。
- 发(Source_Capabilities):Source 以广播形式发出能力包。注意是不带请求的,纯粹告诉 Sink:“我有这些电,你挑一个。”
- 选(Request):Sink 回应一个 Request。这个 Request 可以要求更高的电压,更高的电流,但必须在 Source_Capabilities 列出的组合之内。
- 定(Accept + PS_RDY):Source 回 Accept,然后调整电压电流。调整完成后发 PS_RDY 通知 Sink:“电稳了,可以用。”
我的调试习惯:
在写代码时,我会在每收到一个 PD 消息后打印 1 个字节的 “PDO 索引 + 电压代码”。这样逻辑分析仪抓到乱序时,能快速定位是 Source 的 Accept 延迟了,还是 Sink 的 Request 发错了。
4.4 避坑指南:常见的角色协商失败场景
项目做多了,你就会发现电源协商失败大多是“角色不匹配”和“消息超时”引起的。这里列三个我遇到最多的:
- Source 发 Capabilities 后 Sink 没回 Request —— 大概率是 Sink 端的 CC 上拉/下拉电阻配置不对,导致物理层没握手。
- Sink 的 Request 被 Source Reject —— 检查 Source Capabilities 里的 PDO 类型是不是 Fixed PDO,有些 Source 不支持可编程(APDO)的 Request。
- 角色交换后 VBUS 掉电 —— 一般是 PR_Swap 流程里的 PS_RDY 丢失。规范要求 Source 在发送 PS_RDY 之前必须确保 VBUS 已经稳定在新的电压域。
嗯,这里还要提醒一点:很多工程师只看 PD 报文包不看时间戳。但实际上 PD 的 协议层超时 非常严格,比如 SenderResponseTimer 是 24 ms 到 30 ms 之间,超出就复位状态机。我曾经帮朋友调一个充电宝,就是 Sink 那边的 Request 发送速度太慢,每次都卡在超时边界上。
4.5 电源协商背后的策略层
你可能注意到了,电源协商的物理层和协议层都是固定的,真正的“智能”在策略层(Policy Engine)。策略层决定了源端应该给哪个 PDO,耗电端应该选哪个 PDO,以及什么时候触发角色交换。
规范里把策略层的决策叫 Policy,但是它没有强制规定算法。所以你设计自己的产品时,完全可以根据电池曲线、温度、输入能力来写一套策略。说白了,这就是 PD 协议给你留的“定制空间”。
电源协商与角色定义的要点速查:
- 物理层:BMC 编码、CC 线检测
- 协议层:4个主要状态 + 消息对
- 策略层:电源策略、角色偏好、Swap 时机
- 角色:DFP/UFP/DRP,可用 PR_Swap / DR_Swap 动态改变
- 常见故障:角色不匹配、消息超时、VBUS 过渡不稳
讲到这里,电源协商和角色定义的骨架就搭起来了。你之后在逻辑分析仪抓到的 PD 报文,每一步都能对应到这张图和这张表里。下次调试时,记得先看 Source_Capabilities 和 Request 有没有成对出现——八成的问题都卡在这两步。