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 协议层帧包含以下部分:

  1. SOP 序列(SOP / SOP' / SOP'')— 4~5 个字节,包含前导同步码和 K-code。
  2. 消息头(Message Header)— 前 16 位:包含报文长度、Message Type、端口角色、Spec Revision 等。
  3. Data Object(可选) — 若为全功能消息,这里跟着 1~7 个 32-bit 的数据对象。
  4. CRC(Cyclic Redundancy Check) — 2 字节校验,覆盖消息头 + data object。
  5. EOP(End of Packet) — 告诉接收方“包发完了”。

你想想看,如果你只掌握消息头和数据体,却搞错了 SOP 类型,线缆里的 eMarker 根本不搭理你。我见过一个同行把 SOP' 报文错写成 SOP 发给线缆,结果线缆一直不响应,他以为芯片坏了——其实就是错了一个 bit。

USB PD 协议层通信框架(SOP 角色与消息分类) Source Sink Cable(eMarker 芯片) SOP SOP' SOP'' 控制消息(无 Data Object) GoodCRC, Accept, Reject, Wait PS_RDY, Soft_Reset, Not_Supported Get_Source_Cap, Get_Sink_Cap 全功能消息(带 Data Object) Source_Capabilities, Request, BIST VDM(Vendor Defined Message) Enter_Mode / Exit_Mode 左端芯片 右端芯片

避坑:别忽视 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 家族和消息分类,下一层才能分析具体的二进制怎么拼接 — 不过,那是后面的事了。