1. USB Power Delivery 协议概览

咱们直接聊 USB Power Delivery,也就是 PD 协议。说实话,很多工程师一开始觉得 PD 就是“快充协议”,其实没那么简单。PD 的本质是在 USB Type-C 线缆上,让两个设备协商出双方都能接受的电源配置——电压、电流、甚至方向。

我最早接触 PD 是在一个 Type-C 扩展坞项目上。那时候我以为插上就能用,结果设备死活不充电。抓包一看,Source 和 Sink 在 Capabilities 消息上就谈崩了。从那一刻起我才真正意识到:PD 不是物理层的事,是协议层的事。

1.1 为什么需要 PD?

传统 USB 2.0/3.0 的供电能力太弱了——5V、最大 900mA。笔记本、显示器、大功率充电宝根本带不动。PD 协议把电压范围拉到 5V 到 48V,电流可以到 5A,最大功率 240W(EPR 模式)。但这只是表面。

你想想看,如果直接把大功率怼到设备上会怎样?设备可能烧掉。所以 PD 引入了一个协商机制:源端(Source)先说自己能提供哪些电压/电流组合,受电端(Sink)从中挑一个,最后双方确认。

PD 的核心价值:不是“快”,而是“安全协商下的精准供电”。

1.2 PD 协议栈长什么样?

PD 协议分好几层,我习惯用“三明治”来记:物理层、协议层、策略层。

  1. 物理层(Physical Layer)—— 处理信号传输,包括 BMC 编码、CRC、硬复位等。说白了就是怎么在 CC 线上把 0 和 1 发出去。
  2. 协议层(Protocol Layer) —— 负责消息的组装、拆分、重传、超时控制。这一层定义了 SOP、SOP'、SOP'' 等包类型。
  3. 策略层(Policy Layer) —— 也叫 PE(Policy Engine),决定什么时候发送什么消息。比如插拔后先发 Source Capabilities,还是先等 Sink 请求。

嗯,这里要注意:很多开发者在调试时只抓了协议层的报文,但问题可能出在物理层的 CRC 错误或者策略层的状态机卡住了。我的建议是——看问题先确认物理层有没有持续硬复位,再查协议层消息是否完整。

USB PD 协议栈与协商流程 物理层 BMC 编码 / 硬复位 协议层 消息组装 / 重传 / CRC 策略层 (PE) 状态机 / 消息决策 ① Source 发送 Capabilities ② Sink 解析 → 发出 Request ③ Source 回应 Accept / Reject ④ Source 输出新电压 → PS_RDY ⑤ Sink 检测到 PS_RDY → 开始用电 整个协商一气呵成,任何一步超时都会导致 Hard Reset 层次间通过 FIFO / 状态接口通信

1.3 消息类型:控制消息 vs 数据消息

PD 的消息分两大阵营:控制消息(16 字节以内)和数据消息(最长 260 字节)。

类型常见例子长度作用
控制消息GoodCRC, Accept, Reject, PS_RDY, Get_Source_Cap固定 16 字节握手、确认、状态通报
数据消息Source_Capabilities, Request, BIST, Vendor_Defined最多 260 字节传递电源参数、自定义数据

我的经验:调试时最先看 GoodCRC 有没有回复。如果 Source 发了一条 Source_Capabilities 但 Sink 没回 GoodCRC,那问题很可能在物理层或协议层的 CRC 校验上。别一上来就怀疑策略层。

1.4 核心协商流程(一张图就能讲清)

上面那张 SVG 已经画了。我再拿文字捋一遍:

  • Source 上电或插入新 Sink → 发送 Source_Capabilities(列出所有 PDO,即 Power Data Object)。
  • Sink 收到后,根据自身需求选择一个 PDO → 回 Request(包含 RDO,Request Data Object)。
  • Source 检查请求是否合法 → 同意则回复 Accept,不同意回 Reject 或 Wait。
  • Source 调整输出电压/电流 → 完成后发 PS_RDY(电源就绪)。
  • Sink 检测到 PS_RDY → 开始正常取电。

我遇到过最头疼的问题:Sink 发完 Request 后,Source 一直不回 Accept,也不回 Reject,直接超时导致 Hard Reset。后来发现是 Sink 的 Request 中的 RDO 字段设置了一个 Source 不支持的电压。说白了,Sink 在“漫天要价”,Source 根本不认。

避坑提醒:Request 消息中的 Object Position 字段必须匹配 Source_Capabilities 中的 PDO 编号,从 1 开始。曾经有工程师习惯从 0 计数,结果始终协商失败。嗯,这个低级错误我见过不止一次。

1.5 描述符的“误区”

很多刚入门的同学会把 USB PD 和 USB 设备描述符搞混。PD 协议本身不依赖传统的 USB 设备描述符(Device/Configuration/Interface 描述符)。PD 通过 CC 线上传输的 BMC 信号直接协商,不需要 USB 数据线枚举。所以你的 USB 2.0 枚举失败,PD 协商可能仍然正常;反过来,PD 协商失败,USB 枚举也可能正常。

不过在后来的教程中你会看到,PD 描述符(比如 Source_Capabilities 里的 PDO 结构、Sink Capabilities 里的 Sink PDO)是排错的关键入口。它们的位定义一旦写错,整个协商就崩了。我们后面几章会挨个拆解。

1.6 本章要点速查

模块关键点
协议分层物理层 → 协议层 → 策略层,排错需要分层隔离
核心流程Source Cap → Sink Request → Accept → PS_RDY
消息类型控制消息固定 16 字节,数据消息最长 260 字节
描述符排错入口PDO (Source/Sink) 和 RDO 的位域解析
常见故障GoodCRC 丢失、超时硬复位、Object Position 索引错误

好了,第一章的概览就这些。你不需要记住所有细节,只要心里有个协议栈的架构图,理解协商的“一来一回”就够了。下节课我们会深入 PD 协议的具体消息格式——尤其是那些让你头疼的位域和字节序。不过那是后面的事,先把今天的内容消化掉。