当前位置: 首页 > news >正文

USB2.0 协议深度解析:从物理信号到枚举与事务传输

USB协议详解本文主要讲解 USB2.0 协议。USB3.0 与 USB2.0 差异较大但目前在单片机上普遍使用的仍然是 USB2.0因此学习 2.0 依然非常有意义。内容涵盖从物理信号握手、速度识别到通信建立完成的整个过程。虽然力求完善但难免存在疏忽欢迎指正。USB协议基础目前 USB2.0 主要有三种速度等级低速 (Low Speed)1.5 Mbps全速 (Full Speed)12 Mbps高速 (High Speed)480 Mbps低速设备现在已经很少使用全速和高速应用较多很多单片机都支持全速/高速。下文将重点围绕全速和高速展开低速只作简单介绍。USB 采用差分信号因此通常只需要 4 根线即可建立通信。下面先介绍几个基本概念。信号状态J/K/SE0/SE1J 状态全速/高速下D D-低速下相反。K 状态全速/高速下D- D低速下相反。NRZI 编码逻辑 0 时电平翻转J↔K逻辑 1 时保持不变。若连续出现 6 个逻辑 1则强制插入一个逻辑 0位填充防止信号长期不变导致失步。SE0 (Single-Ended Zero)D和D-均为低电平常用作总线复位信号或数据包结束标志。SE1 (Single-Ended One)D和D-均为高电平为非法状态正常通信中不应出现。IDLE 状态全速/高速下空闲时为 J 状态低速下空闲时为 K 状态。SOP (Start of Packet)总线从 IDLE 状态切换到 K 状态表示一个数据包的开始。EOP (End of Packet)通常由2 位时间的 SE0后紧跟1 位时间的 J 状态组成表示数据包结束。掌握了这些就能理解 USB 如何传输 0/1 信号以及如何界定数据包边界。物理信号与速度识别热插拔检测USB 支持热插拔主机通常是 PC需要知道设备何时插入、何时拔出。主机侧D和D-均通过 15kΩ 电阻下拉无设备接入时处于 SE0 状态。设备侧全速/高速设备D通过 1.5kΩ 电阻上拉。低速设备D-通过 1.5kΩ 电阻上拉。当设备插入后主机检测到D或D-被拉高即可判断出设备的速度类别。总线复位主机识别到设备后会发送复位信号将D和D-同时拉低SE0并持续至少 10ms。速度协商高速握手高速设备与全速设备初始时都在 D 上拉主机无法直接区分。因此需要额外的握手流程来识别高速能力。高速设备插入后先以全速设备身份工作。主机发送复位信号SE0。在复位期间约2.5ms后高速设备主动将D-拉低使用电流源使总线进入K 状态。这个信号称为chirp K持续时间约 1~7ms。如果主机支持高速在 chirp K 结束后主机交替发送chirp K和chirp J信号序列共 3 对。如果主机不支持高速主机无视 chirp K继续维持 SE0 复位。握手完成后高速设备会断开 D 上的 1.5kΩ 上拉电阻并在 D 和 D- 上接入两个 45kΩ 下拉电阻切换到高速模式电气特性。至此速度协商完成后续通信全部基于数据包不再依赖物理信号协商。这里省略了一些细节但掌握上述流程足以理解 USB 速度协商的核心思想。USB2.0 通信的核心概念主机驱动、从机响应在 USB2.0 通信中主机永远是请求的发起方从机只能被动响应。三个层次传输、事务、数据包数据包 (Packet)最基础的通信单元由多个域字段组成。事务 (Transaction)通常包含三个数据包阶段令牌、数据、握手。传输 (Transfer)由若干个事务组成对应具体的通信需求如控制传输、批量传输等。数据包的通用结构一个标准 USB 数据包包含以下部分域说明SYNC同步域全速/低速下为 8 位00000001二进制高速下为 32 位00000000 00000000 00000000 00000001。多个 0 对应电平不断翻转用于实现硬件同步。PID包标识符域8 位低 4 位表示真正的 PID 类型高 4 位是其位取反用于简单校验。PID 不允许出错它决定了数据包的类型和用途。DATA数据负荷可选不同类型的数据包内容不同。CRC校验前面的所有数据。EOP包结束标志。事务示例SETUP 事务 IN 事务以一个完整的请求设备描述符流程为例第一阶段SETUP 事务主机发送令牌包PID SETUP。主机发送数据包PID DATA0内含请求设备描述符的命令。从机返回握手包ACK。第二阶段IN 事务获取数据主机发送令牌包PID IN。设备将设备描述符数据放入总线数据包PID 根据切换位决定。主机返回握手包ACK。上述两个阶段各是一个事务。而传输则是由这些事务组合而成的更高层次概念详见后文。设备、配置、接口、端点这四个概念是 USB 描述符体系的核心在 C 语言中通常用结构体表示。1. 设备描述符 (Device Descriptor)每个 USB 设备必须有且仅有一个设备描述符包含设备的基本信息厂商 ID、产品 ID、USB 版本等。// 标准设备描述符结构18 字节structusb_device_descriptor{uint8_tbLength;// 描述符长度固定为 18 (0x12)uint8_tbDescriptorType;// 描述符类型固定为 0x01uint16_tbcdUSB;// USB 规范版本号 (BCD 码)uint8_tbDeviceClass;// 设备类代码uint8_tbDeviceSubClass;// 设备子类代码uint8_tbDeviceProtocol;// 设备协议代码uint8_tbMaxPacketSize0;// 端点 0 最大包长 (8/16/32/64)uint16_tidVendor;// 供应商 ID (VID)uint16_tidProduct;// 产品 ID (PID)uint16_tbcdDevice;// 设备版本号 (BCD 码)uint8_tiManufacturer;// 制造商字符串索引uint8_tiProduct;// 产品字符串索引uint8_tiSerialNumber;// 序列号字符串索引uint8_tbNumConfigurations;// 支持的配置数量}__attribute__((packed));2. 配置描述符 (Configuration Descriptor)描述设备的某个配置一个设备可以有多个配置但通常只有一个。配置描述符本身之后紧跟着该配置下的接口描述符和端点描述符。// 标准配置描述符结构9 字节structusb_config_descriptor{uint8_tbLength;// 描述符长度固定为 9 (0x09)uint8_tbDescriptorType;// 描述符类型固定为 0x02uint16_twTotalLength;// 整个配置的总长度包括配置、接口、端点等所有描述符uint8_tbNumInterfaces;// 该配置下的接口数量uint8_tbConfigurationValue;// 用于 SetConfiguration 请求的参数值uint8_tiConfiguration;// 配置字符串索引uint8_tbmAttributes;// 配置属性如自供电、远程唤醒等uint8_tbMaxPower;// 总线最大电流单位 2mA}__attribute__((packed));3. 接口描述符 (Interface Descriptor)一个配置下可以有多个接口每个接口代表一个逻辑功能例如一个 USB 耳机可能有音频接口和麦克风接口。接口描述符中包含了该接口的类、子类、协议等信息用于匹配驱动。// 标准接口描述符结构9 字节structusb_interface_descriptor{uint8_tbLength;// 描述符长度固定为 9 (0x09)uint8_tbDescriptorType;// 描述符类型固定为 0x04uint8_tbInterfaceNumber;// 接口编号从 0 开始uint8_tbAlternateSetting;// 备用设置编号用于动态调整带宽uint8_tbNumEndpoints;// 该接口使用的端点数不含端点 0uint8_tbInterfaceClass;// 接口类代码uint8_tbInterfaceSubClass;// 接口子类代码uint8_tbInterfaceProtocol;// 接口协议代码uint8_tiInterface;// 接口字符串索引}__attribute__((packed));4. 端点描述符 (Endpoint Descriptor)端点描述符定义了实际数据传输通道的详细信息方向、地址、传输类型、最大包大小、轮询间隔等。端点 0 没有端点描述符其参数已在设备描述符中给出。// 标准端点描述符结构7 字节structusb_endpoint_descriptor{uint8_tbLength;// 描述符长度固定为 7 (0x07)uint8_tbDescriptorType;// 描述符类型固定为 0x05uint8_tbEndpointAddress;// 端点地址bit7: 方向 0OUT,1INbit0-3: 端点号uint8_tbmAttributes;// 端点属性bit0-1: 传输类型等时传输时还有额外字段uint16_twMaxPacketSize;// 最大包大小低 11 位有效高速时 bit11-12 为额外事务数uint8_tbInterval;// 轮询间隔中断/等时端点有效批量/控制忽略}__attribute__((packed));层级关系设备 → 配置 → 接口 → 端点一个设备包含一个或多个配置一个配置包含一个或多个接口一个接口包含一个或多个端点以及可选的备用接口。USB2.0 枚举过程速度协商完成后主机需要识别设备类型并加载相应驱动这个过程称为枚举。获取设备描述符前 8 字节主机向地址 0 的端点 0 发送GET_DEVICE_DESCRIPTOR请求只读取前 8 字节目的是获取端点 0 的最大包长bMaxPacketSize0。再次复位总线让设备回到干净状态准备分配新地址。分配设备地址主机发送SET_ADDRESS请求为设备分配一个唯一的地址1~127。此后通信都使用新地址。获取完整设备描述符主机向新地址的端点 0 再次请求完整的 18 字节设备描述符。若端点 0 最大包长较小可能需要多次 IN 事务。获取配置描述符先获取配置描述符的前 9 字节从中得到wTotalLength整个配置的总长度然后再次请求读取完整配置描述符包括其后所有的接口、端点描述符。配置描述符、接口描述符、端点描述符在数据中是连续存放的一次读取即可全部获得。激活配置主机发送SET_CONFIGURATION请求设备根据主机选择的配置生效。此后设备开始正常工作。枚举过程本质上就是通过标准请求读取设备信息、设置工作参数。USB 协议的分层模型设备/配置/接口/端点使得这个过程看起来稍显复杂但逻辑清晰。USB2.0 数据传输四种传输类型传输类型特点典型应用批量传输 (Bulk)数据量大、对实时性要求低强 CRC 校验 重传机制保证数据无误总线繁忙时带宽会被压缩。U盘、硬盘中断传输 (Interrupt)数据量小、响应快轮询方式实现保证低延迟。鼠标、键盘等时/同步传输 (Isochronous)面向音视频流预留固定带宽无握手阶段无 ACK/NAK允许丢包以保证实时性。摄像头、声卡控制传输 (Control)双向、复杂分 Setup、Data、Status 三阶段总线保留底线带宽确保控制命令畅通。枚举过程、配置设备帧/微帧与多设备通信全速/低速时间片大小为1ms称为一个帧 (Frame)。高速模式时间片大小为125μs称为一个微帧 (Microframe)。在每个帧内USB 主机会将所有待执行的传输排队并按优先级调度中断传输 等时传输 控制传输 批量传输为了不让控制传输被饿死总线会强行预留一部分时间给控制传输。普通 IN/OUT 事务非控制传输对于批量、中断、等时传输事务比控制传输简单通常只有三个阶段批量/中断需要握手等时无握手。IN 事务设备 → 主机主机发送 IN 令牌包。设备硬件自动将准备好的数据放到总线上数据包CPU 不参与逐位传输。主机返回 ACK等时传输无此步骤。OUT 事务主机 → 设备主机发送 OUT 令牌包。主机发送数据包。设备硬件接收后自动放入缓冲区并返回 ACK等时传输无此步骤。由于 USB 硬件自动完成数据包的收发单片机 CPU 无需逐位处理因此能跟上 480Mbps 的高速通信。USB2.0 真实吞吐量全速 (12 Mbps)每帧 1ms理论最大传输约 1500 字节除去协议开销实际有效载荷约1000 字节/帧即约 1 MB/s。高速 (480 Mbps)每微帧 125μs理论最大约 7500 字节/微帧除去协议开销实际有效载荷约3000 字节/微帧即约 24 MB/s~40 MB/s。通用 USB 驱动即插即用接口描述符中的bInterfaceClass和bInterfaceSubClass字段定义了设备所属的标准类如 HID 类、大容量存储类、音频类等。操作系统内置了这些类的通用驱动程序因此绝大多数符合标准的 USB 设备都能实现即插即用无需额外安装驱动。结语本文对 USB2.0 协议进行了较为全面的概述涵盖了物理层信号、速度协商、数据包结构、事务/传输概念、描述符体系、枚举流程以及四种传输类型。希望读者能建立起一个完整的知识框架。更深层次的细节如具体的标准请求、各描述符中每一位的含义、TinyUSB 协议栈实现等欢迎继续探索。如果有时间后续会写一篇关于开源 USB 协议栈TinyUSB的实现分析敬请期待。
http://www.zskr.cn/news/1388019.html

