第2章:PD协商流程与关键状态归纳总结
PD协商,说白了就是Source和Sink之间的“讨价还价”。
Source说:“我能提供5V、9V、15V、20V,每种电流多少。”
Sink回:“给我9V 3A,谢谢。”
Source确认:“OK,Ready。”然后电压电流才真正切换。
这中间任何一个环节出错,设备要么不充电,要么乱跳电压。我调过不少次这种bug,所以这一章我把整个流程拆开,把关键状态拎出来,咱们一条一条过。
2.1 完整协商流程图
先上一张图,把整件事串起来。你盯着它看三分钟,再往下读心里就有底了。
2.2 每一步在做什么
图中的5个节点,就是一次成功协商必须走过的台阶。少一个都不行。
- 设备连接:VBUS拉高,Source检测到Sink的存在。这是物理层的事,但软件里也要确认CC线状态。
- Source Capabilities:Source通过CC线广播自己支持的所有电压/电流组合(PDO)。消息类型是
Source_Capabilities。 - Sink Request:Sink从收到的PDO里选一个,发
Request消息。里面关键字段是Object Position,指向选中的第几个PDO。 - Source Accept:Source检查请求合法后,回复
Accept。如果Source不认可(比如电流超出),它会回Reject,协商直接崩。 - PS_RDY:Source完成所有内部电压/电流切换后,发
PS_RDY。只有收到这个消息,Sink才敢大功率拉电流。
2.3 关键状态机介绍
PD协议里,两边的状态机名字又长又绕。我习惯只看几个核心状态,剩下的遇到再查。
常用状态对照表(Source侧):
| 状态缩写 | 全称 | 做什么 |
|---|---|---|
| PE_SRC_Capabilities | Source Capabilities 发送等待 | 发完Capabilities后等待Sink回复 |
| PE_SRC_Negotiate | 协商中 | 收到Request后处理,决定Accept/Reject |
| PE_SRC_Transition | 电压切换中 | 收到Accept后调压,完成后发PS_RDY |
| PE_SRC_Ready | 就绪 | 稳定供电,持续发送PS_RDY或保持 |
我的经验:Sink侧的状态更简单。但要注意PE_SNK_EvaluateCapabilities这个状态——很多固件在这里选了不存在的PDO。我翻过一个项目,Sink的Request写死了Object Position = 3,可Source只发了2个PDO,结果一直收不到Accept,整机不充电。
2.4 避坑指南:常见错误
讲了这么多理论,说几个我实际踩过的坑,你写固件或者排查时直接对应。
- Capabilities 消息格式不对:PD 3.0要求SourceCapabilities的MessageHeader里
Number of Data Objects必须和实际PDO数量一致。我见过有人填错了,Sink解析后直接崩溃。 - Request 里的 Checksum 算错:Request消息的CRC32计算方式与普通数据包一致,但很多新手把
Object Position等字段的字节顺序搞反了。 - SOP 序列用错:PD协商全部走
SOP(即Source→Sink方向),不要用SOP'或SOP''。这些是给VCONN或Cable用的。有一次我在调一个多口Hub,把SOP'发给了Source,Source完全不理。 - PS_RDY 超时:Source切换电压需要时间(通常在10ms内)。如果Sink在发送Request后等了超过500ms还没收到PS_RDY,协议规定要发
Hard Reset。我建议你在Sink固件里加上超时计数器,省得死等。
2.5 用逻辑分析仪抓一次完整协商
光看书不实战,等于白看。我整理了一份抓包后的数据,你跟着走一遍。
假设Source发了一条Capabilities,包含两个PDO:
# 原始二进制(4字节Header + 8字节 PDO1 + 8字节 PDO2)
Header: 0x2C 0xF1 0x00 0x02 (MessageID=2, #DataObj=2)
PDO1: 0x19 0x2C 0x05 0x00 0x00 0x00 0x00 0x00 (5V @ 3A Fixed)
PDO2: 0x19 0x2C 0x09 0x00 0x00 0x00 0x00 0x00 (9V @ 3A Fixed)
CRC32: 0xA3 0x4B 0x7C 0xFE
你看,PDO1的第一个字节0x19表示Fixed Supply,第2、3字节0x2C 0x05表示电压是500mV * 0x2C(44) = 22V?不对!注意电压编码:实际是(Voltage & 0x03FF) * 50mV。0x2C05的低10位是0x05(5V),高位是控制位。这里容易算错,建议参考PD3.0标准第6.4.1节。
Sink回Request时,Object Position填0x01(选第一个PDO)。Source收到后如果发现电流不符合,会返回Reject。最好的做法是Sink遍历所有PDO,挑最能满足自己功率需求的,而不是写死。
2.6 小结
PD协商,说白了就是5个步骤、4个关键状态。大部分问题出在消息格式上(CRC、Checksum、PDO数量),其次是时序(超时)。
我自己习惯先把逻辑分析仪抓到的数据用PD Analyzer解码,再看是哪一步卡住了。遇到僵局,就先复位CC线,重新来一轮。