第二章:USB PD协议基础——版本演进、分层结构与消息类型
做PD调试快十年了,我一直觉得,搞懂PD协议的基础比学会抓波形更重要。为什么呢?因为协议是骨架,波形是血肉——骨架歪了,血肉再丰满也没用。这一章我们就把PD协议的演进、分层和消息类型掰开揉碎了讲清楚。
2.1 PD协议版本演进:从1.0到3.1,中间踩过多少坑
USB PD协议从2012年诞生到现在,一共经历了四个大版本。每个版本背后都有现实痛点在驱动。我列个表格,你会看得更清楚。
| 版本 | 发布时间 | 最大功率 | 关键特性 | 我碰到的典型问题 |
|---|---|---|---|---|
| PD 1.0 | 2012 | 100W (20V/5A) | BMC编码、CC线通信、Source/Sink角色 | 早期设备兼容性极差,经常握手失败 |
| PD 2.0 | 2014 | 100W (20V/5A) | 引入PDO/RDO、扩展功率范围(EPR)雏形 | 部分Hub在角色协商时死锁 |
| PD 3.0 | 2015 | 100W (20V/5A) 后扩展至240W | PPS可编程电源、快充协议统一、FRC快速角色交换 | PPS响应时间不达标,我被客户追着改过三次 |
| PD 3.1 | 2021 | 240W (48V/5A) | EPR正式落地、SFR固定角色、AVS自适应电压 | 48V隔离方案翻过车,后来加了预放电电路 |
我的习惯:做新项目时,我会先翻一遍PD 3.1的规范,再对照PD 2.0找差异。因为很多「新功能」其实是旧需求的补丁,比如EPR的电源管理序列就借鉴了2.0的电源交换流程。
版本演进的核心逻辑其实就一句话:功率越来越高,协议越来越智能。PD 1.0的时候,大家还在纠结怎么用CC线传数据;到了PD 3.1,已经开始讨论48V怎么安全输出。
2.2 协议分层结构:像看洋葱一样一层层剥开
PD协议分四层,从物理层到策略层。嗯,这跟OSI七层模型有点像,但更轻量。我建议你在调试前先把这个分层结构刻在脑子里——很多奇怪的Bug都是因为跨层状态没对齐导致的。
别说,这张图我做了不下二十次培训。每次我都强调:物理层出问题,上面三层全歇菜。 有一次客户说协议层老是CRC错误,我让他们先看CC线上的上升沿——结果是BMC编码的抖动太大了,拉一根长USB线就丢帧。
2.3 消息类型:Control、Data、Extended,一个都不能少
PD协议的消息分三种:Control Message(控制消息)、Data Message(数据消息)和Extended Message(扩展消息)。你掌握好这个分类,就能看懂绝大部分的PD通信。
2.3.1 控制消息:短小精悍,用于握手指令
控制消息没有数据负载,只有消息头。最常用的几个:
- GoodCRC —— 收到任何消息后的确认应答,丢了就要重传
- GotoMin —— 让电源降到最低电压,我用来测试欠压保护
- PS_RDY —— Source输出功率就绪,高端充电器常在这里卡住
- Accept/Reject —— 对方是否同意你的请求,最粗暴的握手方式
避坑指南: 我曾经在量产产线上发现一批设备GoodCRC回应超时,查了半天发现是PCB走线太长导致BMC信号反射。把CC线上的RC滤波改到1nF+100Ω就解决了。
2.3.2 数据消息:携带具体数值,协商功率全靠它
数据消息有消息头+数据对象。举个例子:
- Source_Capabilities —— Source能提供的电压/电流组合(PDO列表)
- Request —— Sink从中选一个组合(RDO)
- BIST —— 内置自检,我做认证测试时常用
- VDM —— Vendor Defined Message,大厂用来做私有协议
// 一个典型的Source_Capabilities PDO解析(5V/3A, 9V/3A, 20V/5A)
// 以16进制数据为例:
// 原始数据:0x2C 0x91 0x00 0x00 0x2C 0x91 0x40 0x00 0xB4 0x02 0x50 0x00
// PDO1: 5V/3A -> 电压=5000mV, 电流=3000mA
// PDO2: 9V/3A -> 电压=9000mV, 电流=3000mA
// PDO3: 20V/5A -> 电压=20000mV, 电流=5000mA
// 注意:电压单位0.05V,电流单位0.01A
说白了,数据消息就是PD世界里讨价还价的「报价单」和「订购单」。你想想看,Source拿出三个配置让你挑,Sink选一个并提出「我要这个」,这个过程在面试中最常被考到。
2.3.3 扩展消息:PD 3.0才有的「大文件」
扩展消息比数据消息能装更多字节,比如:
- Source_Capabilities_Extended —— 支持EPR时会发,列出48V等超高压档位
- Status —— 设备健康状态:温度、电压、电流实时值
- Battery_Status —— 电池充放电信息,笔记本专用
特别注意: 扩展消息的长度字段在消息头里比较隐蔽。我有个同事在解析Source_Capabilities_Extended时漏看了数据大小位,结果读出来的电压值全部偏移了4个字节。从那以后我写解析代码一定先读MessageHeader[3:0]里的DataSize。
2.4 消息交互流程:一个完整Power Negotiation的例子
光说不练假把式。我们看一个最简单的5V→9V升压协商过程:
- Source发送Source_Capabilities(数据消息),列出PDO1=5V/3A,PDO2=9V/2A
- Sink回复GoodCRC(控制消息),告诉Source我收到了
- Sink发送Request(数据消息),选中PDO2,要求9V/2A
- Source回复GoodCRC + 稍后发送Accept(控制消息)
- Sink回复GoodCRC
- Source调整输出电压到9V(硬件完成),然后发送PS_RDY(控制消息)
- Sink回复GoodCRC,开始正常拉载
这个流程你在逻辑分析仪上能看到六七个报文。我建议初学者先用一个USB PD分析仪抓一遍完整流程,跟规范逐条对照,印象会深刻很多。
| 步骤 | 发送方 | 消息类型 | 数据内容(简化) |
|---|---|---|---|
| 1 | Source | 数据消息 | Source_Capabilities [5V/3A, 9V/2A] |
| 2 | Sink | 控制消息 | GoodCRC |
| 3 | Sink | 数据消息 | Request [Object: 9V/2A] |
| 4 | Source | 控制消息 | Accept |
| 5 | Sink | 控制消息 | GoodCRC |
| 6 | Source | 控制消息 | PS_RDY |
| 7 | Sink | 控制消息 | GoodCRC |
调测小技巧: 如果Sink一直趴在5V不升压,90%的情况是Sink没发出Request。我的调测习惯是先在协议层开日志,打印每次收到的Source_Capabilities,确认PDO列表是否正确。——这个习惯帮我省了至少三次返工。