2. OTG 概念详解
讲 OTG 之前,我们得先搞清楚一个问题:为什么要有 OTG?
早年 USB 的世界很简单:电脑永远是 Host,键盘鼠标永远是 Device。可后来手机、平板这些便携设备出来了,用户想把 U 盘插手机上直接读文件,怎么办?手机平时是 Device,可这时候它得临时当一下 Host。OTG 就是干这个的——让一个 USB 口在 Host 和 Device 之间来回切换。
说白了,OTG 就是让一个 USB 口能当 Host 也能当 Device。但这个切换不是随便喊一句就能完成的,底层有一套严格的协议在撑着。
2.1 OTG 协议规范
OTG 规范最早由 USB-IF 在 2001 年发布,后来经历了几个版本迭代。目前主流设备基本都支持 OTG 2.0 和补充规范 ECN(工程变更通知)。核心就三个关键词:HNP、SRP、ADP。
我个人习惯把这三者称为 OTG 的“三板斧”——
- HNP(Host Negotiation Protocol):主机协商协议,负责角色互换。
- SRP(Session Request Protocol):会话请求协议,负责唤醒总线。
- ADP(Attach Detection Protocol):连接检测协议,负责识别对端类型。
2.2 HNP / SRP / ADP 原理
HNP:主机协商协议
HNP 的流程,我画个图你就明白了。
默认情况下,OTG 设备插上后,谁的主机能力更强谁就当 Host。但 HNP 允许双方在运行中动态交换角色。举个例子:你的手机正在给鼠标供电(手机是 Host),这时候你想把手机切换成 Device 去连电脑,HNP 就派上用场了。
HNP 的过程可以分为三步:
- 初始状态: A 设备(默认 Host)供电,B 设备(默认 Device)等待。
- 请求切换: A 设备拉低 D+ 线,通知 B 设备“我想做 Device”。
- 角色翻转: B 设备检测到 D+ 拉低后,拉起 D+ 并释放 VBus 控制权,B 成为新 Host。
VBus 持续供电的前提下进行。如果 VBus 掉了,一切归零。
我记得最早调 HNP 时被坑过一次——代码里 HNP 请求发出去后,对方没反应。折腾了两天才发现,是 D+ 拉低的时间窗口没满足规范要求的 5ms 最小值。芯片厂商的参考代码给了个 1ms 的延时,但实际测试发现有些设备反应慢,必须拉到 5ms 以上。
SRP:会话请求协议
SRP 解决的是省电问题。当总线上长时间没有活动时,Host 可以关闭 VBus 进入挂起状态。这时候如果 Device 想干活——比方说鼠标动了一下——就得用 SRP 把 Host 叫醒。
SRP 的机制分两种:
- 数据线脉冲(Data-line Pulsing): Device 在 D+ 或 D- 上发送一个脉冲,告诉 Host“我有事”。
- VBus 脉冲(VBus Pulsing): Device 在 VBus 上拉一个短暂的高电平,Host 检测到后恢复供电。
我测过很多客户的设备,发现一个问题:很多嵌入式工程师只实现了数据线脉冲,忽略了 VBus 脉冲。结果就是——某些 Host 只认 VBus 脉冲,你的设备永远叫不醒它。
ADP:连接检测协议
ADP 是后来补充的协议,主要为了解决一个问题:如何在不耗尽电池的情况下知道对端是什么东西?
传统的检测方式需要 Host 一直提供 VBus 电流,这在电池供电的场景下是灾难。ADP 的思路是:双方都只发一个极短的脉冲(微秒级),通过脉冲的反射情况判断对端是 Host、Device 还是空载。
ADP 有三种状态:
| ADP 状态 | 行为 | 典型场景 |
|---|---|---|
| ADP_PROBE | 发送探测脉冲 | 设备刚插入,想知道对方是什么 |
| ADP_SENSE | 监听对端脉冲 | 设备等待对方响应 |
| ADP_PRB | 响应脉冲 | 已确定角色,保持连接 |
个人觉得 ADP 比纯粹检测 VBus 电平要优雅得多。VBus 电平检测只能判断“有没有电”,但 ADP 能判断“对端是什么类型的电”。举个例子:手机上插了一个充电器,VBus 有 5V,但如果你以为它是 Host 就去发起 HNP,那就错了——充电器根本不会响应 HNP。ADP 可以提前识别这是充电器,避免无谓的握手。
2.3 角色切换(Host / Device / OTG)
OTG 设备有且仅有三种角色:
- Host: 提供 VBus,管理总线枚举和数据传输。
- Device: 接收 VBus,响应 Host 的请求。
- OTG: 两者皆可,通过 ID 引脚和协议协商确定当前角色。
这里有个容易混淆的地方:普通设备要么是 Host 要么是 Device,但 OTG 设备是“我全都要”。OTG 设备必须额外提供一个 ID 引脚。Micro-USB 的第五根线就是 ID 引脚,它用来识别插头的类型:
- ID 引脚接地(GND)→ 该设备默认为 Host。
- ID 引脚悬空(Float)→ 该设备默认为 Device。
但 ID 引脚只是初始值,真正的角色切换靠 HNP。也就是说,ID 引脚告诉你“我是什么”,HNP 告诉你“我现在想变成什么”。
我做过的项目里,最常见的角色切换拓扑就两种:
| 拓扑 | 初始 Host | 初始 Device | 切换后 Host |
|---|---|---|---|
| 手机 ↔ U 盘 | 手机(ID 接地) | U 盘(ID 悬空) | 不切换,手机一直是 Host |
| 手机 ↔ 电脑 | 电脑(ID 接地) | 手机(ID 悬空) | 可通过 HNP 让手机变成 Host |
嗯,这里要注意:很多开发板的 USB-OTG 驱动默认只实现了 ID 引脚检测,没实现 HNP。也就是说,插什么头就固定当什么角色,不能动态切换。这在某些场景下足够了,但如果你要做的是像 USB-C 那种纯软件协商的解决方案,HNP 是绕不过去的。
为了帮你理清整个 OTG 系统的运作逻辑,我画了一张图——从系统初始化到 HNP 角色切换的全流程:
从这张图能看到,整个 OTG 的生命周期其实很简单:上电 → 读 ID → 确定初始角色 → 工作 → 需要切换时执行 HNP → 新角色继续工作。ADP 则是在 ID 检测之前悄悄完成的,用来判断对端是否值得唤醒。
最后补充一句:不要小看角色切换的时序。USB 2.0 规范要求角色切换必须在 100ms 内完成,否则总线会超时。我见过不少方案,软件协商是走通了,但切换时间花了 300ms,结果电脑端直接报“USB 设备无法识别”。所以如果你在做 OTG 嵌入式开发,一定在逻辑分析仪上卡一下 HNP 的时序,D+ 拉低到角色翻转完成,必须在 100ms 以内。