Source 和 Sink 角色:OTG 中的角色定义与切换
角色到底是什么?
在 USB OTG 里,Source 和 Sink 是电源角色的两个端点。说白了,Source 就是负责供电的那一方,Sink 则是用电的那一方。传统 USB 里,主机永远是 Source,设备永远是 Sink,这个关系是死的。但 OTG 打破了这种固定搭配——一台手机既可以当充电宝给外设供电,也可以反过来被充电。
我刚开始接触 OTG 时,以为角色切换就是简单的硬线切换。结果在项目里踩了个大坑:电源角色和 Data 角色是两个独立的概念,不能混为一谈。后来才明白,电源角色决定谁提供 VBUS,Data 角色决定谁当主机。这两个角色可以同步切换,也可以独立管理。
Source 的职责
- 提供 VBUS 电压(通常 5V,PD 时可协商更高)
- 检测 Sink 的连接状态
- 管理电源能力协商(比如通过 PD 协商电流)
- 在空闲时关闭 VBUS 以省电
Sink 的职责
- 从 VBUS 取电
- 向 Source 请求需要的电流或电压
- 在不需要电源时通知 Source 关闭 VBUS
角色切换的时机
OTG 规范定义了两种切换方式:HNP(主机交换协议)和 SRP(会话请求协议)。HNP 用于交换 Data 角色,SRP 用于唤醒休眠中的总线。但在实际产品中,我更推荐直接使用 USB PD 的 DR_Swap 消息,它更灵活,且兼容性更好。
我以前参与过一个双角色快充方案,最初用的 HNP,结果发现某些老旧设备不支持 HNP 握手。后来改用 PD 的电源角色交换,问题就解决了。嗯,这里要注意:HNP 依赖硬件 ID 引脚,而 PD 是通过 CC 线协商的,后者更干净。
电源角色与 Data 角色的关系
很多人以为 Source 必须同时是主机,Sink 必须同时是设备。其实不是。下表列出四种可能组合:
| 电源角色 | Data 角色 | 实际场景 |
|---|---|---|
| Source | Host | 手机通过 OTG 接鼠标 |
| Source | Peripheral | 手机连接 PC,但手机给 PC 供电(罕见,需硬件支持) |
| Sink | Host | 平板接键盘,同时从键盘取电(键盘内置电池时) |
| Sink | Peripheral | 手机连接电脑传文件、同时充电 |
注意: 表格最后一种即最常见的“充电 + 数据通信”模式。这个时候电源角色是 Sink,Data 角色是 Peripheral,并不冲突。
角色切换流程
下面这张图展示了 OTG 双角色设备之间的一次典型角色交换过程。初始时设备 A 是 Source + Host,设备 B 是 Sink + Peripheral;经过 HNP + SRP 或 PD 协商后,双方交换角色。
如何检测当前角色?
在嵌入式开发中,我们通常通过读取硬件状态寄存器或者系统接口来获知角色。以 Linux 为例,可以读取 /sys/class/power_supply/usb/ 下的文件,或者使用 USB gadget 的配置接口。下面是一个简化的检测示例:
# 检查 VBUS 是否存在(Sink 角色)
cat /sys/class/power_supply/usb/online
# 输出 1 表示有 VBUS,即本设备作为 Sink
# 检查当前 gadget 角色
cat /sys/kernel/config/usb_gadget/g1/UDC
# 如果非空,表示本设备作为 Peripheral(Data 角色)
但更可靠的方法是直接读取 USB 控制器的角色寄存器。我习惯在初始化时打印这些值,方便调试。
角色切换的工程陷阱
- VBUS 浪涌:角色切换时 VBUS 会经历“断开-重新建立”,可能产生浪涌电流。我通常会在 VBUS 路径上加 soft-start 电路。
- 协议优先级:如果同时支持 HNP 和 PD,应该优先使用 PD 协商。因为 HNP 需要 ID 引脚,而有些 Type-C 线缆已经不再包含 ID 线。
- 角色锁死:某些设备的固件在角色协商失败后会死锁。建议增加超时回退机制,比如 3 秒内协商失败就恢复到默认角色。
小结
Source 和 Sink 是 OTG 的电源角色,它们决定了谁供电谁用电。配合 Data 角色,总共可以组合出四种模式。角色切换的核心在于正确检测当前状态、触发协商协议、以及安全地翻转 VBUS。在实际项目中,多花点时间在硬件保护上,比在软件里堆各种状态机要划算得多。