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怎么讨价还价的。那才是真正有意思的部分。