PD消息结构:协议沟通的通用语言
做PD Negotiation这么多年,打交道最多的就是这个消息结构了。
PD消息的结构其实不复杂,就三部分:消息头、消息体,还有CRC校验。你想想看,就像寄快递,头是快递单,体是包裹,CRC是封条。
我个人习惯把这套结构背得滚瓜烂熟。为什么?因为你要和Source、Sink来回讨价还价,消息结构就是你们共同的语言。语言不通,谈判肯定崩。
消息头(Header)
消息头占16位(2个字节),是整条消息的"身份证"。它告诉对方:我是谁、我想干嘛、我说了多长的内容。
| 字段名 | Bit位 | 含义 | 我踩过的坑 |
|---|---|---|---|
| Message Type | 15:12 | 消息类型号(0-15) | 有一次我写错类型号,Source直接不理我了 |
| Port Role | 11:10 | 当前端口角色 | DFP/UFP搞反,消息全被丢弃 |
| Specification Revision | 9:8 | 协议版本(2.0还是3.0) | 版本不匹配,协商直接失败 |
| Message Type (Sub) | 7:6 | 子类型(保留扩展) | 一般不用的字段,但得知道它存在 |
| Data Object Count | 5:4 | 消息体包含的Data Object数量 | 数错这个字段,CRC永远对不上 |
| Extended | 3 | 是否为扩展消息 | 嗯,这里要注意,和消息类型是两码事 |
| Direction | 2 | 发送方向(Initiator/Responder) | 方向反了,对方会认为你在胡扯 |
| Reserved/STOP | 1:0 | 保留位 或 停止位 | 标准留的余量,别乱写 |
说实话,我最头疼的是Message Type这个字段。协议里定义了十几类消息,比如Source Capabilities、Request、Accept、Reject...每个都有固定编号。你写错了,对方解析出来的就是你根本没发过的消息。
我遇到过最奇葩的bug是:某次我把0x01(Source Capabilities)写成了0x02(Request),结果Sink端收到后以为自己发的请求被同意了,直接开始拉电流,把整个系统搞崩了。你说冤不冤?
消息体(Payload / Data Object)
消息体在协议里叫Data Object,每个Data Object固定32位(4个字节)。
消息头里的Data Object Count就是告诉对方本轮有几个Data Object。最常见的是1个(比如Power Delivery Object),也有2个、3个的情况(比如带电池信息的消息)。
我建议你把这个字段当做一个协议状态机来理解。因为PD消息的个数不是随便定的,协议明确规定了哪种消息必须带几个对象。
我记得刚接手一个PD项目时,前任写的代码里消息体长度直接写死0。结果发出去的Source Capabilities消息没有附上任何电压电流信息,对面设备根本看不懂你要提供什么,协商直接卡死。后来查了一天,才发现是这里的问题。
CRC校验
CRC的全称是Cyclic Redundancy Check,本质上是消息的"指纹"。
PD协议里使用的是CRC-32算法,覆盖范围是消息头+消息体。你想想看,一条消息从发送到接收,中间经过线缆、连接器、各种静电打火,难免会有比特翻转。这时候CRC就能帮你判断——这条消息到底有没有坏。
为什么CRC很重要?因为在PD Negotiation里,Step和Step之间的时序卡得很死。如果消息错了,对方会直接回一条Soft Reset或Hard Reset,整个协商就得从头来过。你说气不气人?
消息类型:Control、Data、Extended
注意了,这条消息类型和消息头里的Message Type是两回事。这个消息类型说的是消息的整体分类,而不是具体的功能编号。
我见过很多新手在这儿犯迷糊。他们以为"消息类型"就指Control、Data、Extended这三种。不对,这三种是"大类",而Header里的Message Type是"小类"。
| 类型 | 特征 | 例子 |
|---|---|---|
| Control Message | 不带Data Object,只有Header | GoodCRC、Accept、Reject、Soft Reset |
| Data Message | 带至少1个Data Object | Source Capabilities、Request、BIST |
| Extended Message | 消息头里Extended位=1,数据包更长 | Extended Source Capabilities、Status |
实际项目里Control Message用得最频繁。因为每次发完一个数据消息,对方必须回一个GoodCRC来确认收到。你没收到这个GoodCRC,就得重发。这是协议层面的应答机制。
我做过一个调试——用示波器量CC线上的信号,发现某个从设备从来不发GoodCRC。后来拆开一看,固件里把GoodCRC对应的Message Type写成0x00(保留值)。你说这找谁说理去?
至于Extended Message,它是PD 3.0引入的玩意儿。普通的Data Message最多带几个Data Object,但Extended Message可以带更多数据,适合传送厂商自定义信息或复杂状态。不过,说白了,你要是只用标准PD充电,大部分场景碰不到它。
一张图看懂PD消息结构
下面这张SVG图,是我画给自己团队培训用的。它把消息头、消息体、CRC、以及三种消息类型的关系串在一起。你看完应该能有个全景概念。
你从图里可以看出来,Control Message其实就是只有Header、没有Payload、没有CRC的短消息。但不对——仔细看,实际上Control Message也有CRC,它的CRC只覆盖Header自己。这也是协议的规定。
嗯,这里要记住:Control Message虽然短,但它的完整性同样依赖CRC保护。别因为它简单就不重视。
为什么要理解这三者的关系?
说白了,你的调试逻辑是这样的:
- 收到一条消息,先在固件里解析Header,拿到Message Type和Data Object Count。
- 根据Count决定要等几个Data Object。
- 算CRC,比对。
- 看Extended位,决定是普通数据消息还是扩展消息。
如果任何一步出错——比如你解析成Data Message但对方发的是Control Message,Object Count对不上,CRC校验不过——协议状态机就会被打断,轻则重传,重则硬复位。
我建议你在写固件时,专门加一个消息解析调试函数,把收进来的每一个字节都打印出来,包括Header各字段的解析结果。调试时开着串口或者逻辑分析仪,逐个字段验证。
总的来说,PD消息结构是谈判的基石。你再会写策略,消息格式不对,连第一轮Source Capabilities都发不出去,后面的电压协商、电流协商全都无从谈起。