1. USB OTG 概述与基础概念

做嵌入式这么多年,USB OTG 这块确实坑不少。每次驱动加载失败,工程师们第一反应往往是怀疑硬件,但很多时候问题出在对 OTG 基础概念的理解上。今天我们就从根儿上捋一捋。

1.1 什么是 USB OTG?

USB OTG,全称 USB On-The-Go,是 USB 2.0 规范的一个补充协议。它解决了一个很实际的问题:设备间的直接数据交换。

传统 USB 架构里,主机(Host)只有一个,外设(Peripheral)也只有一个。手机连电脑,电脑是 Host,手机是 Device。但如果两个手机想互传文件呢?没门儿。OTG 就是来打破这个僵局的。

我记得最早接触 OTG 是在 2012 年做的一款平板电脑项目。客户要求平板能接 U 盘,也能当 U 盘被电脑识别。当时我就想,这不就是让一个设备动态切换角色吗?对的,OTG 的核心就是角色动态协商。

关键定义: USB OTG 允许设备在 Host 和 Device 之间动态切换,通过 ID 引脚和主机协商协议(HNP)来实现。它兼容 USB 2.0,但并非所有 USB 设备都支持 OTG。

1.2 OTG 的演进路线

技术总是向前走的。USB OTG 从诞生到现在,经历了几个版本。我给你们列个表,这样一目了然。

版本 发布时间 核心变化 我的实际感受
USB OTG 1.0 2001 定义 ID 引脚、主机协商协议(HNP) 初次引入,硬件设计复杂,很多芯片不支持
USB OTG 2.0 2006 增加 A-device、B-device 概念,优化 HNP 流程 兼容性好了不少,但功耗控制仍是痛点
USB OTG 3.0 2008 支持 SuperSpeed,加入无缝切换 性能提升明显,但驱动协议复杂度大增

简单说,从 OTG 1.0 到 3.0,主要解决三个问题:连接兼容、角色切换、高速支持。你在实际项目中遇到的大部分加载失败,往往卡在第一个版本的定义上——硬件没焊 ID 引脚,或者 ID 引脚电平不对。

1.3 核心硬件结构

USB OTG 在传统 USB 四根线(VBUS、D+、D-、GND)的基础上,特意加了第五根线——ID 引脚。这根引脚干啥用?简单说就是给设备一个“身份牌”。

  • ID 引脚接地(低电平):设备默认为 Host(A-device),负责供电和枚举。
  • ID 引脚悬空或拉高(高电平):设备默认为 Device(B-device),等待 Host 枚举。

我见过一个坑:有工程师为了省成本,直接把 ID 引脚悬空,结果插上 U 盘后系统识别不出。后来查了半天,发现芯片内部弱上拉,导致 ID 引脚状态不确定。嗯,这地方一定要加硬性的下拉或上拉电阻,不能用软件配置糊弄。

除了 ID 引脚,还有一个非常重要的概念:VBUS 供电能力。OTG Host 必须提供至少 8mA 的 VBUS 电流,而标准 Host 是 100mA 或 500mA。你想想看,如果一个 OTG 设备驱动能力不够,插上功耗大的外设(比如移动硬盘),VBUS 就会跌落,驱动加载直接失败。我在项目中就遇到过这种“驱动加载失败”,调了三天才发现是供电不足。

1.4 角色协商协议(HNP)

HNP 全称 Host Negotiation Protocol,是 OTG 的灵魂。它负责让两个设备在物理连接后,动态决定谁当 Host,谁当 Device。

过程其实不复杂:

  1. 默认情况下,A-device(ID 引脚接地)先做 Host,提供 VBUS。
  2. 如果 A-device 觉得没必要继续做 Host,它可以挂起总线的同时,释放 VBUS 使用权。
  3. B-device 检测到 VBUS 变化,如果它想做 Host,就通过拉低 D+ 或 D- 来发出请求。
  4. A-device 收到请求,同意切换,两者交换角色。

听起来很顺畅对不对?但实际执行时,时序要求非常严。我调过一家的 OTG 控制器,HNP 切换时如果 D+ 线毛刺过多,芯片会误判成双重角色冲突,驱动直接报错码 -ENOTSUPP。后来我们在 D+/D- 线上加了 22pF 对地电容,问题就消失了。这就是实战。

避坑指南: HNP 过程中,软件层面的“角色切换超时”最常见。我曾经用逻辑分析仪抓到过,某个芯片在释放 VBUS 后,B-device 要等 100ms 才能发起请求,驱动里没加这个延时,一切换就崩。记得加 msleep(100)。

1.5 驱动加载失败的常见宏观原因

在深入每个代码细节前,我们先从整体上看看为什么 OTG 驱动加载会失败。这部分是理论框架,后面几章会逐一深挖。

  • 硬件层面: ID 引脚没有正确连接或电平错误;VBUS 供电不足;D+/D- 线阻抗不匹配。
  • 软件层面: OTG 驱动没有注册 HNP 回调;角色状态机未初始化;时钟或电源管理未正确配置。
  • 协议层面: HNP 时序超时;不支持 SRP(会话请求协议);控制器硬件版本与驱动不匹配。

你仔细看就会发现,很多失败其实都围绕着一个核心:ID 引脚的状态。我见过最离谱的案例是,客户的原理图上 ID 引脚标了上拉,实际上 BOM 里电阻没焊,导致设备永远觉得自己是 Host,插到电脑上电脑就报“无法识别的USB设备”。

1.6 知识体系总览

为了让你对本章有个全局印象,我画了一张结构图。它展示了 USB OTG 的整体知识脉络:从物理层到协议层再到应用层,每层的关键要素。

USB OTG 知识体系 物理层 ID 引脚(高/低电平) VBUS 供电(≥8mA) D+/D- 阻抗匹配 协议层 HNP 主机协商协议 SRP 会话请求协议 角色状态机 驱动层 UDC / UDC-core Gadget 框架 HNP 回调注册 设备角色 A-device (Host) ↕ B-device (Device) 应用层 文件传输 MTP / PTP USB 存储 本质:角色协商 ID 引脚决定初始角色 HNP 完成动态切换

这张图里,物理层、协议层、驱动层、应用层层层递进。大部分驱动加载失败都发生在驱动层和协议层的接口上。比如驱动没注册 HNP 回调,或者 HNP 中断处理不正确。理解了这个架构,再看后面的代码分析,心里就有谱了。

1.7 本章小结

USB OTG 说穿了就是一句话:让设备身兼两职,通过一根 ID 引脚和一套协议来切换身份。但在实际落地时,硬件设计、软件状态机、协议时序任何一个环节疏忽,都会导致驱动加载失败。

我个人习惯在项目初期先确认两件事:一是 ID 引脚电平是否在预期范围,二是 VBUS 供电能否驱动目标外设。这两点对了,至少一半的坑就避开了。