2、物理层基础:CC线功能详解、BMC编码原理、Chunking机制与数据包结构

好,咱们直接进入正题。物理层这东西,说白了就是协议栈的「地基」。你上层逻辑写得再漂亮,物理层没搞定,一切白搭。我在做第一个PD项目时,就吃过这个亏——CC线电平没配好,设备死活不握手,查了三天才发现是上拉电阻选错了。嗯,咱们今天就把这些坑一个个填上。

2.1 CC线:不只是根检测线

CC线,全称Configuration Channel。很多人以为它只是用来检测插拔的,其实它身兼数职:

  • 连接检测:判断Source和Sink是否连上了
  • 角色识别:谁是供电方,谁是受电方
  • 电流能力广播:Source通过CC线告诉Sink「我能给多少电流」
  • 数据传输通道:BMC编码的PD消息就在这根线上跑

你想想看,一根线干了这么多活,设计上自然有讲究。

2.1.1 上拉与下拉电阻

Source端会在CC线上拉一个电阻Rp,Sink端则下拉一个电阻Rd。这个电阻值不是随便选的,它直接决定了通信的电压域。我习惯把Rp和Rd的匹配关系记成一张表:

Source电流能力 Rp电阻值 CC线电压范围
默认USB (0.5A/0.9A) 36kΩ ± 1% 0.85V ~ 1.65V
1.5A 12kΩ ± 1% 1.65V ~ 2.45V
3.0A 4.7kΩ ± 1% 2.45V ~ 3.3V

关键点:Sink端通过检测CC线上的电压,就能知道Source能提供多大电流。这个电压是Rp和Rd分压的结果。公式很简单:Vcc = 3.3V * Rd / (Rp + Rd)。

避坑指南:我曾经在一个量产项目上,因为用了5%精度的电阻,导致CC线电压刚好落在两个档位的边界上。结果同一批板子,有的识别成1.5A,有的识别成3.0A。从那以后,我所有PD项目都只用1%精度的电阻,多花几分钱,省去一堆麻烦。

2.2 BMC编码:双相标记编码

BMC,全称Biphase Mark Coding。说白了,就是一种自同步的编码方式。为什么要自同步?因为PD通信没有单独的时钟线,接收端得从数据信号里把时钟「挖」出来。

2.2.1 编码规则

BMC的规则其实很简单:

  • 每个bit的起始处,电平一定会翻转
  • 如果传输的是逻辑「1」,在bit中间再翻转一次
  • 如果传输的是逻辑「0」,bit中间不翻转

举个例子:你要发送二进制数据 1010。编码后的波形是这样的:

  • bit 1:起始翻转,中间翻转 → 两个翻转
  • bit 0:起始翻转,中间不翻 → 一个翻转
  • bit 1:起始翻转,中间翻转 → 两个翻转
  • bit 0:起始翻转,中间不翻 → 一个翻转

你发现没有?每个bit的起始处都有翻转,这就保证了接收端总能找到bit的边界。我刚开始学BMC时,总觉得它比NRZ复杂,后来发现它抗干扰能力确实强——因为翻转就是信息,直流分量被消除了。

2.2.2 编码速率与容差

PD 3.0的BMC速率是300kbps,容差要求±0.35%。这个精度要求其实挺高的。我记得有一次调试,示波器上看波形完全正常,但协议分析仪就是报CRC错误。最后发现是晶振偏差大了,300kbps的时钟偏了0.4%,刚好超出规格。

注意:BMC解码时,接收端会用一个4倍频的采样时钟(1.2MHz)来采样。每个bit采样4次,通过判断翻转位置来解码。如果你的MCU主频不够,或者定时器精度不行,解码很容易出错。

2.3 Chunking机制:大消息的分片传输

PD 3.0引入了一个新东西——Chunking。为什么要搞这个?因为PD消息的最大长度是固定的,但有些消息(比如固件升级、VDM命令)可能很长,一次发不完。

Chunking的规则是这样的:

  • 发送端把大消息切成多个Chunk
  • 每个Chunk独立发送,有自己的包头和CRC
  • 接收端收到所有Chunk后,再组装成完整消息
  • 最后一个Chunk会带一个特殊标记,告诉接收端「发完了」

我个人的经验是,Chunking在实现时最容易出问题的地方是超时处理。协议规定,两个Chunk之间的间隔不能超过1ms。如果你在中断里处理得太慢,或者DMA传输有延迟,很容易超时。我曾经在一个项目上,因为RTOS的任务切换延迟,导致Chunk间隔超标,接收端直接丢弃了整个消息。

2.4 数据包结构:从SOP到CRC

PD的数据包结构,我习惯把它分成四层来看:

  1. 前导码(Preamble):一串固定的0和1交替序列,用于接收端同步时钟。长度是64个bit。
  2. SOP(Start of Packet):包起始序列,告诉接收端「数据要来了」。SOP有几种变体,比如SOP、SOP'、SOP'',用于区分不同的通信角色。
  3. 数据负载(Payload):真正的消息内容,包括消息头、数据对象(VDO、SDO等)。
  4. CRC(Cyclic Redundancy Check):32位循环冗余校验,用于检测传输错误。

一个完整的PD数据包,在BMC编码后看起来是这样的:

Preamble (64 bits) + SOP (4 bits) + Payload (可变长度) + CRC32 (32 bits) + EOP (End of Packet, 8 bits)

重要:CRC32覆盖的范围是从SOP之后到CRC之前的所有数据。接收端收到后,会重新计算CRC,跟收到的CRC比对。如果不一致,直接丢弃这个包,不回复任何确认。

2.4.1 消息头结构

消息头是Payload的第一个16位,它告诉接收端「这是个什么消息」。结构如下:

位域 长度 说明
Message Type 5 bits 消息类型,比如Request、Accept、PS_RDY等
Port Role 2 bits 端口角色:Source、Sink、DRP等
Specification Revision 2 bits 协议版本:PD 2.0还是PD 3.0
Number of Data Objects 3 bits 后面跟着几个数据对象(0~7)
Message ID 3 bits 消息序号,用于去重和重传
Reserved 1 bit 保留位,必须为0

嗯,这里要注意Message ID。协议规定,同一个方向连续发送的消息,Message ID必须递增。如果接收端收到两个相同Message ID的消息,说明是重传,直接丢弃第二个。这个机制在信号不好的时候特别有用。

2.5 实战中的几个坑

最后,分享几个我在物理层调试中踩过的坑:

  • CC线电容:CC线上不能加太大的滤波电容,否则BMC信号的上升沿会变缓,导致解码错误。我一般控制在100pF以内。
  • ESD保护:CC线必须加ESD保护,但要注意TVS管的结电容。有些TVS管结电容高达几十pF,加上去之后波形就变形了。
  • 地线回路:PD通信对地线噪声很敏感。我见过一个案例,因为PCB布局不合理,CC线的参考地跟大电流回路共用了同一段走线,结果通信时断时续。

好了,物理层的基础就讲到这里。下一章咱们会深入协议层,看看那些PD消息是怎么组织、怎么交互的。记住一句话:物理层搞不定,上层全是白搭。