上周把一个 Type-C 双向供电原型板调通了,最折腾的地方反而不是 DC-DC,也不是功率路径,而是这个口到底该当“供电方”还是“受电方”。同一个 Type-C 口,插到电脑上要当 Sink,从电脑的 PD 口取电;插到充电器上又要反过来当 Source,给后面的设备送电。角色切来切去,协议芯片的身份识别和切换总是差点意思。折腾一圈之后,我最终选了最土但最稳的方案:用 LDR6500 的 IO 电平来控制主从切换。这篇文章就把这个落地思路完整拆开讲一遍,包括 Type-C 的身份究竟放在谁的引脚上、IO 电平是怎么左右这个身份的,以及从原理图到调试台会遇到哪些坑。适合正拿着 LDR6500 做 Type-C 取电、PD 诱骗、设备角色切换,或者准备画 Type-C 供电板的朋友。看完你应该能自己搭一个最小系统,并且知道怎么排查“插上没反应”“角色切不过去”这类问题。
1. Type-C 的身份不在协议里,而在 CC 引脚的两颗电阻上
1.1 身份协商的本质:Rp 和 Rd 一上一下
很多人第一次玩 Type-C 的时候,以为“这个口能不能供电、能不能被供电”是芯片通过复杂协议决定的,其实第一步恰恰是纯硬件行为。Type-C 的电源角色是靠 CC 引脚上的两颗电阻完成的:供电方(Source)在 CC 引脚上拉一颗电阻到自己的电源轨,这颗电阻叫 Rp;受电方(Sink)在 CC 引脚上下拉一颗 5.1kΩ 电阻到地,这颗电阻叫 Rd。
插上去的瞬间,Source 通过 CC 引脚读到的电压,就是电源轨经过 Rp 和 Rd 分压后的值。电压落在一个特定区间,Source 就认为“对面是个 Sink”,于是打开 VBUS;电压完全为 0,则认为线是空悬的。这个过程和串口检测设备在位很像,但更简单、更快。PD 协议本质上是叠加在这个电阻网络上的一层 BMC 编码通信,双方在 CC 线上协商电压、电流以及数据角色。换句话说,哪怕完全不跑 PD,Type-C 也可以只做“默认 5V 电源”,身份检测照样由电阻完成。
1.2 LDR6500 在身份体系里的位置
LDR6500 这种 PD 协议芯片,内部已经集成了 CC 检测比较器、协议解析逻辑和 Rd 网络。规格书里所谓的“CC1/CC2 检测”,指的就是它会判断 CC 线上有没有被对面的 Rp 拉起来。从角色上看,LDR6500 更偏 Sink:它检测到对面是 Source 之后,会主动发送 PD Request,请求把电压从 5V 提到 9V、12V 甚至 20V。很多 Type-C 诱骗线、电源触发板就是拿它做的,这也是它最常见的用法。
但标题里的“主从切换”,单靠 LDR6500 本身不够。LDR6500 决定的是“我作为受电端要多少电压”,而系统整体对外是 Source 还是 Sink,还要靠外部功率路径和一个 IO 电平来定。我在实际项目中用到的思路是:把 LDR6500 当作“非常会讨价还价的受电端”,由 IO 电平决定它是否参与工作;同时用另一路 MOSFET 控制 VBUS 的走向。这样一个小系统就能在“插上电脑取电”和“插上充电器给别人供电”之间切换。
1.3 电源角色和数据角色,别混为一谈
Type-C 的世界里其实有两套“主从”:电源主从(Source/Sink)和数据主从(Host/Device)。Rp/Rd 直接决定电源角色,但数据角色还要靠 USB 枚举。一个 Type-C 口可以同时是 Sink(正在充电),又同时是 Host(带着 U 盘),手机就是这样。
所以做“主从切换”之前,必须先把话说清楚:你要切的是电源身份,还是数据身份,还是两个一起切?LDR6500 只管电源侧。如果项目还需要切换 USB 数据通道,比如同一个口既要连电脑做 Device,又要接 U 盘做 Host,那就得在 D+/D- 上加模拟开关,由同一个 IO 电平或另一路 GPIO 控制。很多人在产品阶段翻车,就是没分清这两套主从关系,以为协议芯片把电源身份切好了,USB 枚举就自然正确,实际完全不是一回事。
2. IO 电平控制主从切换的硬件思路——为什么硬切比协议切更省心
2.1 为什么选 IO 电平而不是 I2C 寄存器
PD 协议本身有 PR_Swap 这种角色交换机制,可以让两个已经连接好的端口动态交换 Source/Sink 身份。问题在于,跑 PR_Swap 需要协议芯片支持、需要 MCU 维护协议状态、还需要处理各种对方不配合的情况,调试周期一下子拉长。
IO 电平方案完全不同。它是一个纯粹的物理信号:上电瞬间就定死,不需要初始化、不需要握手。在工业设备里,一个继电器触点、一个光耦隔离输入,甚至一个船型开关,都能控制身份。对很多固定场景来说——插到电脑上取电、插到充电器上供电——这种硬切足够用了,而且异常情况少。逻辑分析仪一看就知道当前 IO 是什么状态,排查问题非常直观。相比之下,I2C 配置地址、寄存器、时序,每一步都是潜在故障点。
2.2 IO 电平怎么左右身份:两条路
实际落地有两条路。
第一条,如果协议芯片本身支持双角色(DRP),IO 电平直接切换芯片内部的 Rp/Rd 状态。芯片自己完成 Source 侧或 Sink 侧的全部物理层逻辑,外部电路很干净。但这条路要求芯片选型时就选对,LDR6500 这种偏 Sink 的芯片不一定能直接这么用。
第二条,更常见也更现实的组合方案:LDR6500 负责 Sink 侧的 PD 协议,IO 电平控制外部功率路径和一个“Rd 接入开关”。当系统需要对外表现 Sink 身份时,IO 让 LDR6500 正常工作,CC 上的 Rd 接好,外部电源可以通过 VBUS 进来;当系统需要表现 Source 身份时,IO 先停止 LDR6500,同时把 CC 上的 Rd 断开,再把外部电源送到 Type-C 口。关键就在“断开 Rd”这一步,很多人的 Source 模式一开就短路,就是因为忘了 CC 上还挂着 5.1kΩ 对地电阻。
| IO 电平状态 | 系统对外身份 | LDR6500 在干嘛 | VBUS 通路方向 |
|---|---|---|---|
| 低 | Sink | 启用 CC 检测与 PD 协商 | 外部电源进入系统 |
| 高 | Source | 停止抢电,CC 上的 Rd 被断开 | 系统电源输出到外部 |
2.3 两块 IO 还能顺便选电压档位
LDR6500 这类芯片在很多固件里会提供两个 IO 引脚,用来选择 PD 请求电压档位。两个 IO 的组合能表达 4 个状态,正好对应常见的 5V、9V、12V、20V。比如:
| IO1 | IO2 | 请求电压(示意) |
|---|---|---|
| 低 | 低 | 5V |
| 高 | 低 | 9V |
| 低 | 高 | 12V |
| 高 | 高 | 20V |
这段只讲原理。不同批次、不同固件版本的实际映射可能完全不一样,一定以你拿到的 LDR6500 规格书或供应商提供的参数表为准。我自己的习惯是拿到芯片先做一个小板验证,把两个 IO 分别拉高拉低,用示波器看 CC 线上的 PD 握手,确认固件行为再往下设计。
2.4 最小系统接线要点
一个可用的最小系统,大致包含这些节点:VBUS、GND、CC1、CC2、IO1、IO2,以及最终的电源输出。Sink 模式下,CC1/CC2 通过芯片内部的 Rd 网络挂在检测电路上,VBUS 从 Type-C 口进到系统电源路径;Source 模式下,CC1/CC2 外部改接到我们自己搭的 Rp 上拉网络,VBUS 则由系统电源送出。
这里有几个容易出现低级错误的地方。第一,不要在 CC 线上加太大的对地电容,CC 上要跑 BMC 编码的 PD 通信脉冲,电容一大,边沿变缓,协议容易握手失败。第二,IO 引脚不要在外部悬空,哪怕芯片内部有弱上拉,也建议外部再并一个 10kΩ 电阻,明确默认身份。第三,VBUS 路径必须加负载开关或者背靠背 MOSFET,不要让外部电源无控制地倒灌进系统。这几点是“画板前就要想清楚”的,不是等板子回来再调。
3. 落地实操:从原理图到示波器验证的完整流程
3.1 第一步,把 LDR6500 的 IO 行为查清楚
拿到芯片或者模块,第一件事不是画原理图,而是确认 IO 电平的有效极性。有些固件里,IO 拉低代表 Sink,拉高代表 Source;有些正好相反。还有些批次会默认把 IO 内部拉到某个固定电平,外部接法必须配合内部状态,否则你的“拉高”可能被内部拉低淹没了。
我的做法是:焊一块最小板,CC1/CC2 引出测试点,用一个可调电源模拟 Source,把 IO 分别接到 3.3V 和 GND,看 CC 电压和 VBUS 输出状态的变化。这一步花两个小时,后面省两个星期。尤其是从不同渠道买到的 LDR6500,固件版本可能不同,IO 行为也可能不同,不要想当然。
3.2 第二步,设计 CC 网络与 VBUS 功率路径
Sink 模式比较简单:CC 上的 Rd 网络让芯片自己处理,VBUS 进到负载开关,再进到系统。Source 模式真正麻烦:你必须在系统对外体现 Source 身份时,把原来连接在 CC 上的那颗 Rd 完全断开,同时接入自己的 Rp 上拉网络。
实际操作中,我一般用一个 P-MOS 或 N-MOS 做 Rd 断开开关,IO 电平低时 MOS 导通,Rd 接入地;IO 电平高时 MOS 关断,Rd 从电路上消失。VBUS 路径则用背靠背 MOSFET,这样无论哪边先上电,都不会出现电流倒灌。如果你觉得 MOSFET 驱动麻烦,也可以用现成的负载开关芯片,但要注意负载开关的导通电阻和最大电流,Type-C PD 动辄 3A、5A,压降不能太大。
3.3 第三步,固件状态机与 IO 读取
LDR6500 本身很多功能是硬件完成的,但主从切换需要一个控制方,通常是 MCU。下面是一段伪代码,展示 IO 电平控制主从切换的主循环思路:
// 伪代码:IO 电平控制身份切换 #include "ldr6500_driver.h" #define IO_MODE_PIN GPIO_PIN_5 typedef enum { MODE_SINK, MODE_SOURCE } sys_mode_t; sys_mode_t read_sys_mode(void) { // 外部电平决定身份:低电平=Sink,高电平=Source(具体极性以硬件为准) return gpio_read(IO_MODE_PIN) ? MODE_SOURCE : MODE_SINK; } void system_switch_role(sys_mode_t new_mode) { // 安全第一:先断开 VBUS,避免切换瞬间两边打架 vbus_path_disable(); if (new_mode == MODE_SINK) { ldr6500_sink_enable(1); // 启用 LDR6500 的 CC 检测和 PD 协商 cc_rd_connect(1); // 接上 5.1k Rd,对外表现为 Sink } else { ldr6500_sink_enable(0); // 停止 LDR6500 抢 VBUS cc_rd_connect(0); // 断开 Rd,避免把电源拉死 } delay_ms(10); // 等待 CC 电平稳定 if (new_mode == MODE_SOURCE) { vbus_path_enable(); // 把外部电源送到 Type-C 口 } } int main(void) { sys_mode_t mode = read_sys_mode(); system_switch_role(mode); while (1) { sys_mode_t cur = read_sys_mode(); if (cur != mode) { mode = cur; system_switch_role(mode); } delay_ms(100); } }这段代码的核心思想是“切换身份之前先断 VBUS”。我踩过一次坑,直接把 IO 从 Sink 切到 Source,结果 LDR6500 还在抓着之前的 VBUS 不放,外部电源也同时送过来,两边电压一碰,适配器直接打嗝。先关路径、再切身份、最后送电,这个顺序不能反。
3.4 第四步,示波器验收
调试的时候,示波器至少抓三个点:CC1、CC2、VBUS。Sink 模式下,插入 Source 后 CC 应该由低变高,被对面 Rp 拉起来,然后出现一段 BMC 编码脉冲,那是 PD 协商过程;VBUS 从 0 跳到请求的目标电压。Source 模式下,CC 应该由我们自己的 Rp 网络维持在一个偏高的电平,接入一个 Sink 设备后,CC 会被对方 Rd 拉低到分压点;VBUS 则是我们主动送出的。
判断主从切换是否成功,最直接的就是看 CC 和 VBUS 的先后关系。如果 VBUS 起来了,CC 还没有稳定,说明身份检测和功率输出之间存在竞争条件,需要增加延时或者调整切换顺序。这一步不要节省时间,波形存下来,后面改板子的时候还能拿来对比。
4. 调板踩坑记录——CC 电阻、热插拔和 CH340 那些邻居
4.1 坑一:IO 浮空,身份随机
这是最隐蔽的坑。有些模块的 IO 引脚不做任何处理,直接留给用户,如果 MCU 还没启动或者用户忘了接,IO 就浮空。浮空状态下,芯片内部的弱上拉/弱下拉会随机决定一个身份,表现就是“这次插上去取电,下次插上去居然往外面送电”。
解决方式很简单:外部加一个 10kΩ 下拉或上拉,把默认身份固定下来。如果你希望默认是 Sink,就在 IO 上并联 10kΩ 到地;如果希望默认是 Source,就并联 10kΩ 到电源轨。MCU 接管之后,再通过 GPIO 主动输出覆盖这个默认状态。另外,MCU 和 LDR6500 共用电源域时,注意一下上电时序,别让 MCU 的 GPIO 在初始化之前输出一个不确定电平。
4.2 坑二:插上电脑没反应,多半是 CC 少了 5.1kΩ
用 Type-C 座子做设备端,最常见的问题是插上电脑完全没反应。绝大多数情况不是芯片坏了,而是 CC 引脚上的 5.1kΩ 下拉电阻没有放。主机只会通过 CC 引脚的电压判断是否有受电设备接入,如果 CC 悬空,电压不在 Sink 检测窗口内,主机的 VBUS 根本不会打开。
一个标准 Type-C 母座,USB 2.0 相关信号的引脚分组大致是这样:
| 引脚位置 | 信号 | 作用 |
|---|---|---|
| A1/A12 | GND | 地 |
| A4/B4 | VBUS | 总线电源 |
| A5 | CC1 | 身份检测 / PD 通信 |
| B5 | CC2 | 身份检测 / PD 通信 |
| A6/B6 | D+ | USB 2.0 数据 |
| A7/B7 | D- | USB 2.0 数据 |
CC1 和 CC2 上必须各放一颗 5.1kΩ 下拉到地,尤其在只做 USB 2.0 或纯供电的设备端,这两颗电阻就是“身份证明”。我之前帮同事修过一台笔记本,Type-C 口插移动硬盘没反应,拆机量了一圈,不是主控坏,而是 CC 引脚附近的一颗 ESD 保护器件漏电,导致 CC 电压掉出检测窗口,系统一直认为没插设备。换掉那颗器件就恢复了。所以做 LDR6500 板子的时候,CC 网络上的保护器件结电容不要选太大,TVS 管的结电容尽量选 1pF 以下的。
4.3 坑三:D+/D- 要不要串电阻
网上经常有人问 CH340 这类 USB 转串口芯片接到 Type-C 座子上,D+/D- 需不需要串电阻。我的经验是:如果 CH340 和 Type-C 座在同一块板上,走线不超过几厘米,可以不串;如果通过线缆连接,或者板子走线较长、又没有良好的阻抗控制,那就在 D+/D- 上各串一个 22Ω 到 33Ω 的电阻,放在靠近芯片的一端。串电阻的作用不是限流,而是抑制信号边沿反射、减少 EMI。
更需要注意的是 D+/D- 和 CC 走线不要平行太长。CC 上的 BMC 脉冲频率不低,平行走线容易串扰进 D+/D-,导致 USB 枚举不稳定。如果只是 12Mbps 的 Full-Speed 设备,问题不大;如果跑 480Mbps High-Speed,就不建议乱加对地电容,保持走线短、参考地完整更有效。
4.4 坑四:切换瞬间 VBUS 打架
IO 刚切到 Source 时,如果对面正好也是一个 Source,比如两台适配器对插,两个 Rp 各自上拉,谁也不会先输,CC 电压会处在一个异常区间。更危险的是,如果两边都开了 VBUS,正极对正极,电流会从一个电源灌进另一个电源。
我的处理方式是:IO 切到 Source 后,延迟 20ms 再开 VBUS。这 20ms 内先用 ADC 读 CC 电压,如果发现电压异常高或者持续抖动,就认为对面也是 Source,此时回退到 Sink 模式,或者直接报错。这个检测逻辑不增加任何成本,MCU 一个 ADC 通道就能搞定。热插拔的时候也一样,VBUS 上不要瞬间硬切,加一个软启动,用小电流预充一下 Type-C 口的输出电容,能明显减少打火和电压跌落。
5. 从 Demo 到量产:状态机、防倒灌和几种值得做的扩展玩法
5.1 把切换逻辑写成状态机
原型板调通之后,不要停在“能跑就行”的状态。主从切换涉及到电源路径、协议协商、热插拔多种情况,必须用状态机把行为固定下来。我的状态定义大致如下:
- IDLE:空载,VBUS 无输出。
- SINK_ATTACH:检测到对面 Source 的上拉,LDR6500 开始工作。
- SINK_NEGOTIATE:LDR6500 正在和 Source 协商电压。
- SINK_READY:协商完成,VBUS 正常进入系统。
- SOURCE_ATTACH:IO 切换为 Source,断开 Rd,等待对面 Sink 接入。
- SOURCE_READY:检测到对面 Sink,VBUS 主动输出。
- FAULT:过压、过流、倒灌或协商超时。
跳转条件主要依赖 IO 电平变化、CC 电压阈值和超时定时器。我习惯用一个 5ms 周期的轮询任务扫描这些状态,任何跳转之前先执行“断开 VBUS”这个动作。这样做的好处是逻辑清晰,每个状态都对应一组硬件行为,出问题的时候看状态寄存器就知道卡在哪一步。
5.2 功率路径可靠性设计
主从切换产品的可靠性,一大半在功率路径上。背靠背 MOSFET 是必须的,防止两个 Source 互灌;VBUS 入口和出口各放一颗 TVS,吸收热插拔浪涌;IO 电平控制线加一个 RC 滤波,比如 1kΩ 电阻串联 100nF 电容到地,防止干扰脉冲误切身份。
量产测试阶段,我建议在固件里加一条“身份回读”指令,让上位机或测试治具读取当前 IO 状态和身份状态。很多虚焊、电阻贴错的问题,靠这一条命令就能在生产线上筛掉。别小看这个细节,Type-C 座子是手工焊接重灾区,CC 电阻虚焊是“插上没反应”的第一大原因。
5.3 扩展玩法
这套“IO 电平控制主从切换”的思路,换一下外围电路就能套到好几个项目上:
- 双向移动电源/户外电源:LDR6500 做受电侧的 PD 协商,IO 电平决定系统是给电池充电还是从电池放电,外部配一颗双向 DC-DC。
- Type-C 调试器/串口工具:LDR6500 负责从主机取电,CH340 做串口,再配合一颗 TS3USB221 模拟开关切换 D+/D- 的数据通道,IO 电平控制模拟开关的选通方向。
- 开发板 Type-C 供电口:一个 IO 决定请求 20V 还是 5V,配合板上的 DC-DC 给主控供电,避免上电时请求错误电压烧板。
- 工业设备主从切换:用光耦隔离的 IO 信号控制身份,配合 PLC 的远程 IO 模块做角色切换,这在现场调试中比软件改地址稳得多。
5.4 什么时候别用 IO 电平硬切
IO 硬切也不是万能的。如果产品需要在运行时频繁动态交换角色,比如扩展坞在插拔不同设备时自动切换 Source/Sink,或者需要根据电池电量和外接设备状态综合考虑身份,这时候就需要换一颗支持完整 DRP/PR_Swap 的 PD 控制器,比如 STUSB4500、FUSB302 配合 MCU 跑协议栈。LDR6500 加外部 MOS 的硬切方案适合“上电定身份”的场景,不该拿去硬扛动态切换需求。
我个人在实际操作中的体会是,这套方案最大的优点就是“可预期”。IO 电平落下去,身份就定下来,不会因为协议栈状态混乱而出现两个口互相等对方的死局。第一次调切换时我总觉得协议芯片可以自己搞定一切,结果发现硬件上 Rd 没有断开,Source 模式一开,5V 直接被拉到地,适配器打嗝。后来老老实实按状态机做,把身份切换理解成“先把手里的刀放下,再决定自己是什么人”,问题就顺了。给后来者一个建议:画板之前先用 LDR6500 模块把 IO 行为摸清楚,用示波器把 CC 波形存下来,再动手设计功率路径。这比上来就画板、烧板后拿着万用表到处捅要省一个迭代周期。另外,网上搜“IO”会出来一堆磁盘性能、PLC IO 之类完全不相关的内容,搜 LDR6500 相关文章时注意别跑偏。