PD协议基础:协议栈分层、消息类型与VDM

各位同学,今天我们正式进入PD协议的世界。说实话,PD协议是Type-C接口的灵魂。没有它,Type-C就是个普通的充电口。我最早接触PD协议是在2016年,当时做一款快充适配器,被协议栈的复杂度吓了一跳。但摸清楚之后,你会发现它的设计其实非常优雅。

PD协议栈的分层结构

PD协议栈,说白了就是一套分层的通信架构。它跟TCP/IP有点像,每一层各司其职。我习惯把它分成四层来看:

层级 名称 职责
第4层 协议层(Protocol Layer) 消息组装、状态机管理、策略引擎
第3层 传输层(Transport Layer) 数据包封装、重传机制、CRC校验
第2层 物理层(Physical Layer) BMC编码、电气特性、信号时序
第1层 线缆层(Cable Layer) CC线通信、阻抗匹配、EMC

嗯,这里要注意。实际开发中,我们最常打交道的是协议层和传输层。物理层的事情,一般芯片厂商已经帮你搞定了。但如果你要做芯片级的开发,那物理层的BMC编码细节就绕不过去了。

核心要点:PD协议栈的分层设计,让不同厂商的芯片可以互相通信。你只要保证协议层兼容,底层实现可以各不相同。我在项目中见过不少因为分层理解不到位导致的兼容性问题,尤其是传输层的重传超时参数,不同芯片的默认值可能不一样。

消息类型:控制消息与数据消息

PD协议里的消息,分成两大类:控制消息和数据消息。这个分类方式,我一开始也觉得有点绕,但用多了就发现其实很直观。

控制消息(Control Message)

控制消息,你可以理解为「握手信号」。它们没有数据负载,只有消息头。长度固定,通常是24位。常见的控制消息有:

  • GoodCRC:确认收到,相当于TCP的ACK。我调试时最喜欢看这个,如果GoodCRC不回来,说明链路有问题。
  • GotoMin:让对端切换到最小功率模式。省电场景下很常用。
  • Accept:接受请求。比如你发了一个Request消息,对方回Accept,表示同意你的功率请求。
  • Reject:拒绝请求。嗯,这个在兼容性测试中经常出现,尤其是老设备不支持新协议版本时。
  • PS_RDY:电源就绪。告诉对端,电压电流已经切换好了,可以开始供电。

实战技巧:我曾经在调试一个PD充电器时,发现手机总是无法进入快充模式。抓了日志一看,原来是充电器在收到Request后,回复了Reject。原因是对端请求的电压档位,充电器不支持。后来我加了一个Source_Capabilities的广播,问题就解决了。

数据消息(Data Message)

数据消息,顾名思义,是带数据负载的。它们可以携带0到260字节的数据。数据消息的类型就多了,我挑几个重点的说:

  • Source_Capabilities:源端广播自己的供电能力。比如支持5V/3A、9V/2A、15V/1.5A等。这是PD通信的第一步。
  • Request:接收端向源端请求具体的电压电流。你想想看,手机插上充电器后,就是通过这个消息告诉充电器「我要9V快充」。
  • BIST:内置自测试。生产测试时用的多,平时开发很少碰。
  • VDM:厂商自定义消息。这个我们下面单独讲。

数据消息的格式,我简单说一下:消息头(16位)+ 数据负载(可变长度)+ CRC(32位)。消息头里包含了消息类型、数据长度、方向等信息。

// 数据消息结构示例
typedef struct {
    uint16_t header;      // 消息头:类型、长度、方向等
    uint8_t  data[260];   // 数据负载,最大260字节
    uint32_t crc;         // CRC32校验
} pd_data_message_t;

VDM与Discover Identity

VDM,全称是Vendor Defined Message。说白了,就是留给厂商自己发挥的空间。PD协议规定了一些标准消息,但每个厂商都有自己的小算盘,比如苹果的快速角色切换、谷歌的固件升级等,都是通过VDM实现的。

VDM又分成两种:

  • UVDM(Unstructured VDM):无结构VDM,厂商自己定义格式。爱怎么玩就怎么玩。
  • SVDM(Structured VDM):结构化VDM,遵循PD协议规定的格式。Discover Identity就是SVDM的一种。

Discover Identity 详解

Discover Identity,我习惯叫它「设备自报家门」。当一个设备接入Type-C口后,通过这个命令,可以获取到对端的身份信息。包括:

  • VID(Vendor ID):厂商ID,由USB-IF分配。比如苹果的VID是0x05AC。
  • PID(Product ID):产品ID,厂商自己定义。
  • XID(eXtended ID):扩展ID,用于区分同一产品的不同版本。
  • 产品类型:是Hub、外设、还是线缆?
  • 支持的VDM版本:比如支持1.0还是2.0?

避坑指南:我曾经在一个项目中,因为Discover Identity的响应超时,导致整个PD协商失败。后来发现是线缆的eMarker芯片响应太慢。PD协议规定,Discover Identity的响应必须在15ms内返回,否则对端会认为设备不支持VDM。所以,如果你在做eMarker芯片的开发,一定要注意这个时序要求。

Discover Identity的请求和响应,都是通过SOP'(Start of Packet Prime)来传输的。SOP'是专门用于线缆通信的包类型。普通设备之间的通信用SOP,线缆相关的用SOP'。这个区分,我刚开始也搞混过。

// Discover Identity 请求格式(简化版)
// SOP' + 消息头 + VDM Header + CRC
// VDM Header 中的 Command 字段设为 0x01(Discover Identity)

// 响应格式(简化版)
// SOP' + 消息头 + VDM Header + 身份信息 + CRC
// 身份信息包含:VID(16bit) + PID(16bit) + XID(32bit) + ...

你想想看,如果没有Discover Identity,两个设备插在一起,都不知道对方是谁。有了这个机制,充电器可以知道「哦,插进来的是个iPhone,那我给它输出苹果的快充协议」。这就是VDM的魅力所在。

小结

这一章我们讲了PD协议栈的分层结构、控制消息和数据消息的区别,以及VDM和Discover Identity。说实话,这些概念刚接触时会觉得有点抽象,但只要你动手抓一次PD通信的日志,看着那些消息在CC线上飞来飞去,一切就都清晰了。

下一章,我们会深入PD的电源协商流程,看看Source和Sink是怎么讨价还价的。到时候我会拿一个真实的充电器案例来拆解,保证让你看得过瘾。