4. USB PD协议简介:电源协商与角色定义

USB PD(Power Delivery)协议,说白了就是让 USB Type‑C 接口不只是传数据,还能商量到底怎么送电。我刚开始接触 PD 的时候,觉得它不过是一个“我要电,你给电”的简单过程。但实际做项目时才发现,里面的角色定义、层叠协商、策略切换,任何一个细节都会让整块板子硬生生卡死。

这一节,我们只抓最关键的两条线:电源协商(Power Negotiation) 和 角色定义(Role Definition)。搞懂它们,你手上那把逻辑分析仪才能真正用起来。

4.1 电源协商:谁说多少电就多少电?

电源协商不是一句“我要 20V/5A”就完了。它是两层结构加四个状态的严格流程。我个人习惯把这套流程分成下面这张图来记:

USB PD电源协商流程 物理层 协议层 策略层 ① Attach.SRC/SK ② 角色发现 ③ 能力交换 ④ 请求 → 接受 ⑤ 显式契约 CC 上 BMC 编码 DFP vs UFP Source_Capabilities Request + Accept PS_RDY 电源协商:物理层 → 协议层 → 策略层 逐级完成 每个状态都必须收到正确消息才能跳转

你看,图上分了三层和五个状态。为什么会这样分层?因为 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.SNKDRP 的偏好模式某些快充头、OTG 设备

这里有个容易踩坑的地方:角色不是一成不变的。PD 协议允许在显式契约建立之后,通过 PR_Swap(电源角色交换)或 DR_Swap(数据角色交换)来翻转角色。

我曾经掉进的一个坑:

做一款便携式显示器,它一开始是 UFP / Sink,需要笔记本给它供电。但用户想拔下笔记本,插到另一台手机上反过来给手机充电。这个场景需要 PR_Swap。我没有在固件里处理 PS_RDY 之后的角色翻转,结果是设备直接重启。后来翻 PD 规范 2.0 里才有详细的状态机描述。

4.3 电源协商的四个状态机跳转

有了角色之后,协商才正式开始。我总结了一个四步骤小口诀:“连、发、选、定”。

  1. 连(Attach):CC 上检测到有效下拉/上拉电阻,物理层建立。
  2. 发(Source_Capabilities):Source 以广播形式发出能力包。注意是不带请求的,纯粹告诉 Sink:“我有这些电,你挑一个。”
  3. 选(Request):Sink 回应一个 Request。这个 Request 可以要求更高的电压,更高的电流,但必须在 Source_Capabilities 列出的组合之内。
  4. 定(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 有没有成对出现——八成的问题都卡在这两步。