相关文章:

  • 6.3二叉树层序遍历
  • 无人机视角目标检测避坑指南:用YOLOv7训练VisDrone数据集时,我遇到的5个典型问题与解法
  • openstack+公有云
  • 如何绕过百度网盘限速:开源工具baidu-wangpan-parse完全指南
  • CentOS 7从VMWare搬到Hyper-V后卡在dracut?别慌,手把手教你重建initramfs搞定它
  • 盒须图底层原理与Matplotlib/Seaborn实战精讲
  • Python generator实战:用懒加载对抗大数据OOM
  • 【DeepSeek代码重构黄金法则】:20年架构师亲授5大高危代码异味识别与秒级修复方案
  • 杭州哪家AI广告片制作公司创意强
  • Tableau去重计数COUNTD实战:从界面操作到LOD精准控制
  • 安全设备篇——WAF
  • 2026年想要找到靠谱的大型亚克力鱼缸厂家 这份实用参考指南别错过
  • VScode拓展插件迁移
  • AI Agent成本优化实战:3分钟定位LLM API成本黑洞与系统化节流方案
  • 从AI编码工具到智能工作空间:Skaro 2.0如何重塑人机协作开发范式
  • 从IT系统到高压电机:绝缘监测双技术路线的工程实践
  • STL详解——stack与queue的介绍与使用
  • 告别轮询!用STM32CubeMX+HAL库玩转USART中断收发(附LED控制实战代码)
  • android kotlin Flow:distinctUntilChangedBy + stateIn 的坑
  • 一线观察发现:宝宝湿疹辅助改善的几个细节
  • 初次在Taotoken模型广场选型并成功调用新上线模型的步骤
  • 零基础做GEO 关键词覆盖?这份保姆级教程让你秒懂
  • PowerSetting极速下载优化方案全解析
  • 2025-2026年天津国际学校推荐:五大高性价比选择评测课程衔接案例市场份额 - 品牌推荐
  • 苏宁开放平台商品详情接口实战:多维度数据获取与结构化处理(附核心代码 + 避坑指南)
  • HAMi 源码阅读笔记 09:/bind 路由入口如何接收 kube-scheduler 的绑定请求
  • 对比测试:Claude Sonnet 4.6 vs GPT-5.5 vs DeepSeek V4
  • 微信小游戏19MB主包体积控制实战指南
  • Python TDD实战入门:从red-green-refactor到高覆盖率测试套件
  • 线程任务执行报错后,线程会不会挂掉,Java线程池