2. OTG 角色切换机制:HNP 协议、SRP 协议、角色切换时序

做 USB OTG 调试,最头疼的就是角色切着切着就断了。我记得刚接触 OTG 那年,遇到一个平板连键盘投屏的案子,设备死活不切换 Host 模式,最后查出来是 HNP 协商超时。说白了,角色切换不是一个简单的电平跳变,它是有一套完整握手协议的。

2.1 SRP 协议:谁先发起对话?

SRP(Session Request Protocol)是设备想主动干活的敲门砖。你想想看,一条 USB 线平时是静默的,VBUS 甚至可能都没电。这时候某个设备想发起会话,就得通过 SRP 让总线活起来。

SRP 分为两步:

  • 数据线脉冲(Data Line Pulsing):发起方在 D+ 或 D- 上拉一个 5~10ms 的脉冲。B 设备想当 Host 时,拉 D+;A 设备想结束会话时,拉 D-。
  • VBUS 脉冲(VBus Pulsing):如果数据线脉冲没反应,发起方再拉一下 VBUS(大概 2.1V 以上、20ms 左右),告诉对面“我要用电了”。

我遇到过的一个坑:某款 Type-C 转 micro-B 线缆,VBUS 上的下拉电阻选型不对,导致 VBUS 脉冲幅度一直被拉低到 1.8V,另一端死活检测不到。嗯,这里要注意:SRP 的 VBUS 脉冲阈值是 2.0V,小于这个值就不会触发。

SRP 关键参数速查表(这些是我在实际抓波形时记下来的)

参数最小值典型值最大值说明
数据线脉冲宽度5 ms10 ms20 ms超过 20ms 视为超时
VBUS 脉冲电压2.0 V2.1 V3.6 V高于 3.6V 可能误触发过压保护
VBUS 脉冲持续时间10 ms20 ms40 ms太短容易漏检
SRP 后的等待时间100 ms—1 s超时后发起方应重试或放弃

2.2 HNP 协议:角色互换的握手流程

HNP(Host Negotiation Protocol)才是真正的“换座位”。只有支持 OTG 的设备才能玩这个。你能想象吗?一根线插上,两个设备商量谁来当 Host。有一次我在调试一块开发板,连接 U 盘后想切到 Device 模式,结果 HNP 一直失败,抓着逻辑分析仪一看——对方根本没发 HNP 请求。

HNP 的标准流程:

  1. 初始状态:A 设备(默认 Host)提供 VBUS,B 设备(默认 Peripheral)挂起。
  2. B 设备申请切换:B 设备把 D+ 拉低(至少 2ms),然后释放。这表示“我想当 Host”。
  3. A 设备响应:A 设备检测到 D+ 上的脉冲后,停止驱动 D+/D-,并释放总线。A 设备进入 Peripheral 模式。
  4. B 设备接管:B 设备在检测到总线空闲超过 3ms 后,驱动 D+ 上拉,成为新 Host。
  5. 总线重新枚举:新 Host 开始复位和枚举,原来的 A 设备作为 Peripheral 响应。

注意,HNP 要求双方都必须支持 OTG(即有 ID 引脚或 Type-C CC 逻辑)。我个人习惯在协议分析仪上同时抓 D+/D- 和 VBUS,这样才能看到完整的脉冲时序。

曾经让我通宵的一个 Bug:某颗 MCU 的 OTG 控制器在 HNP 握手期间,被 USB 中断强行打断了 D+ 脉冲的检测窗口。结果导致 A 设备认为 B 设备没发请求,双方僵持在那里。解法是把 USB 中断优先级降低,或者用 DMA 处理数据,让脉冲检测不受影响。

2.3 角色切换时序:谁先动、谁后动?

时序是 OTG 切换的命门。SRP 和 HNP 都依赖严格的时间窗。我见过产品送检时因为时序差几毫秒被 USB-IF 打回的案例。下面这张图基本覆盖了完整的“A 设备 vs B 设备”切换过程。

OTG 角色切换完整时序图 A 设备(初始 Host) B 设备(初始 Peripheral) ① 提供 VBUS,枚举 B 挂起总线,等待 HNP (释放 D+/D- 驱动) 检测到 D+ 脉冲 停止自身驱动,进入 Peripheral ④ 响应新 Host 枚举 ② 枚举完成,进入挂起 (等待用户/应用触发切换) SRP + HNP 请求 (拉 D+ 脉冲 10ms) 检测到总线空闲 > 3ms 驱动 D+ 上拉,成为新 Host ⑤ 复位总线,枚举 A ① 枚举 ② D+ 脉冲 ③ 总线空闲 整个切换过程必须 在 100ms 内完成,否则系统可能超时重置 实际抓波形时重点观测 D+ 的 10ms 脉冲和总线空闲 3ms

2.4 切换失败时的常见根因分析

我整理了一个对照表,大家调试时可以直接对号入座:

现象可能原因我的排查建议
永远切不过去ID 引脚或 CC 逻辑未正确实现检查 OTG ID 引脚是否悬空,Type-C 的 CC1/CC2 是否正确上拉/下拉
切过去后无法枚举HNP 时序中总线空闲时间不足 3ms用示波器测量 D+ 释放到对方上拉的时间差
偶尔切成功,大部分失败SRP 脉冲宽度不够稳定看固件里定时器是否被中断抢占,我建议在 HNP 请求期间关中断
切完角色很快又自动切回来对方设备在枚举期间又发了一次 HNP 请求检查新 Host 是否在枚举完成前误判了总线状态

我的一个小窍门:调试 HNP 时别光看逻辑分析仪的 D+/D- 电平,把 VBUS 也拉上。有一次我发现 VBUS 在切换瞬间跌落到了 4.2V 以下,导致新 Host 启动时电压不足,枚举失败。加一个足够大的 VBUS 电容就能解决。

2.5 关于 SRP 和 HNP 的一点补充

你可能会问:为什么需要两个协议?我简单解释一下:SRP 是“我要用电,开启会话”,HNP 是“我要当管理,交换角色”。在 USB-IF 的规范里,SRP 可以独立于 HNP 使用——比如一个鼠标想从挂起状态唤醒总线,只需要 SRP 就够了,不需要切换角色。但如果你要做的是手机通过 OTG 线连 U 盘又想反过来插电脑,那你就得先后经过 SRP + HNP 完整走一遍。

嗯,还有一点容易忽略:有些设备只实现了 SRP 却没有实现 HNP,所以它只能唤醒总线,不能切换主机。这在调试时容易误判——你看到它发出脉冲了,以为它在做 HNP,结果它只是 SRP。我曾经在客户现场被这个问题坑过两个多小时。