手里正好在做一台需要“全电压域”测试的PD受电设备,从5V一直诱骗到20V,再折腾PPS可编程电源档位,绕不开一颗芯片:CH224A/Q。这颗来自WCH的USB PD诱骗芯片,物理层和协议层都帮你啃干净了,但很多人只是把它当成一个“把充电器电压拉出来”的黑盒子,IIC读完寄存器就给完事。实际上,IIC上能读到的状态、配置、检测到的VBUS电压,和CC线上真正在跑的Source Capabilities报文是严格对应的。搞懂这层对应关系,你才算是真正会用这颗芯片。
这篇文章就拿CH224A/Q当主线,把从IIC寄存器里挖数据到CC线上Source Capabilities报文解析的完整链条走一遍。内容包括PD报文头的逐bit拆解、Fixed PDO和PPS APDO的编码规则、用C代码解析PDO的实操示例,以及我踩过的几个坑。适合正在做PD诱骗、电源测试、或者自己捣鼓USB PD供电方案的工程师和DIY玩家。
1. 从IIC数据到快充协议:为什么要死磕SRC Cap报文
1.1 CH224A/Q到底是个什么角色
CH224A/Q本质上是一个USB PD的Sink端控制器,也就是受电端设备里的“谈判代表”。充电器作为Source端,上电后会往CC线上广播自己的能力,这份广播报文就是Source Capabilities,简称SRC Cap。CH224A/Q收到这份报文后,根据内部配置(或者外部IIC写入的请求),从中挑一个合适的电压电流档位,再发Request报文回去,完成一次PD协商。
这里的关键点在于:CH224A/Q并没有把原始PD报文缓存在IIC寄存器里给你看。它做完了物理层BMC解码、协议层CRC校验、PDO筛选,最终只把结果状态量暴露给主机。所以当你说“从IIC数据到快充协议”的时候,实际上是一条双向链路:CC线上的SRC Cap报文是源头,IIC寄存器里的状态和配置是结果。你想完整体验这条链路,两头都得会看。
只看IIC的人,经常会遇到一个困惑:为什么同一个充电器,插上后输出的电压档位和我预期的不一样?原因就在于你没看SRC Cap。充电器宣告的PDO集合决定了它能给什么,CH224A/Q的配置决定了它挑了哪个,缺一头都没法解释最终现象。
1.2 解析SRC Cap报文能解决的实际问题
我总结下来,真正需要去抠SRC Cap报文的场景至少有三种。
第一种是兼容性调试。你做的设备插到某个充电器上,诱骗不出20V,或者只能出5V,这时候你必须搞清楚充电器到底广播了哪些PDO。是充电器本身不支持20V,还是CH224A/Q的配置没对上?没有SRC Cap的解析能力,你只能靠猜。
第二种是PPS调试。支持PPS的充电器会在SRC Cap里带APDO,这类PDO的电压电流是连续可调的,而不是固定档。如果你想做锂电池充电、或者需要精确电压的测试设备,必须能从SRC Cap里解析出PPS的最小电压、最大电压和最大电流,才能正确发起请求。
第三种是产品验证。如果你的设备用CH224A/Q诱骗供电,量产前可能要验证它在不同PDO下的表现。这时候抓一次SRC Cap报文,确认档位选择逻辑是否符合预期,比反复插拔充电器要高效得多。
2. 前置基础:IIC怎么从CH224A/Q里把数据挖出来
2.1 CH224A/Q的IIC角色和地址怎么定
CH224A/Q在IIC总线上是标准从机角色,主机用MCU来读它。这个设计很实用,因为主控可以随时查询芯片当前状态,而不需要额外占用IO口去模拟PD握手。IIC的7位地址由ADDR0和ADDR1两个引脚的电平组合决定,常见的默认配置下地址是0x48,换算成8位写地址就是0x90,读地址是0x91。如果你在板子上把这两个引脚都拉低,那就是这个地址。
有的板子会把地址配成0x49、0x4A、0x4B,具体取决于ADDR0/ADDR1的上下拉。这里有个容易踩的坑:你搜到的例程里写着0x48,但你手上的模块把ADDR引脚接到了其他地方,导致IIC通信一直不成功。所以拿到模块或者自己画板子时,第一件事就是确认这两个引脚的接法,而不是直接抄代码里的地址。
IIC速率建议走100kHz或者400kHz即可。CH224A/Q内部对IIC时序要求并不苛刻,普通MCU的硬件IIC和软件模拟IIC都能稳定工作。我个人习惯用硬件IIC,省CPU,但如果你只是调试期间偶尔读一次状态,软件模拟反而更灵活,能快速换引脚。
2.2 寄存器读取流程与最小主机代码
CH224A/Q的寄存器读取遵循标准IIC读寄存器流程:先发设备地址加写位,再发目标寄存器偏移地址,然后重新发设备地址加读位,连续读取数据。一个最简的读取流程大概是这样:
// 伪代码:读取CH224A/Q指定寄存器的值 uint8_t ch224_read_reg(uint8_t reg_addr) { uint8_t val = 0; iic_start(); iic_send_byte(0x90); // 7位地址0x48 + 写位 iic_wait_ack(); iic_send_byte(reg_addr); // 目标寄存器偏移 iic_wait_ack(); iic_start(); iic_send_byte(0x91); // 7位地址0x48 + 读位 iic_wait_ack(); val = iic_read_byte(); iic_send_nack(); iic_stop(); return val; }读回来之后,关键是把原始数据映射到物理意义上。比如电压检测寄存器读回的值,通常需要经过一个比例换算才是真实的VBUS电压,这个换算系数必须在CH224A/Q的数据手册里查,不同固件版本可能不一样。我在调试时就遇到过读回数值一直偏大的情况,最后发现是手册版本没看对,换了换算系数就好了。
还有一点要注意:CH224A/Q内部某些寄存器是只读状态位,有些是只写配置位,读写属性不一样。你在往配置寄存器里写数据之前,最好先读一遍,掌握当前值,再用“读-改-写”的方式操作,避免把其他bit覆盖掉。
2.3 拿到的裸数据怎么对应到状态量
IIC读回来的裸数据,最有价值的是三类:配置信息、状态信息、ADC检测结果。
配置信息决定了芯片会怎么响应SRC Cap。比如目标电压、是否启用PPS、是否允许降级到5V等,这些配置会被芯片拿去和SRC Cap里的PDO做匹配。状态信息则告诉你当前协商结果,比如当前输出电压档位、协商是否完成、是否发生错误。ADC检测结果能反映VBUS实际电压,这个对验证PDO请求是否真的生效很关键。
举个实际例子:你给CH224A/Q配置了请求20V,IIC读到的状态也显示协商成功,但VBUS ADC读回来的电压只有5.2V。这时候你就该怀疑,是不是充电器的SRC Cap里根本没有20V的PDO,或者那个PDO的电流能力不满足你的配置需求。看IIC数据能发现问题,但定位问题还得回到SRC Cap报文本身。
3. Source Capabilities报文的“解剖学”
3.1 PD通信里SRC Cap报文的位置
USB PD通信发生在CC线上,物理层用的是BMC编码,比普通UART要复杂一些。充电器上电后会周期性地发送SRC Cap报文,直到收到Sink端的Request报文为止。整个PD协商的顺序是:Source发SRC Cap,Sink回Request,Source回Accept和PS_RDY,之后Source才正式拉高电压。
SRC Cap报文是Data Message类型,报文头里的Message Type字段指向的正是“Source Capabilities”这个消息。一个完整的PD报文由前导码、SOP、报文头、Data Objects、CRC32、EOP组成。SRC Cap报文里的Data Objects就是PDO,每个PDO固定32bit,最多可以带7个。也就是说,一颗充电器最多宣告7个供电能力档位。
很多人以为CC线上跑的数据可以直接用串口助手看,这是个误会。BMC编码下的信号不是标准TTL电平,需要用逻辑分析仪配合解码工具,或者专门的PD分析仪才能还原出报文内容。好在做SRC Cap解析时,我们真正需要关心的主要是报文头和PDO的bit含义,物理层解码工具搞定就行。
3.2 报文头和PDO结构逐位拆解
报文头是16bit,它承担了“这包是什么”的职责。Bit15到Bit12是消息类型,对于SRC Cap来说,这个值就是Source Capabilities的编码。Bit8到Bit6是消息ID,每发送一次新的报文,这个ID会递增,用来让对端区分新报文和重传。Bit4到Bit0是Data Object的数量,也就是PDO个数,对于SRC Cap报文,这个值至少是1,最多是7。
PDO才是真正承载供电能力的地方。最常见的Fixed PDO和PPS APDO,解析方法我直接给表:
| PDO类型 | 最高位[31] | 电压字段位置 | 电压单位 | 电流字段位置 | 电流单位 |
|---|---|---|---|---|---|
| Fixed Supply PDO | 1 | Bit19-Bit10 | 50mV/LSB | Bit9-Bit0 | 10mA/LSB |
| PPS APDO | 0 | Bit19-Bit15 | 100mV/LSB | Bit9-Bit6 | 50mA/LSB |
Fixed PDO的电压标称值可以理解成“这一段PDO只提供某个固定电压”,比如20V的PDO,电压字段读出来的十进制数乘以50mV就是20V。PPS APDO则不同,它的电压字段表示的是一个可调范围的上限,实际能输出的电压在最小值和最大值之间连续可调,这个“可调”的能力才是PPS的核心价值。
3.3 一条真实SRC Cap报文的解析演示
我拿一个常见的65W充电器来举例。假设从逻辑分析仪抓到的SRC Cap报文里,Data Objects有三个,分别对应三个PDO,按理应该翻译成9V/3A、12V/3A、20V/3.25A之类的档位。实际上从PDO原始值反推电压电流的算法非常简单:
// 解析Fixed PDO原始值 uint32_t pdo = 0x0002C0B0; // 举例:某个原始PDO值 int voltage_mv = ((pdo >> 10) & 0x3FF) * 50; // 单位mV int current_ma = (pdo & 0x3FF) * 10; // 单位mA printf("Fixed PDO: %d.%03dV / %d.%03dA\n", voltage_mv / 1000, voltage_mv % 1000, current_ma / 1000, current_ma % 1000);这段代码的核心逻辑就是按位取出电压字段和电流字段,再乘上各自对应的LSB权重。写成这样后,不管是解析哪颗充电器的SRC Cap,你只需要把PDO原始值喂进去就行。如果你抓到的是一条PPS的APDO,解析方法类似,只不过移位宽度和LSB不一样。
我在实际解析中还遇到过一个小陷阱:有些充电器宣告的PDO电压是5V、9V、12V、15V、20V五个固定档,但还有第六个PPS档。如果只解析前五个,你会漏掉PPS支持,导致诱骗时无法进入可调电压模式。所以写解析代码时,循环要遍历完报文中所有的Data Objects,一个PDO都别跳。
4. 把IIC数据和PD报文拼起来:实操流程
4.1 抓取CC线报文的准备工作
要看到真正的SRC Cap报文,你需要在CC线上做物理层采样。最省事的办法是买一个USB PD协议分析仪,插到充电器和CH224A/Q模块之间,它会把CC线上的BMC信号解码成报文,直接在PC软件里显示。如果没有专用分析仪,也可以用高采样率的逻辑分析仪,但需要自己写BMC解码逻辑,门槛高一些。
另一种思路是用示波器单次触发抓CC线上的波形,然后手动解码。这个方法我只在确认物理层时序时用,效率太低,不适合日常调试。实操中我更推荐直接上协议分析仪,几百块的投资能省下大量时间。抓包时注意把分析仪串接在CC线中,不要只夹在单端,否则可能影响PD通信。
抓包前,充电器最好先断电再上电,这样能捕获到完整的协商过程。因为SRC Cap只在充电器上电后广播一次,如果错过了就得重新拔插。我用逻辑分析仪时习惯把触发条件设为CC线上出现第一个下降沿,这样基本不会漏。
4.2 数据关联:IIC状态与SRC Cap PDO怎么对应
当我们同时拿到了IIC读出的状态和CC线上抓到的SRC Cap报文,下一步就是建立映射关系。举例来说,IIC里读到的“当前电压档位”是9V,你回头去看抓到的SRC Cap,确认里面确实存在一个9V/3A的Fixed PDO,这个对应就闭环了。
还有一种情况,IIC里能读到“当前请求的PDO索引”,这个索引对应的正是SRC Cap报文里的第几个PDO。我调试时最喜欢看这个字段,因为一眼就能判断芯片的档位选择逻辑是不是符合预期。明明是第三个PDO是20V,结果芯片偏偏选了第二个PDO的12V,这时候就要去看配置寄存器里的优先级设置了。
只盯着IIC数据不看SRC Cap,最容易出的问题是把“充电器没有这档电压”误判为“芯片配置错误”。反过来,只抓SRC Cap不看IIC,你又没法确认芯片到底选没选上。两条数据线一块看,才是完整的调试视角。
4.3 常见场景下的PPS与固定档位选择策略
做PD受电时,档位选择策略会直接影响设备的可靠性和效率。如果你的设备只是需要固定电压,比如12V给电机驱动、20V给Type-C扩展坞,那就用Fixed PDO就够了,配置CH224A/Q去匹配对应的固定档位,逻辑简单,兼容性最好。
如果你的设备需要在一定范围内调整电压,比如给电池做恒压恒流充电,那就得用PPS的APDO。PPS的好处是电压可以按100mV步进连续调整,而且电流限制也更精细。但要注意,PPS档位不是所有充电器都支持,而且有些充电器的PPS标称范围和实际能力有出入。我建议在设备固件里做降级逻辑:优先PPS,匹配不到PPS就退回Fixed PDO。
还有一个经验:解析SRC Cap时,最好把每个PDO的电压、电流、类型都打印出来,不要只打印电压。因为有些充电器会宣告一个20V/0.5A的奇怪PDO,如果你只看电压不看电流,请求之后可能直接触发过流保护。看清楚再选,稳得多。
5. 常见问题与排查技巧实录
5.1 IIC读出来的数据全是0xFF或0x00
这个现象十有八九是地址不对或者接线有问题。先用万用表确认ADDR0和ADDR1的电平,再对照手册确认IIC地址。如果你用的是软件模拟IIC,检查SCL和SDA引脚是否接反,以及有没有加上拉电阻。CH224A/Q的IIC引脚需要上拉,一般接4.7kΩ到3.3V,别偷懒省掉。
还有一种情况是供电没起来。CH224A/Q需要VCC供电正常后才响应IIC,如果VBUS还没建立,芯片可能处于未初始化状态,IIC读出来自然是异常值。我遇到过好几次,排查到最后发现是充电器没插好,VBUS根本没上来。
5.2 报文头PDO数量对不上
你抓到的SRC Cap报文里,报文头写的PDO数量是5,但后面实际解出来的有效PDO只有4个,或者某个PDO解出来电压是0V。这种情况通常是抓包丢数据了。CC线上的BMC信号对采样率要求不低,逻辑分析仪采样率不够容易丢bit,导致后续位错乱。
解决方法是提高采样率,至少保证每个BMC位有20个采样点以上。如果条件允许,换用专门的PD协议分析仪,能省很多事。另外,解析代码也要做健壮性校验:PDO个数超出报文头声明的范围时直接报错,不要强行继续解析。
5.3 诱骗电压和PDO宣告不一致
这是最让人抓狂的问题:SRC Cap里明明写了20V/3.25A,CH224A/Q也配置成要20V,但实际量VBUS就是只有12V。先排除一种简单情况:充电器有多路输出,你插的Type-C口可能和抓包的CC线不是同一个口。多口充电器每个口的PDO能力可能不同,你得确认供电口和分析仪的通道是同一物理链路。
如果链路没问题,那大概率是请求的电流能力超过了PDO宣告值。比如PDO宣告的是20V/2.25A,而你配置的请求电流是5A,芯片可能会拒绝这个请求,退回5V。CH224A/Q本身有降级机制,请求不匹配时它会选择一个更安全的档位。这时候我去查IIC里的协商失败原因寄存器,基本都能看到线索。
5.4 一台充电器反复插拔后PDO不固定
现在很多智能充电器会根据负载和线缆情况动态调整SRC Cap,尤其是带多口功率分配功能的充电器。你第一次插上时它宣告了65W的PDO,第二次插上时可能变成了45W。这种现象不是芯片问题,是充电器的策略在起作用。
遇到这种动态PDO,调试时不要指望IIC配置一次就永远生效。我现在的做法是在设备端做一个简单的PDO缓存,每次协商成功后把SRC Cap的关键信息存下来,下次上电先读取上次的记录做参考。这样即使充电器策略变了,也能快速发现差异。
5.5 检查清单与快速定位建议
我把调试流程整理成了一个固定顺序:先确认VBUS有没有电,再确认CC线上的物理层抓包是否干净,接着看SRC Cap报文内容是否合理,然后看IIC寄存器里芯片选择的档位,最后用万用表量VBUS实际电压。这五步按顺序走下来,基本能覆盖90%的CH224A/Q调问题场景。
如果IIC和抓包都正常但电压还是不对,最后一个排查手段是换一颗充电器对比测试。有时候就是某颗充电器对CH224A/Q的兼容性不好,换了就好。别在单颗充电器上死磕太久,浪费时间。
我自己的习惯是做一个简单的桌面工具,把IIC读取、PDO解析、报文抓取三样整合到一起。每次调试时,软件里同时显示SRC Cap的所有PDO和CH224A/Q当前状态,一眼就能看出问题在哪。磨刀不误砍柴工,这个工具做出来之后,后面测再多的充电器都轻松很多。