2. PD协议基础概念:Source/Sink/DRP 角色定义、PD通信的物理层与协议层、Chunk与Message结构

好,咱们正式开始啃PD协议的基础概念。这一节我把它叫做「地基中的地基」。你想想看,如果连Source和Sink都分不清,后面谈策略配置那就是空中楼阁。我个人习惯,每接手一个新项目,第一件事就是把角色定义和通信模型在白板上画一遍,哪怕我已经画过几十次了。

2.1 Source / Sink / DRP 角色定义

先说最简单的。USB PD里定义了三种基本角色:

  • Source(源端):提供电力的那一方。说白了就是「往外送电」的。比如充电器、充电宝、扩展坞。
  • Sink(受电端):接收电力的那一方。就是「吃电」的。比如手机、笔记本、耳机。
  • DRP(双角色端口):既能当Source也能当Sink,动态切换。比如现在的Type-C笔记本,插上充电器它是Sink,插上手机它又能当Source给手机充电。

嗯,这里要注意:DRP不是同时做两件事,而是在两个角色之间来回切换。我见过不少新手以为DRP可以一边充电一边放电,那是不可能的——除非你搞的是无线充电那种特殊场景。

关键点:角色是在物理连接建立时通过CC线协商确定的。不是你想当什么就当什么,得看对方的策略和你的能力。

我在项目中遇到过一个问题:某款DRP设备在连接某些老旧充电器时,死活切不到Sink模式。后来发现是CC线上的Rp/Rd电阻匹配出了问题。你想想看,硬件上一个小小的电阻值偏差,就能让整个协议跑不起来。

2.2 PD通信的物理层与协议层

PD通信分两层:物理层和协议层。我习惯这么理解——物理层负责「怎么把话说出去」,协议层负责「说什么话以及怎么理解对方的话」。

2.2.1 物理层

PD的物理层用的是BMC编码(双相标记编码),在CC线上传输。速率是300kbps,不算快,但足够用了。为什么用BMC?因为它自带时钟恢复,不需要额外的时钟线。

我曾经踩过一个坑:某次调试时发现通信时好时坏,示波器一看,CC线上的信号上升沿太缓了。原因是PCB走线太长,寄生电容太大。后来在CC线上加了一颗小电阻做阻抗匹配,问题就解决了。嗯,硬件细节往往决定成败。

物理层的关键参数我列个表:

参数 说明
传输速率 300 kbps 不算快,但PD不需要高速
编码方式 BMC 双相标记编码,自带时钟
传输介质 CC线 Type-C的配置通道
电压摆幅 约1.05V 基于3.3V逻辑,经过电平转换

2.2.2 协议层

协议层就复杂一些了。PD协议层定义了消息的格式、类型、交互流程。说白了就是「你说一句,我回一句」,不能乱来。

协议层有几个核心概念:

  • 消息类型:控制消息(Control Message)和数据消息(Data Message)。控制消息比如GoodCRC、Accept、Reject,数据消息比如Source_Capabilities、Request、BIST。
  • 事务(Transaction):一次完整的请求-响应过程。比如Source发Source_Capabilities,Sink回Request,Source再回Accept,最后Source切换电压——这就是一个事务。
  • 重试机制:如果对方没回应或者回应了错误信息,发送方会重试。我建议重试次数不要超过3次,否则容易造成协议死锁。

我的经验:调试PD协议时,最好用逻辑分析仪抓CC线上的原始数据。只看软件日志有时候会漏掉物理层的异常。我曾经靠逻辑分析仪抓到过一次「对方发了GoodCRC但CRC校验本身是错的」这种奇葩情况。

2.3 Chunk 与 Message 结构

好,接下来是很多人觉得绕的地方——Chunk和Message到底是什么关系?

简单说:Message是逻辑上的完整消息,Chunk是物理传输时的分片。因为PD协议允许消息长度超过单次传输的最大长度(最大有效载荷是26字节),所以需要把长消息切成多个Chunk来发送。

2.3.1 Message结构

一个标准的PD Message由以下几部分组成:

  • Preamble(前导码):用于同步,固定为64个0和1交替的序列。
  • SOP(帧起始):标识消息的开始,有SOP、SOP'、SOP''三种,用于不同场景。
  • Header(消息头):16位,包含消息类型、数据长度、端口号、方向等信息。
  • Data Object(数据对象):0~7个,每个32位。具体内容取决于消息类型。
  • CRC(循环冗余校验):32位,校验整个消息的完整性。
  • EOP(帧结束):标识消息结束。

我刚开始学的时候,总觉得Header里的位域分配特别绕。后来我画了一张位图贴在工位上,天天看,慢慢就熟了。这里我建议你也这么做——把Header的16位拆开,每一位代表什么,记牢了。

2.3.2 Chunk结构

当Message长度超过26字节时,就需要分片。每个Chunk的结构和Message类似,但多了个Chunk编号和总Chunk数信息。

Chunk的规则:

  • 每个Chunk最大28字节(2字节Header + 26字节数据)。
  • 接收方收到所有Chunk后,按编号重组为完整的Message。
  • 如果某个Chunk丢了或者CRC错了,整个Message都要重传。

注意:Chunk机制只在数据消息中使用。控制消息因为长度短,不需要分片。我曾经见过一个协议栈实现,把控制消息也做了分片处理,结果对方设备直接不认——因为规范里明确说了控制消息不分片。

2.3.3 一个实际的例子

假设Source要发送一个包含5个数据对象的Source_Capabilities消息。每个数据对象32位,5个就是160位,加上Header的16位,总共176位,也就是22字节。没超过26字节,所以不需要分片,直接一个Message发出去就行。

但如果某个厂商自定义了一个消息,带了8个数据对象(256位),加上Header就是272位,34字节。这就超了,必须切成两个Chunk:第一个Chunk带26字节,第二个Chunk带剩下的8字节。

// 伪代码示例:Message分片逻辑
if (message_length <= 26) {
    send_single_chunk(message);
} else {
    chunk_count = ceil(message_length / 26);
    for (i = 0; i < chunk_count; i++) {
        chunk = create_chunk(message, i, chunk_count);
        send_chunk(chunk);
    }
}

嗯,代码看着简单,但实际调试时坑不少。我记得有一次,接收方一直报重组错误,查了半天发现是发送方把Chunk编号从0开始,而接收方期待从1开始——规范里写的是从0开始,但某家芯片厂商的实现偏偏从1开始。你说这找谁说理去?

小结

这一节的内容就这些。总结一下:

  • Source/Sink/DRP是角色定义,DRP最灵活但也最复杂。
  • 物理层用BMC编码在CC线上传数据,速率300kbps。
  • 协议层定义了消息类型和交互流程,重试机制很重要。
  • Chunk是Message的分片,只在长消息时使用。

下一节我们会讲PD的电源协商流程,也就是Source和Sink怎么讨价还价的。那才是真正有意思的部分。