USB PD 协议层框架:SOP、SOP'、SOP'' 报文与消息分类
这一章我们聊聊协议层的整体骨架。说白了就是搞清楚消息怎么在总线上跑,以及跑的是哪类消息。我记得刚接触 PD 时,看到 SOP、SOP'、SOP'' 这三个词特别懵——不就发个包吗,搞这么多花样?后来踩了坑才明白,不区分这些,连线缆里的 eMarker 都读不出来。
从物理层到协议层:谁在跟谁说话
USB PD 的物理层,我们上一章说了是 BMC 编码加 1.1Mbps 的差分信号。但这条总线上一共挂着好几个角色:Source(供电方)、Sink(受电方)、Cable(线缆内部的电子标签芯片)。你可以把它们想象成三拨人,用同一部对讲机,但需要不同的“呼号”才不会串线。
这个“呼号”就是 SOP(Start of Packet)。协议层在封装消息时,先塞一个 SOP 序列,告诉接收方:这个包是给你谁的。
三种 SOP 的用途(USB PD 规范明确划分):
- SOP(同步码 0b01):端口 ↔ 端口。比如 Source 给 Sink 发 Source_Capabilities。
- SOP'(同步码 0b10):端口 ↔ 电缆一端。Source 读取线缆的电子标签(eMarker),或者 Source 告诉线缆切换 VCONN。
- SOP''(同步码 0b11):端口 ↔ 电缆另一端,或者线缆内部两芯片之间的通信。实际项目中用得少,但明确这么定义的。
在我参与的一个 EUV 充电器调试中,Sink 总是不认线缆,抓包发现 Sink 发了 SOP' 请求,线缆根本没回。换了一条线又好了。检查了半小时,结果是线缆的 eMarker 芯片焊接虚焊。那之后我养成了一个习惯:遇到 PD 通信失败,先拿逻辑分析仪看 SOP 前缀对不对,别一上来就改协议栈代码。
消息分类:全功能消息 vs 控制消息
知道包是发给谁之后,还得看包里装的是什么。PD 协议把消息分成两大类——控制消息(Control Message) 和 全功能消息(Data Message,也叫数据消息)。区分方法很简单:看消息头里的 Message Type 字段最高位(bit 5~4)有没有被置位。实际判断上,控制消息不携带任何 Data Object,全功能消息至少带一个。
| 对比项 | 控制消息 | 全功能消息 |
|---|---|---|
| 消息头结构 | 仅有前 16 字节(消息头) | 消息头 + 至少 4 字节 Data Object |
| Message Type 范围 | 0x00 ~ 0x0F(但实际只用到部分) | 0x10 ~ 0x1F(也留了扩展空间) |
| 常见示例 | GoodCRC、Accept、Reject、Wait、PS_RDY、Soft_Reset、Not_Supported、Get_Source_Cap、Get_Sink_Cap | Source_Capabilities、Request、BIST、VDM、Enter_Mode 等 |
| 发送频率 | 极高(比如每收到一个全功能消息都要回 GoodCRC) | 较低(仅在协商或模式切换时出现) |
| 数据处理 | 接收方只需解析消息头,立即决定应答或丢弃 | 需要解析 Data Object 中的结构化信息(如电压电流组合) |
一个小技巧:抓包时如果看到大量 GoodCRC 和 PS_RDY,基本确认物理层和链路层没问题。如果连 GoodCRC 都收不到,那就别纠结协议,先回去修硬件吧。
协议层完整一帧长什么样?
把前面概念串起来,一个完整的 PD 协议层帧包含以下部分:
- SOP 序列(SOP / SOP' / SOP'')— 4~5 个字节,包含前导同步码和 K-code。
- 消息头(Message Header)— 前 16 位:包含报文长度、Message Type、端口角色、Spec Revision 等。
- Data Object(可选) — 若为全功能消息,这里跟着 1~7 个 32-bit 的数据对象。
- CRC(Cyclic Redundancy Check) — 2 字节校验,覆盖消息头 + data object。
- EOP(End of Packet) — 告诉接收方“包发完了”。
你想想看,如果你只掌握消息头和数据体,却搞错了 SOP 类型,线缆里的 eMarker 根本不搭理你。我见过一个同行把 SOP' 报文错写成 SOP 发给线缆,结果线缆一直不响应,他以为芯片坏了——其实就是错了一个 bit。
避坑:别忽视 SOP 的 Extra Mode
你可能会觉得 SOP 和 SOP' 已经够用了。但有几点容易翻车:
- Cable 有两个芯片时,SOP'' 才有意义。很多双芯线缆(比如 Thunderbolt 兼容线)内部有两个 eMarker,它们之间需要用 SOP'' 互传信息。我曾经遇到一个测试案例:Sink 明明应该读线缆右端芯片,却一直在发 SOP',结果读回来的全是零。后来改了 SOP 类型才正常。
- DevNote 里建议 SOP' 和 SOP'' 使用不同的 VCONN 供电策略。如果你在调试线缆时发现 eMarker 不应答,优先检查 VCONN 电压是否在 3.0~5.5V,而不是怀疑 SOP 编码。
- 控制消息的 GoodCRC 是唯一一个没有 CRC 校验的?错!GoodCRC 本身也带 CRC,只不过它是接收方回复发送方,表示数据包已正确到达。它的消息头里携带了被确认包的字节计数和 Message Type。我之前犯过一个低级错误:在软件里把 GoodCRC 的 CRC 计算写反了,结果对方一直 NAK……
重要提醒:不要用 SOP 去读线缆 eMarker,也不要用 SOP' 去做端口协商。规范里明确写了每种 SOP 的使用边界,乱用会导致设备无法进入 PD 协议层。如果协议分析仪上看到大量 CRC 错误,先确认你抓的包是不是用了正确的 SOP 类型。
深入:消息头里的“隐藏位”
最后分享一个提高调试效率的小细节。消息头的 bit 6 和 bit 7 表示 Spec Revision:
| bit[7:6] | 含义 |
|---|---|
| 0b00 | PD 2.0 规范 |
| 0b01 | PD 3.0 规范 |
| 0b10 | PD 3.1 / 3.2 规范(扩展功率范围) |
我曾经调过一款 PD 3.0 充电器,Source 发出来的 Source_Capabilities 里只包含标准的 5V/9V/15V/20V 组合。但 Sink 是 PD 3.1 设备,要求读扩展的 28V/36V/48V。按 PD 3.1 规范,这种扩展功率消息的 Revision 字段必须是 0b10。如果 Source 只认 0b01,直接就忽略不发了。那会儿我对着逻辑分析仪查了两天,才在消息头的 2 个 bit 上找到了原因。
嗯,这一章把协议层的基本框架立起来了。掌握了 SOP 家族和消息分类,下一层才能分析具体的二进制怎么拼接 — 不过,那是后面的事了。