USB-C PD分析仪与可编程Sink:协议解析与调试实践

发布时间:2026/8/28 23:53:52
USB-C PD分析仪与可编程Sink:协议解析与调试实践 USB-C Power Delivery 分析仪并不只是一个抓包工具。它通常由带 PD 物理层芯片的 MCU 板卡、串口或 USB 通信链路和 PC 端解析工具构成既能把 CC 线上的 BMC 信号解析成控制报文和数据报文也能作为可编程 sink 主动向电源适配器请求指定电压和电流。这个组合让同一块设备既能用于协议学习又能用于适配器兼容性测试。这篇文章以开源 USB-C Power Delivery 分析仪和可编程 sink 的通用实现为线索讲清楚它抓到的报文长什么样、sink 请求如何构造、烧录和接线后怎么验证以及最常见的一批调试问题。1. 先理解 USB-C Power Delivery 分析仪和可编程 sink 的分工拿到这类开源项目时第一件事不是急着接线而是先弄清楚“分析仪”和“可编程 sink”到底分别在链路里扮演什么角色。两者都围绕同一个 PD 物理层展开但做的事情完全不同。1.1 分析仪解决什么问题普通万用表只能看到 VBUS 的电压电流变化示波器能看到波形但都看不到 CC 线上的协议内容。PD 分析仪要解决的核心问题是在适配器和受电设备协商时把 CC1/CC2 线上经过 BMC 编码的二进制流恢复出来再按 USB PD 规范解析成一条条可读报文。分析仪通常被串接在 Source 和 Sink 之间或者并联在 CC 线上监听。对于学习 PD 的人来说最直观的收益是能看到Source_Capabilities里到底声明了哪些电压电流档位以及 Sink 发出Request后Source 是返回了Accept还是Reject。这些信息直接决定了为什么某些充电器不输出 20V为什么快充协议不兼容为什么会发生电压回退。与逻辑分析仪相比PD 分析仪更贴近协议层。逻辑分析仪抓的是原始电平需要自己按 BMC 时序解码PD 分析仪一般已经在固件里完成了物理层解码和字节流组帧PC 端只需要处理报文语义。这也是它更适合开发者和测试人员的原因。1.2 可编程 sink 解决什么问题可编程 sink 的角色是主动扮演一个受电设备。普通 Type-C 设备插上充电器后由设备里的协议栈决定申请哪个电压档位可编程 sink 则把这个决定权开放出来让你通过串口、命令行或上位机指定“请输出 20V 5A”。它和简单 PD 诱骗器的主要区别在于灵活性。早期诱骗器常用固定电阻或按键切换电压只能支持很少几个档位可编程 sink 则解析完 PDO 列表后可以根据用户规则动态构造Request报文。因此它非常适合测试充电器是否真的支持某个 PDO、验证电子负载带载能力、或者给一块电源板做自动化回归。注意可编程 sink 本身并不消耗大功率它只是把协议协商过程完成。之后如果要测试带载能力需要外接电子负载或其他耗电设备。不要把可编程 sink 和高功率充电器直接短路当成“带载测试”。1.3 两种功能合在一起的典型场景把分析仪和可编程 sink 放在同一个开源项目里最常见的测试链路是PD 适配器 - Type-C 线缆 - 分析仪/可编程 sink 板 - 电子负载或被测试备调试时先以“监听模式”运行抓取适配器发出的首包和协商过程确认 PDO 列表后切换到“可编程 sink 模式”申请目标电压再用电子负载逐步加大电流观察 VBUS 是否有跌落、报文是否出现重传。如果是开发自己的 PD Sink 固件分析仪则可以并接在旁边用来对比“正常请求”和“自定义请求”的报文差异。很多棘手的兼容性问题比如适配器只接受某种 RDO 排列、MessageID 回绕导致重传、接上 E-Marker 线缆后出现 SOP’ 报文都能通过分析仪日志定位到具体报文序。2. 协议基础PD 协商链路、报文类型和关键字段要使用分析仪和可编程 sink必须先建立一套共同的协议语言。USB-C PD 协议分物理层、协议层和策略引擎。分析仪主要在协议层工作但很多异常现象要从物理层解释。2.1 CC 线、BMC 和 SOP 是你最先会遇到的名词USB-C 接口里的 CC1/CC2 引脚承担两个任务一是通过上拉电阻 Rp 或下拉电阻 Rd 检测设备插入方向二是在连接建立后承载 PD 通信。PD 物理层使用的是 BMC 编码全称是双向标记编码。它和常见的 UART 不同BMC 用时钟和数据的相对关系表达比特对信号上升下降沿非常敏感。PD 通信中的最小寻址单位叫 SOP。不同 SOP 类型对应不同通信对象SOP 类型通信对象典型用途SOPSource 与 Sink 之间主协商报文SOP’与线缆 E-Marker 通信读取线缆能力、电流等级SOP’’与插头或插座内的芯片通信可选的应用场景分析仪如果只解析 SOP通常会忽略 E-Marker 相关报文如果项目支持 SOP’ 解析就能看到线缆协商过程这对验证 5A 线缆和雷电/USB4 线缆特别有用。2.2 一次完整的 Source 到 Sink 协商流程一个最简单的 PD 协商过程可以按下面顺序理解Source 检测到 Sink 接入通过 Rp/Rd 完成连接检测。Source 向 Sink 发送Source_Capabilities携带它支持的 PDO 列表。Sink 读取 PDO 列表选择其中一个档位构造并发送Request报文。Source 返回Accept表示接受请求。Source 调整 VBUS 电压完成后发送PS_RDY。Sink 检测到PS_RDY开始从新的电压档位取电。分析仪在串口日志里看到的正是这类时序。如果缺少PS_RDY说明 Source 可能仍在调整电压如果收到Reject说明请求的 PDO 不被接受。2.3 关键报文类型与字段USB PD 报文分两类控制报文和数据报文。控制报文没有 Data Object数据报文携带一个或多个 32 位数据对象。类别报文名称作用控制报文GoodCRC链路层确认收到有效报文后回复控制报文Accept接受 Sink/ Source 的请求控制报文Reject拒绝请求控制报文Wait暂时无法处理提示重试控制报文PS_RDY电源准备好电压已切换完成控制报文Get_Source_Cap请求 Source 重新发送能力包数据报文Source_CapabilitiesSource 的能力列表包含 PDO数据报文RequestSink 的请求报文包含 RDO数据报文Sink_CapabilitiesSink 的能力列表数据报文BIST测试模式报文每个报文都有一个 Message Header里面带有报文类型、MessageID 和数据对象数量。MessageID 是关键的反重传机制接收方如果收到重复 MessageID通常会回复 GoodCRC 但不会重复处理。调试时如果发现报文不断重传先看 MessageID 是不是没有递增。3. 搭建项目和硬件环境这类开源项目的硬件核心并不复杂但环境准备直接影响后面所有调试。建议先按“可以跑通的最小集合”搭建再逐步加入保护、隔离和自动化功能。3.1 硬件组成参考以常见的开源实现为例硬件链路由以下模块组成模块作用选型提示MCU运行协议栈和用户逻辑常见选择如 STM32、RP2040、ESP32-S3PD 物理层芯片负责 BMC 编解码、SOP 识别、CRC 校验独立 PD PHY或 MCU 内置 PHYType-C 连接器接入 Source 和 Sink按项目设计选 Type-C 母座或公头串口/USB 转接把日志和命令传到 PCCP2102、CH340 或 MCU 原生 USB电源保护保护后级电路TVS、防反接、限流电路不要小看电源保护。分析仪直接接触 VBUS20V 甚至更高的电压在调试中很常见。如果板子没有保护一次接错线就可能烧掉 PD PHY 或 MCU。学习阶段也要在桌面准备一个隔离或限流环境。3.2 固件与 PC 工具准备大部分开源项目会提供固件和 PC 端解析脚本。先安装基础工具链再按照仓库 README 构建固件。常见软件需求Git用于拉取源码。Python 3用于运行上位机解析脚本。pyserial用于串口通信。对应 MCU 的编译工具链或构建系统例如 CMake、PlatformIO、Arduino CLI。一个串口终端工具例如 minicom、PuTTY 或 VS Code 串口插件。git clone 项目仓库地址 cd 项目目录 pip install pyserial这里不指定具体仓库地址因为你拿到的开源项目可能使用不同目录结构。关键是先找到firmware/和pc_tools/这类约定目录理解固件输出什么格式PC 端读取什么协议。固件烧录命令也依赖具体芯片。以常见 STM32 为例# 假设项目提供 openocd 或 st-flash 脚本 make -C firmware st-flash write firmware/build/firmware.bin 0x8000000如果项目使用 PlatformIO可以直接pio run -t upload。烧录后打开串口终端确认能看到启动日志。3.3 接线和上电检查清单接线前先列一张检查清单避免把问题混在一起检查项预期结果如果不对怎么办板卡 USB 转串口能否被 PC 识别设备管理器或 lsusb 能看到换线、装驱动、检查板卡供电串口波特率是否匹配日志无乱码确认固件配置的波特率PD 适配器是否连接正确CC 线路被正确连接检查 Type-C 插座和线缆方向VBUS 电压是否在预期范围内万用表测量安全不要直接短接先断开负载检查固件是否进入监听或 sink 模式日志有状态切换信息查看命令行或按键配置注意上电前先把可编程 sink 的默认请求设置为安全档位例如 5V。不要在上电瞬间就请求 20V否则一旦 RDO 配置错误可能直接带高压进入不稳定状态。4. 实现一个最小可运行的 PC 端报文解析器开源项目通常自带解析工具但自己实现一个最小解析器是理解 PD 报文结构最有效的方式。下面用 Python 写一个通用示例只做“接收串口日志 - 提取报文 - 解析消息类型和能力档位”三件事。4.1 从串口读取原始日志假设固件输出每一帧时以RAW:开头后面是十六进制字节。不同的开源项目格式可能不同但思路一致先读行再过滤有效数据。import serial ser serial.Serial( portCOM23, # Linux 下可以使用 /dev/ttyUSB0 baudrate115200, timeout0.5 ) while True: line ser.readline().decode(utf-8, errorsreplace).strip() if line.startswith(RAW:): print(line)这段代码只是原始日志回显。真正使用时需要把RAW:后面的十六进制字符串转成字节数组再送到解析函数。4.2 解析 Message Header 与控制/数据报文这里使用简化帧格式第一个字节表示 SOP 类型0x02表示 SOP0x03表示 SOP’0x04表示 SOP’’接下来是 Message Header 和 Data Object。具体位定义在不同固件中可能已经做过归一化因此下面的解析函数只用于说明思路。from struct import unpack CONTROL_TYPES { 1: GoodCRC, 2: GotoMin, 3: Accept, 4: Reject, 5: PS_RDY, 6: Get_Source_Cap, 7: Get_Sink_Cap, } DATA_TYPES { 1: Source_Capabilities, 2: Request, 3: BIST, 4: Sink_Capabilities, 15: Vendor_Defined, } def parse_raw_frame(hex_str: str): data bytes.fromhex(hex_str) if len(data) 3: return None sop data[0] # 简化的 16 位 Message Header。 msg_header unpack(H, data[1:3])[0] msg_type msg_header 0x1F is_data (msg_header 5) 0x01 if is_data: name DATA_TYPES.get(msg_type, fUnknownData_{msg_type}) else: name CONTROL_TYPES.get(msg_type, fUnknownCtrl_{msg_type}) return { sop: sop, type: name, msg_header_raw: hex(msg_header), payload: data[3:], }实际 PD 报文中的 Message Header 位数和这里不完全一致但这类简化逻辑已经能帮你在 PC 端完成第一层分类。如果你的固件输出已经解析好类型字段可以直接跳过这一层。4.3 解析 Source Capabilities 中的 PDO 和 Request 中的 RDO当识别到Source_Capabilities时payload 里的每个 4 字节就是 PDO。以最常见的 Fixed PDO 为例电压原始值单位是 50mV电流原始值单位是 10mA。def parse_pdo(raw: bytes): pdo unpack(I, raw)[0] pdo_type (pdo 30) 0x03 if pdo_type 0: # Fixed Supply voltage_50mv (pdo 10) 0x3FF current_10ma (pdo 20) 0x3F return { type: Fixed, voltage_mv: voltage_50mv * 50, max_current_ma: current_10ma * 10, } return {type: fPDO_{pdo_type}, raw: hex(pdo)}Request报文里的 RDO 字段要关注三个关键位置目标 PDO 位置、操作电流、最大电流。位置从 1 开始当前分析仪的日志会显示Request obj_pos4这就表示 Sink 希望使用第 4 个 PDO。5. 使用可编程 sink 定制请求电压 / 电流解析出适配器的能力列表后可编程 sink 的价值开始显现你可以不依赖某个固定诱骗器主动选择任一个受支持的档位。5.1 sink 端请求参数设计在实际项目里sink 请求参数通常通过命令行或 JSON 配置传入。设计上至少需要这几个字段参数含义示例pdo_position选择第几个 PDO4voltage_mv目标电压单位毫伏20000max_current_ma最大工作电流单位毫安5000operating_current_ma操作电流单位毫安4000电压和电流都需要与 PDO 中声明的范围匹配。不要请求适配器没有声明的电压档位否则会收到Reject或Wait。5.2 构造并发送 Request 报文很多开源实现会通过串口向固件发送结构化命令固件再生成Request报文。PC 端可以用脚本控制python sink_control.py --pdo 4 --voltage 20000 --max-current 5000 --op-current 4000固件侧收到命令后会构造一个简化 RDOdef build_rdo(pdo_pos: int, op_current_ma: int, max_current_ma: int) - int: # 这是简化示例实际字段位置以 PD 规范和固件实现为准。 rdo 0 rdo | (pdo_pos 0x7) 28 rdo | ((op_current_ma // 10) 0x3FF) 10 rdo | ((max_current_ma // 10) 0x3FF) 20 return rdo发送后固件会把 RDO 放在Request数据报文里并等待 Source 的Accept。如果 Source 回复Reject需要回到能力列表重新选择 PDO。5.3 与真实 PD 电源适配器联调联调时建议按以下步骤操作用 5V/3A 这类安全档位先跑通一次完整协商。切换到监听模式抓下适配器的Source_Capabilities。根据能力列表配置目标电压。再次进入 sink 模式观察日志中是否出现Accept和PS_RDY。确认 VBUS 达到目标电压后再接电子负载逐步加电流。不要一开始就把电子负载设成 5A 再请求 20V。先把协议跑通再考虑功率测试。6. 运行验证与结果分析跑通一次协商后不能只看到“有日志”就认为成功。需要按阶段验证每个关键报文是否出现、VBUS 是否进入预期状态。6.1 验证 sink 请求是否成功阶段预期报文说明连接建立GoodCRC链路层已经工作能力获取Source_CapabilitiesSource 已经声明能力列表请求发送RequestSink 已经选中 PDO请求确认AcceptSource 接受请求状态切换PS_RDYSource 已完成 VBUS 切换如果只看到GoodCRC和Source_Capabilities后续没有Request问题通常出在固件状态机没有收到配置。如果看到Request后有Reject说明 RDO 字段或 PDO 选择有问题。6.2 抓包日志解读假设串口日志如下SOP Source_Capabilities num_objects5 PDO[0] Fixed 5000mV/3000mA PDO[1] Fixed 9000mV/3000mA PDO[2] Fixed 15000mV/3000mA PDO[3] Fixed 20000mV/5000mA SOP Request obj_pos4 op_current5000mA SOP Accept SOP PS_RDY这个日志说明适配器声明了 5V、9V、15V、20V 档位sink 选择了第 4 个 PDO适配器接受并输出 20V。VBus 电压达到稳定后可以开始带载测试。如果日志中出现大量重传SOP Request msg_id2 crc00000001 SOP GoodCRC msg_id2 SOP Request msg_id2 crc00000001说明 Source 已经回复了 GoodCRC但 Sink 没有正确进入下一状态导致 Sink 重发相同 MessageID。重点检查 Sink 侧的协议状态机是否把 GoodCRC 和 Accept 的处理顺序分清楚。6.3 常见误判和抓包陷阱抓包调试时容易产生三类误判第一类是把“收到 GoodCRC”误认为“协商成功”。GoodCRC 只代表链路层确认不代表 Source 接受请求真正的接受标志是Accept。第二类是忽略 SOP’/SOP’’。带 E-Marker 的线缆插入后Source 可能先与 E-Marker 通信再开始主协商。如果分析仪没有抓 SOP’日志里会出现一段空白。不要急着判定为协议异常。第三类是误以为电压档位一定按 PDO 顺序排列。PDO 顺序由适配器固件决定有些适配器会插一个 5V 特殊档位正确选择方式是通过 PDO 的电压电流值匹配而不是按固定位置猜。7. 常见问题排查下面按常见现象、可能原因、检查方式和解决方案展开适合直接对照定位。7.1 收不到报文或串口无输出如果分析仪上电后没有任何串口日志先确认固件是否已经运行。现象可能原因检查方式解决方案串口无法打开驱动未安装或串口被占用查看设备管理器或 lsusb安装驱动关闭占用串口的终端有启动日志但插入适配器后无报文CC 线未接好检查 Type-C 插座方向、飞线确认 CC1/CC2 已接入 PD PHY波特率乱码波特率不匹配查看固件配置改为固件实际波特率插入适配器后板卡重启VBUS 倒灌或供电不足测量 VBUS 对地电压断开外部负载使用独立供电7.2 CRC 错误或 BMC 解码失败BMC 解码错误通常发生在物理层信号质量差时。常见原因包括地线没有接好、CC 线接触不良、线缆太长、PD PHY 电源噪声过大。现象可能原因检查方式解决方案大量 CRC Error信号电平异常示波器看 CC 波形检查地线、缩短线缆、改善电源偶发错误接触不良重新插拔线缆换成短 Type-C 线或检查连接器虚焊某一类 SOP 全部错误SOP 类型过滤错误检查固件配置确认 PHY 是否配置了对应 SOP 帧如果项目固件支持日志级别调整可以把物理层诊断打开。这样能区分是“没收到帧”还是“收到帧但 CRC 错误”。7.3 请求电压失败请求电压失败是 sink 调试中最常见的问题现象通常是Request之后收到Reject或Wait。现象可能原因检查方式解决方案Reject请求了 PDO 列表之外的档位打印 Source_Capabilities重新选择正确 PDORejectPDO 位置错位核对 RDO 中 obj_pos从 1 开始计数WaitSource 正在处理其他任务查看是否收到 Wait延时后重发请求无响应MessageID 不递增观察日志修复 MessageID 递增逻辑要特别注意RDO 中的 PDO 位置是一个 3 位字段从 1 开始不是从 0 开始。很多第一次写 sink 逻辑的开发者容易在这里失误。7.4 板子发热或 VBUS 跌落如果板子正常协商到 20V但一接负载 VBUS 跌落问题多半不在分析仪而在电源链路。应对策略确认适配器最大电流是否满足负载需求。确认线缆和连接器是否支持对应电流特别是 5A 线缆需要 E-Marker 或有正确 Ra 标识。使用电子负载时先以 0.5A 步进逐步增加观察 VBUS。如果板子上的走线本身不承载大电流不要把功率电流直接流过分析仪板卡。注意大电流测试需要专门的负载和足够粗的连接线。分析仪板卡更擅长抓协议不适合长期作为功率传输通道持续满载运行。8. 最佳实践与扩展方向把协议跑通只是开始。要让这个开源设备真正服务于项目还需要考虑工程化、安全性和扩展能力。8.1 学习环境和生产环境的工程设计差异设计点学习环境生产/长期测试环境供电USB 供电即可独立隔离供电避免 VBUS 干扰日志串口终端打印落盘为文本按时间戳存储保护基本 TVS加入过压、过流、防反接和限流固件手动烧录固件版本记录支持远程升级监控人工看日志配合电子负载、万用表和视频监控长时间抓包时PC 端脚本需要处理串口缓冲溢出。建议把日志按帧写入本地文件而不是只打印到终端如果日志量很大还要给每条报文加时间戳方便后续分析时序。8.2 可复用调试清单每次开始调试前可以按下面清单过一遍确认固件版本和 PC 工具版本匹配。确认串口端口和波特率正确。用安全档位先跑通一次完整协商。抓一次Source_Capabilities核对 PDO 数量。检查 RDO 的 PDO 位置是否从 1 开始。确认Accept与PS_RDY是否都出现。切换大电压前先用万用表测量 VBUS。带载前先确认线缆电流等级。记录异常报文的原始十六进制而不是只记录解析后文本。这套清单看起来简单但大部分调试时间都浪费在这几项上。8.3 扩展方向PPS、E-Marker 和自动化本项目如果继续扩展优先级可以从三个方向考虑首先是 PPS 支持。PPS 用 APDO 描述连续可调电压和电流对手机直充和电池充电策略非常关键。解析器需要增加 APDO 类型sink 端需要支持在 PPS 范围内动态调整电压。其次是 E-Marker 支持。增加 SOP’ 报文解析后可以读取线缆的电流能力和 USB4 能力在线缆验证和兼容性测试中很有价值。最后是自动化回归。把分析仪和可编程 sink 接到一台带有自动流程的 PC 上可以批量扫描不同适配器的 PDO 列表自动生成兼容性报告。配合电子负载还能形成“协议协商 带载能力 降压测试”的完整测试脚本。对这个项目来说最值得持续投入的部分是固件状态机和 PC 端日志分析。固件状态机越贴近 PD 规范能覆盖的异常场景越多PC 端越擅长把原始帧转成可读报表整个工具就越接近一台正经的 PD 协议分析仪。建议在完成最小闭环后优先补充错误日志和极简的时序分析功能这会让你在真实项目中节省大量排错时间。