ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

自研USB-C快充协议分析仪:PD/QC协议解析与实现

自研USB-C快充协议分析仪:PD/QC协议解析与实现 先说清楚这玩意儿是什么吧。USB-C快充协议分析仪简单说就是能插在充电器和被充设备之间实时把两边的“对话”内容抓出来、解出协议、算好功率的一台小仪器。它能识别QC协议和PD协议还能把电压、电流、功率数据录下来方便你判断充电头有没有虚标、线材是不是偷工减料、设备为什么没能跑满快充。我确认我用过功率计、诱骗器、各种“解锁工具”最后发现都不如自己做一个协议分析仪靠谱。这篇就记录一下我整个设计过程包括硬件选型、固件里PD和QC的解析思路、上位机展示还有实测时踩过的那些坑。适合正在搞电源维修、快充兼容性测试、嵌入式开发或者单纯想研究充电协议的同学参考。1. 项目整体设计与原理拆解1.1 为什么不是功率计也不是诱骗器很多朋友一开始会问市面上几十块钱的USB功率计不是也能测功率吗确实能但它只能告诉你“当前电压、电流、功率是多少”相当于只看结果不看过程。而诱骗器就更极端了它强行把充电头拉到某个电压档位根本不关心充电头到底支持什么协议也不管设备愿不愿意协商这个档位。协议分析仪要做的是“偷听”source和sink之间的完整握手过程。我习惯打一个比方功率计就像你去饭店只看到最后结账单诱骗器是你冲进后厨硬点一道菜而协议分析仪是坐在旁边听顾客和厨师怎么点菜、怎么讨价还价、中途有没有加菜退菜。最后你会得到一串完整的“点菜记录”包括一开始source广播说“我支持5V、9V、12V、20V还有几个PPS档位”然后sink回复“我要20V/3A那一档”双方一拍即合。如果中途电流太大或者电压掉压又会触发重新协商整个过程都能被记录下来。这正是维修和测试中最重要的信息一个充电头标称65W但插上笔记本只能跑45W问题出在PDO广播、线缆限流、设备请求还是充电头稳定输出没有协议分析数据你只能靠猜。所以这个项目的核心定位是“通信监听功率监测”而不是简单的电压电流表。1.2 整体架构从物理链路到最终数据整个系统我拆成了几块物理接入层、信号采集层、主控处理层、数据输出层。物理接入层解决“怎么把分析仪串进充电链路里”。我用了一个USB-C公头接充电器一个USB-C母座接被测设备中间把VBUS、GND、CC1、CC2、D、D-六条关键信号全部引出来。VBUS和GND直接贯穿保证大电流通路不经过任何有源器件这是原则。CC线不能直接贯穿因为PD通信协议要求sink端通过特定电阻下拉如果把source和sink的CC直接接在一起会破坏双方的电平关系。所以我的做法是两个CC口都引到主控芯片由固件决定在什么时间点、用什么电阻下拉相当于把分析仪伪装成一个“既能听又能替设备说话”的中间人。信号采集层包含两套感知一是电流电压采样我用INA226加分流电阻专门负责功率数据二是通信信号采集CC线上的PD信号和D/D-线上的QC信号都要送到主控的输入引脚。主控处理层是灵魂。它要做的事情很多实时监听CC线上的BMC信号、解析PD报文、读取INA226、检测D/D-电平、把协议状态和功率数据打上时间戳打包输出。我最初想过用FPGA做但后来发现普通MCU足够关键在代码思路。数据输出层我用串口把解析结果发到上位机PC端用Python脚本画曲线、记录日志。如果你不太想写上位机也可以直接用串口助手看文本日志但那就浪费了波形回放这个重要功能。2. 核心硬件设计与选型解析2.1 主控与功率监测芯片选型主控芯片的选择直接决定固件难度。PD通信的比特率大概是300kbps一个bit的时间约3.3微秒BMC编码下最短的脉冲宽度只有1.67微秒左右。这意味着主控的GPIO采样能力至少要能达到1微秒级别最好用中断加定时器的方式捕捉跳变沿。我选的是STM32F103C8T6原因是便宜、资料多、定时器资源够用。它的GPIO外部中断响应时间在几百纳秒级别处理PD解码没有问题。如果你手头有RP2040或者ESP32-S3也完全可行特别是RP2040的PIO外设可以直接做PD解码能省不少CPU负担只是上手门槛稍微高一点。功率监测芯片我强烈建议用INA226而不是常见的INA219。INA226的分辨率是16位而且能直接测量VBUS电压和分流电阻两端电压内部带校准寄存器精度比INA219好不少。INA219虽然更便宜但分辨率只有12位在低电流时候的读数波动比较明显。话虽这么说如果你只是想先验证一下原理INA219也能跑后面再升级。分流电阻的选择需要算一笔账。我用了0.01欧姆的金属箔电阻最大允许功耗2W。这个电阻在5A电流下的功耗是I^2R250.010.25W在10A下是1W刚好在2W器件的安全范围内。如果被测设备可能拉到20A那0.01欧姆上就是4W电阻会严重发热必须换成0.005欧姆或者更大的封装。另外注意一点分流电阻一定要用四线开尔文接法也就是说采样线要单独从电阻两端引到INA226的输入引脚不能直接依赖PCB走线电阻否则几个毫欧的走线电阻就会带来百分之几的误差。2.2 CC线与D/D-信号的处理这是整个硬件设计里最容易被新手搞砸的部分。CC线上的PD信号是双向的source会发BMC编码的广播报文sink也会发请求报文。想监听这组通信理论上可以直接把CC线接到主控的GPIO上通过输入捕获模式捕捉跳变沿。但实际调试时你会发现CC线上的信号幅度并不大而且还会叠加一个直流偏置电压source端的Rp上拉电阻会把CC线拉到特定电平必须用RC隔直加偏置电路做预处理。我第一版电路图省事直接把CC线经过一个电阻分压后接进STM32引脚结果发现波形严重畸变根本解不出BMC。后来我把CC信号引到一个高速比较器上比如LM393或者TLV3501设置合适的阈值把BMC波形整形成标准的方波再送进MCU问题就解决了。如果你不想加比较器也可以选用内部带模拟比较器的MCU型号比如STM32G0系列这样外围能少一颗芯片。D/D-线的处理思路又不一样。QC协议的识别机制是通过D/D-上的电压组合来“请求”档位的不是高频数字通信所以不需要比较器整形直接用ADC采样就能判断当前处于什么状态。但问题是D/D-在普通USB通信时候传输的是差分数据信号跟QC协商的电平信号是叠加在一起的中间必须有模拟开关去做通断隔离。我在电路里用了一颗双通道模拟开关固件里控制它在“监听QC电平”和“透传USB数据”两种模式之间切换。这个设计是为了避免设备在走USB数据时分析仪的ADC输入电容把高速信号拖垮。2.3 PCB布局与供电设计PCB布局我吃了不少亏说几个关键点。第一采样电阻两端到INA226的采样线要短、要对称而且不能跟大电流路径平行走线太长。最好在采样电阻正下方铺一块独立的模拟地岛所有模拟信号都在这个岛内走线单点接到数字地。第二CC线和D/D-线要远离VBUS。很多人在两层板上把CC线走在VBUS铜皮旁边结果VBUS上几安培的电流变化耦合出噪声直接把PD通信波形淹没了。我后来把CC线放在内层两侧用GND包住问题明显改善。第三分析仪自身供电不能从被测VBUS上取。原因是VBUS在不协商的时候只有5V协商后可能跳到20V而且还有大电流瞬态波动不能作为稳定的逻辑电源。我单独引了一个USB Micro口给分析仪供电再用一片LDO稳压到3.3V这样主控和比较器的工作电源与被测链路完全隔离。3. 固件与协议解析从波形到数据的完整链路3.1 PD协议的BMC解码原理与实现PD协议在CC线上用的是BMC双相标记编码传输很多人一看“BMC”就头大其实原理并不复杂。每个bit周期内信号必然发生一次跳变作为时序参考逻辑“0”会在bit周期的中间位置再跳变一次而逻辑“1”整个周期都没有额外的跳变。收端只需要测量两次相邻跳变之间的时间间隔间隔短一个周期的一半代表“0”间隔长一个完整周期代表“1”。明白了这个机制解码思路就简单了。我在固件里把CC线接到定时器的输入捕获通道上每次检测到跳变沿就记录一次当前计数值然后计算与前一次的时间差。根据时间差落在“半bit窗口”还是“全bit窗口”输出对应的bit。这里有个关键参数PD的bit率大约300kbps那么一个bit周期约3.33微秒半bit约1.67微秒。定时器频率我设置成1MHz也就是每微秒计一个数这样时间分辨率在0.3微秒左右已经完全够区分“1.67微秒”和“3.33微秒”的间隔。解码出连续的bit流之后还需要做帧同步。PD报文以固定的前导码开头然后是指示报文类型的SOP标记、Header字段、数据区、CRC32校验和、EOP结束符。我一开始试图直接按bit流硬解析结果因为前导码的bit模式识别错误导致整个帧全部错位。后来我改成状态机方式先锁定前导码的特定序列再按固定字段长度往下走每读完一帧就做一次CRC32校验校验不通过就丢弃并重新等待前导码。这样在噪声干扰严重的时候也能保持大部分帧能正确解出。3.2 QC协议的识别机制与实现QC协议和PD协议完全是两个时代的东西。QC2.0时代适配器通过D/D-上的固定电平组合来指示支持的电压档位设备也在D/D-上拉出特定电平来请求想要的档位。QC3.0则引入连续可调电压适配器通过D/D-上的电压变化来控制0.2V步进。到QC4.0之后干脆放弃了这套模拟握手方式直接回归PD协议。所以在分析仪里“识别QC协议”要做的事情不是等一个报文而是持续监测D/D-上的直流电压。我在固件里让ADC以每秒几百次的频率对D和D-采样然后根据两者电压范围判断当前处于哪个阶段是没握手时的高电平空闲状态还是已经进入了某个电压档位或者是处于QC3.0的连续调节过程。有一个容易踩的坑QC握手过程中D/D-上的电平变化速度比你想象中要快尤其是QC3.0的步进切换可能只有几十毫秒间隔。如果ADC采样频率太低就会漏掉中间过程最后只看到跳变前和跳变后的两个状态。所以我强制把D/D-的ADC采样率提到1kHz以上。当然这个采样率对STM32来说毫无压力。3.3 功率监测、时间戳对齐与数据融合功率监测的固件部分相对独立。INA226通过I2C接口读取内部有电压寄存器和电流寄存器直接把读取值经过换算就能得到当前功率。我在初始化时写入了校准参数让芯片能直接输出以毫安为单位的电流值。这个校准值不是随便填的它依赖于分流电阻的实际阻值。我这里用的0.01欧姆所以校准寄存器算出来是相应的一组数值。如果你换了不同阻值的电阻这里一定要重新算否则电流显示会差好几倍。但单独读电压电流意义不大真正有价值的是把功率数据和协议状态在时间轴上对齐。我的做法是在固件里维护一个微秒级的时间戳计数器每当解析出一个PD报文或者检测到一次QC电平跳变就把当前时间戳连同事件类型一起写入环形缓冲区。同时INA226的采样结果也打上同一个时间戳。这个对齐的价值在于你能在波形图上看到某个PD请求报文发出之后电压和电流是如何逐步爬升到目标值的或者在某个瞬间电压发生跌落的同时是否出现了新的PD协商报文。没有时间戳对齐你只能拿到一段杂乱无章的日志很难定位问题。我第一次做数据合并时因为时间戳单位不一致导致曲线和事件错位了大概200毫秒排查了整整一个晚上才找到原因。4. 上位机与数据展示设计4.1 串口数据帧格式与通信协议固件解析出的数据最终要通过串口发给上位机。串口协议我使用了简单的帧格式帧头0xAA 0x55帧类型数据长度数据体最后加上CRC8校验。数据体的内容包括事件类型、时间戳、PD报文的关键字段值或QC状态值以及当前的电压、电流、功率读数。数据帧格式示例字段长度字节说明帧头20xAA 0x55帧类型10x01表示PD事件0x02表示QC事件0x03表示功率数据数据长度1数据体长度时间戳4毫秒时间戳数据体可变协议事件内容或功率读数CRC81对前面所有字节做CRC8校验这里我特别强调一下波特率。有些朋友用9600波特率结果发现日志丢失严重这个不奇怪。事件爆发时候PD协商一条报文可能包含多个消息每个消息都要输出几十字节加上功率数据的周期刷新整体数据量并不小。我实测过正常协商过程每秒产生的数据量大约是2到3KB。如果还要记录完整的电压电流曲线每秒至少5到10KB。所以我直接把波特率定在921600保证数据不丢。串口不够快的话也可以改用USB虚拟串口但那样驱动写起来更麻烦。4.2 实时波形显示与日志回放上位机我用Python写的串口库用pyserial绘图库用matplotlib。首次实现因为matplotlib刷新整个figureCPU占用率很高实时性很差。后来改成只更新数据曲线尾部把历史数据保存在循环缓冲区里性能才好转。如果你的数据量不大也可以用现成的串口示波器软件比如SerialPlot它能直接解析串口数据并画多条曲线。日志回放功能看起来不起眼实际使用中价值巨大。我写了一个简单的CSV日志记录器每条记录包含时间戳、事件类型、当前电压、当前电流、当前功率、PD状态、QC状态。测试结束后把CSV导入分析脚本我可以随便放大任意时间段对照PD事件和功率曲线找出充电过程的所有转折点。这里也说明一下上位机代码不复杂关键是要把数据结构定义好。我自己用的简化数据格式是用逗号分隔的文本虽然比二进制协议浪费一些带宽但调试方便能在串口助手里直接肉眼读。你要做产品化的话再用二进制协议提升效率。5. 实测案例与常见问题排查实录5.1 实测案例一65W充电头为什么只能跑45W我用一个标称65W的氮化镓充电头给笔记本充电插上分析仪后记录到了完整的协商过程。PDO广播里确实包含20V/3.25A这一档也就是65W能力。笔记本也正确请求了这档。但继续看后面的事件日志我发现协商成功之后实际功率一直停留在45W左右电流被限制在2.25A。这时候我换了一根线功率立刻跳到60W以上。再对比日志发现原来那根线在PD协商阶段返回了E-mark芯片信息明确写着线缆最大电流只有3A但实际传输中因为线缆内阻太大电压跌落导致实际功率上不去。这个例子说明协议分析仪抓到的不仅是对错还有握手中的所有细节线缆能力是PD协商里很容易忽略的一环。5.2 实测案例二QC3.0设备握手失败另一个案例是我修一个老款支持QC3.0的手机充电器换上电池之后发现手机只能慢充。分析仪显示D/D-电压一直停留在初始状态完全没有进入QC协商流程。检查后发现这个充电头在插入设备时会先短暂地短接D和D-用来探测是不是一个“普通USB设备老式握手”。如果D和D-之间的阻抗电路被破坏它就不会进入QC模式。而我在分析仪的D/D-模拟开关选型时使用了一颗导通电阻偏大的开关导致D和D-之间的等效回路电阻变大适配器认为这不是一个合法的QC设备拒绝握手。换成导通电阻只有几欧姆的模拟开关之后问题解决。这个坑总结成一句话监听电路不能改变被监听链路的电气特性。你加在信号线上的任何器件都会对原有通信造成影响选型时要注意寄生参数。5.3 常见问题速查表现象可能原因排查思路解决办法插上分析仪后没有任何PD报文分析仪未正确下拉CC线检查CC1/CC2是否接了下拉电阻阻值是否接近5.1kΩ补充或更换下拉电阻能收到报文但CRC校验一直失败采样率不足或信号整形不好用示波器观察CC线波形检查比较器阈值是否合适提高定时器采样频率或调整比较器阈值电压电流读数明显偏大或偏小分流电阻标称值和实际值不一致万用表实测分流电阻重新校准INA226校准寄存器功率曲线和协议事件时间对不上时间戳单位不一致检查固件里时间戳计数器的精度统一为毫秒或微秒时间戳QC握手经常失败D/D-模拟开关导通电阻过大测量开关导通电阻换用低导通电阻的模拟开关大电流下VBUS压降明显分析仪串联链路PCB铜箔太细观察负载下VBUS电压跌落加粗铜皮必要时增加过孔数量5.4 校准与避坑的几点经验电流采样必须校准。INA226本身精度不错但分流电阻的温漂、焊接引脚的接触电阻都会带来误差。我的做法是先用电子负载设定一个精确的1A电流读取INA226的电流寄存器值然后用这个实测值反推校准系数。没有电子负载的话找一根已知阻值的功率电阻加在5V上也可以前提是电阻的功率余量足够。另外插拔顺序也有讲究。我先给分析仪通电等主控初始化完成后再插被测充电器避免上电瞬间CC线上的电流浪涌烧坏引脚。插拔的时候动作要干净不能慢慢蹭否则会有打火和瞬态尖峰轻则干扰数据重则损坏电路。最后再说一个小技巧调试PD解析时不要直接就上真实充电器。先用一个PD诱骗器或者另一个能独立控制CC电平的开发板让被测充电器反复发送Source_Capabilities报文然后用分析仪抓这个特定报文。这样你可以在一个非常可控的环境下验证解码逻辑比直接抓真实手机和充电头的通信要容易得多。这段时间做下来我对快充协议的理解比看一百遍文档都深。每次实测都会发现新的问题也逼着自己去查更多资料。如果你也想做这个项目我建议第一版尽量简化只要能解析PD广播报文和QC状态、显示电压电流功率就够了。后续还可以扩展UFCS融合快充支持、蓝牙无线通信版本、或者把PD诱骗和分析功能合到一起做成一个小工具箱。硬件和固件框架搭好之后这些方向都是顺着原来设计一步步延伸出来的。
返回列表