首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >USB总线和协议

USB总线和协议

原创
作者头像
程序员良许
发布2026-02-11 10:59:14
发布2026-02-11 10:59:14
120
举报

大家好,我是良许

在嵌入式开发中,USB可以说是我们最常打交道的接口之一了。

无论是调试设备、烧录程序,还是开发各种外设,USB都扮演着至关重要的角色。

今天我就来和大家深入聊聊USB总线和协议的那些事儿。

1. USB总线概述

1.1 USB的发展历程

USB技术从1996年诞生至今,已经经历了多个版本的迭代。

最初的USB 1.0速度只有1.5Mbps,到USB 1.1的12Mbps,再到USB 2.0的480Mbps,USB 3.0更是达到了5Gbps。

现在最新的USB 4.0甚至能达到40Gbps的惊人速度。

在我们嵌入式开发中,USB 2.0依然是应用最广泛的版本,因为它在速度、成本和功耗之间取得了很好的平衡。

1.2 USB的优势特点

USB之所以能够如此普及,主要得益于它的几个核心优势。

首先是即插即用(Plug and Play),设备连接后系统会自动识别并加载驱动,这对用户来说非常友好。

其次是热插拔(Hot Swap),不需要关机就能插拔设备,大大提高了使用便利性。

第三是供电能力,USB接口可以为外设提供5V电源,最大电流可达500mA(USB 2.0)或900mA(USB 3.0),这让很多小功率设备无需额外供电。

最后是统一的接口标准,一根线缆可以连接各种不同类型的设备。

1.3 USB的拓扑结构

USB采用的是主从架构,也就是Host-Device模式。

在一个USB系统中,只能有一个主机(Host),但可以连接多个设备(Device)。

主机负责管理整个总线,包括设备枚举、数据传输调度等。

通过USB Hub(集线器),一个主机最多可以连接127个设备。

这种星型拓扑结构最多支持5层Hub级联,但实际应用中很少会用到这么深的层级。

2. USB硬件接口

2.1 USB接口类型

USB接口经历了多次演进,我们常见的有Type-A、Type-B、Mini USB、Micro USB以及最新的Type-C。

Type-A是最常见的标准USB接口,通常用于主机端。

Type-B接口则多用于打印机等外设。

Mini USB和Micro USB曾经广泛应用于手机和小型设备,现在逐渐被Type-C取代。

Type-C接口最大的特点是正反可插,并且支持更高的功率传输和数据速率。

2.2 USB引脚定义

以USB 2.0的标准A型接口为例,它有4个引脚,从外到内分别是VCC(+5V电源)、D-(数据负)、D+(数据正)、GND(地)。其中D+和D-是一对差分信号线,用于数据传输。

USB采用差分信号的好处是抗干扰能力强,能够实现较长距离的可靠传输。

在实际PCB设计中,我们需要特别注意D+和D-的走线要等长,并且要做差分对处理,阻抗控制在90欧姆左右。

2.3 USB电气特性

USB 2.0定义了三种速度模式:低速(Low Speed)1.5Mbps、全速(Full Speed)12Mbps和高速(High Speed)480Mbps。

不同速度模式下,电气特性也有所不同。

低速和全速模式使用3.3V的信号电平,而高速模式使用400mV的差分电压。

在设备端,我们可以通过在D+或D-上串联一个1.5K欧姆的上拉电阻来标识设备的速度类型。

全速和高速设备在D+上拉,低速设备在D-上拉。

3. USB协议架构

3.1 USB协议分层

USB协议采用分层设计,从下到上分为物理层、协议层、功能层和应用层。

物理层负责电气信号的传输,包括编码、解码、位同步等。

协议层处理数据包的组装和解析,包括令牌包、数据包、握手包等。

功能层实现具体的USB功能,比如端点管理、数据缓冲等。

应用层则是具体的设备功能实现,比如USB鼠标、键盘、U盘等。

3.2 USB传输类型

USB定义了四种传输类型,分别适用于不同的应用场景。

控制传输用于设备配置和状态查询,所有USB设备都必须支持控制传输。

中断传输用于少量、实时性要求高的数据传输,比如鼠标、键盘。批量传输用于大量数据的可靠传输,但不保证实时性,U盘就是典型应用。

同步传输用于音视频等对实时性要求高但可以容忍少量错误的场景。

3.3 USB数据包结构

USB通信的基本单位是包。

一个完整的USB传输由多个包组成,包括令牌包、数据包和握手包。

令牌包由主机发出,用于指示传输的方向和目标端点。

数据包携带实际要传输的数据。

握手包用于确认传输状态,比如ACK表示成功接收,NAK表示设备暂时无法处理,STALL表示端点出错。

每个包都包含同步字段、PID(包标识符)、数据字段和CRC校验。

4. USB设备枚举过程

4.1 设备连接检测

当一个USB设备插入主机时,主机会通过检测D+或D-上的电平变化来发现新设备。

前面提到的1.5K上拉电阻就是关键,它会将D+或D-拉高,主机检测到这个变化后就知道有新设备连接了。

随后主机会等待至少100ms,让设备的电源稳定下来,这个过程叫做去抖动。

4.2 设备复位和地址分配

检测到新设备后,主机会发送复位信号,持续至少10ms。

复位后,设备进入默认状态,使用地址0进行通信。

接下来主机会通过控制传输读取设备描述符,了解设备的基本信息,比如厂商ID、产品ID、设备类型等。

然后主机会给设备分配一个唯一的地址(1-127之间),设备收到地址后就不再使用地址0了。

4.3 配置和驱动加载

获取设备地址后,主机会继续读取配置描述符、接口描述符和端点描述符,全面了解设备的功能和需求。

根据这些信息,操作系统会加载相应的驱动程序。

最后主机发送SET_CONFIGURATION命令,设备进入配置状态,开始正常工作。

整个枚举过程通常在几秒内完成,这就是我们插入U盘后很快就能使用的原因。

5. USB端点和管道

5.1 端点的概念

端点是USB设备中数据传输的终点,可以理解为设备内部的一个数据缓冲区。

每个端点都有一个编号(0-15)和方向(IN或OUT)。

IN端点表示数据从设备发送到主机,OUT端点表示数据从主机发送到设备。

端点0比较特殊,它是双向的,专门用于控制传输。

一个USB设备最多可以有32个端点(16个IN + 16个OUT),但实际应用中很少用这么多。

5.2 管道的建立

管道(Pipe)是主机和设备端点之间的逻辑连接。

当主机完成设备枚举后,就会根据端点描述符建立相应的管道。

管道分为流管道(Stream Pipe)和消息管道(Message Pipe)。

流管道用于批量、中断和同步传输,数据没有特定的结构。

消息管道用于控制传输,数据有明确的请求-响应结构。

5.3 端点配置示例

在STM32的HAL库中,配置USB端点的代码大致如下:

代码语言:c
复制
// 打开并配置端点
HAL_StatusTypeDef HAL_PCD_EP_Open(PCD_HandleTypeDef *hpcd, 
                                   uint8_t ep_addr, 
                                   uint16_t ep_mps, 
                                   uint8_t ep_type)
{
    HAL_StatusTypeDef ret = HAL_OK;
    PCD_EPTypeDef *ep;
    
    if ((ep_addr & 0x80U) == 0x80U) {
        ep = &hpcd->IN_ep[ep_addr & EP_ADDR_MSK];
        ep->is_in = 1U;
    } else {
        ep = &hpcd->OUT_ep[ep_addr & EP_ADDR_MSK];
        ep->is_in = 0U;
    }
    
    ep->num = ep_addr & EP_ADDR_MSK;
    ep->maxpacket = ep_mps;
    ep->type = ep_type;
    
    // 配置硬件寄存器
    // ...
    
    return ret;
}

// 端点数据发送
HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, 
                                       uint8_t ep_addr, 
                                       uint8_t *pBuf, 
                                       uint32_t len)
{
    PCD_EPTypeDef *ep;
    
    ep = &hpcd->IN_ep[ep_addr & EP_ADDR_MSK];
    ep->xfer_buff = pBuf;
    ep->xfer_len = len;
    ep->xfer_count = 0U;
    
    // 启动传输
    // ...
    
    return HAL_OK;
}

6. USB描述符详解

6.1 设备描述符

设备描述符是USB设备的"身份证",包含了设备的基本信息。

它的长度固定为18字节,包括USB版本号、设备类代码、厂商ID(VID)、产品ID(PID)、设备版本号等。

主机通过读取设备描述符来识别设备类型并加载相应驱动。

在嵌入式开发中,我们需要根据实际设备来定义这个描述符。

代码语言:c
复制
// USB设备描述符示例
const uint8_t USBD_DeviceDesc[USB_LEN_DEV_DESC] = {
    0x12,                       // bLength: 描述符长度
    USB_DESC_TYPE_DEVICE,       // bDescriptorType: 设备描述符类型
    0x00, 0x02,                 // bcdUSB: USB 2.0
    0x00,                       // bDeviceClass: 在接口描述符中定义
    0x00,                       // bDeviceSubClass
    0x00,                       // bDeviceProtocol
    USB_MAX_EP0_SIZE,           // bMaxPacketSize: 端点0最大包大小
    LOBYTE(USBD_VID),           // idVendor: 厂商ID低字节
    HIBYTE(USBD_VID),           // idVendor: 厂商ID高字节
    LOBYTE(USBD_PID),           // idProduct: 产品ID低字节
    HIBYTE(USBD_PID),           // idProduct: 产品ID高字节
    0x00, 0x02,                 // bcdDevice: 设备版本号
    USBD_IDX_MFC_STR,           // iManufacturer: 厂商字符串索引
    USBD_IDX_PRODUCT_STR,       // iProduct: 产品字符串索引
    USBD_IDX_SERIAL_STR,        // iSerialNumber: 序列号字符串索引
    USBD_MAX_NUM_CONFIGURATION  // bNumConfigurations: 配置数量
};

6.2 配置描述符

配置描述符定义了设备的工作配置,一个设备可以有多个配置,但同一时间只能使用一个。

配置描述符本身只有9字节,但它后面会跟着接口描述符和端点描述符,形成一个描述符集合。

配置描述符中包含了接口数量、配置值、供电方式(自供电或总线供电)、最大功耗等信息。

6.3 接口和端点描述符

接口描述符定义了设备的功能接口,一个配置可以包含多个接口。

比如一个USB复合设备可能同时包含HID接口和CDC接口。

接口描述符指定了接口类代码、子类代码和协议代码,这些信息帮助主机识别接口类型。

端点描述符则描述了每个端点的属性,包括端点地址、传输类型、最大包大小和轮询间隔等。

中断传输和同步传输需要指定轮询间隔,表示主机多久查询一次端点。

7. USB类驱动

7.1 HID类

HID是最常见的USB设备类之一,包括鼠标、键盘、游戏手柄等。

HID类的优势是操作系统都内置了HID驱动,无需安装额外驱动就能使用。

HID设备通过报告来传输数据,报告格式由报告描述符定义。

在嵌入式开发中,我们经常用HID类来实现自定义的数据传输,因为它简单方便。

7.2 CDC类

CDC主要用于串口通信。USB转串口模块就是典型的CDC设备。

CDC类使用两个接口:一个通信接口用于控制,一个数据接口用于数据传输。

通过CDC类,我们可以在PC上虚拟出一个COM口,就像使用传统串口一样方便。

这在调试嵌入式系统时非常有用。

7.3 MSC类

MSC用于U盘、移动硬盘等存储设备。

MSC类基于SCSI协议,支持读写扇区、查询容量等操作。

实现MSC类设备需要提供底层的存储介质访问接口,比如Flash、SD卡等。

在STM32中,我们可以使用内部Flash或外部SPI Flash来实现一个虚拟U盘。

代码语言:c
复制
// MSC类读扇区函数示例
int8_t STORAGE_Read(uint8_t lun, uint8_t *buf, 
                    uint32_t blk_addr, uint16_t blk_len)
{
    // 计算实际地址
    uint32_t addr = blk_addr * STORAGE_BLK_SIZ;
    
    // 从Flash读取数据
    for (uint16_t i = 0; i < blk_len; i++) {
        // 读取一个扇区
        memcpy(buf, (uint8_t*)(FLASH_BASE_ADDR + addr), 
               STORAGE_BLK_SIZ);
        buf += STORAGE_BLK_SIZ;
        addr += STORAGE_BLK_SIZ;
    }
    
    return 0;
}

// MSC类写扇区函数示例
int8_t STORAGE_Write(uint8_t lun, uint8_t *buf, 
                     uint32_t blk_addr, uint16_t blk_len)
{
    uint32_t addr = blk_addr * STORAGE_BLK_SIZ;
    
    // 解锁Flash
    HAL_FLASH_Unlock();
    
    for (uint16_t i = 0; i < blk_len; i++) {
        // 擦除扇区
        FLASH_EraseSector(addr);
        
        // 写入数据
        for (uint32_t j = 0; j < STORAGE_BLK_SIZ; j += 4) {
            HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 
                            addr + j, 
                            *(uint32_t*)(buf + j));
        }
        
        buf += STORAGE_BLK_SIZ;
        addr += STORAGE_BLK_SIZ;
    }
    
    // 锁定Flash
    HAL_FLASH_Lock();
    
    return 0;
}

8. USB OTG技术

8.1 OTG的概念

OTG是USB 2.0引入的一项技术,允许设备在主机和从机之间动态切换。

传统USB只能是主机连接设备,而OTG使得两个设备可以直接连接,并协商谁当主机。

比如手机既可以作为设备连接到电脑,也可以作为主机连接U盘或键盘。

OTG设备通过ID引脚来识别角色,ID引脚接地的一方作为主机。

8.2 HNP和SRP协议

OTG定义了两个重要协议:HNP(Host Negotiation Protocol,主机协商协议)和SRP(Session Request Protocol,会话请求协议)。

HNP允许两个OTG设备在连接后交换主机角色,比如手机给相机传完照片后,相机可以变成主机来控制手机。

SRP允许从设备请求主机启动会话,这在省电模式下很有用。

8.3 OTG在嵌入式中的应用

在嵌入式系统中,OTG功能非常实用。

比如一个手持设备,既需要连接PC进行数据传输和充电,又需要连接U盘读取文件。

使用OTG技术就能很好地满足这种需求。

STM32的许多型号都支持USB OTG,我们在设计产品时可以充分利用这个特性,提升产品的灵活性和用户体验。

9. USB调试技巧

9.1 硬件调试

USB硬件调试首先要检查电路连接是否正确,特别是D+和D-的走线。

使用示波器可以观察USB信号的波形,检查是否有过冲、振铃等问题。

如果设备无法被主机识别,可以测量上拉电阻是否正常,电源电压是否稳定。

另外要注意ESD防护,USB接口容易受到静电冲击,建议加装TVS管。

9.2 协议分析

软件调试方面,USB协议分析仪是必不可少的工具。

通过抓取USB通信数据包,我们可以清楚地看到枚举过程、描述符内容、数据传输细节等。

常用的USB协议分析软件有Wireshark、Ellisys、Beagle等。

对于简单的调试,Windows自带的USBView工具也很有用,可以查看设备描述符和当前状态。

9.3 常见问题排查

在实际开发中,经常遇到的问题包括设备无法枚举、数据传输错误、速度不达标等。

设备无法枚举通常是描述符配置错误或硬件连接问题。

数据传输错误可能是端点配置不对或缓冲区溢出。

速度不达标则需要检查时钟配置和DMA设置。

建议在开发初期就建立完善的日志系统,记录USB事件和错误信息,这对问题定位非常有帮助。

10. 总结

USB技术虽然看起来复杂,但只要掌握了基本原理和协议结构,在实际应用中就能游刃有余。

作为嵌入式工程师,我们不仅要会用USB,更要深入理解它的工作机制。

从硬件接口到协议栈,从设备枚举到数据传输,每个环节都值得我们仔细研究。

希望通过这篇文章,能帮助大家建立起对USB技术的系统认识,在今后的项目开发中少走弯路。

USB技术还在不断发展,Type-C和USB PD等新技术也值得我们持续关注。

只有不断学习,才能在技术的浪潮中保持竞争力。

更多编程学习资源

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. USB总线概述
    • 1.1 USB的发展历程
    • 1.2 USB的优势特点
    • 1.3 USB的拓扑结构
  • 2. USB硬件接口
    • 2.1 USB接口类型
    • 2.2 USB引脚定义
    • 2.3 USB电气特性
  • 3. USB协议架构
    • 3.1 USB协议分层
    • 3.2 USB传输类型
    • 3.3 USB数据包结构
  • 4. USB设备枚举过程
    • 4.1 设备连接检测
    • 4.2 设备复位和地址分配
    • 4.3 配置和驱动加载
  • 5. USB端点和管道
    • 5.1 端点的概念
    • 5.2 管道的建立
    • 5.3 端点配置示例
  • 6. USB描述符详解
    • 6.1 设备描述符
    • 6.2 配置描述符
    • 6.3 接口和端点描述符
  • 7. USB类驱动
    • 7.1 HID类
    • 7.2 CDC类
    • 7.3 MSC类
  • 8. USB OTG技术
    • 8.1 OTG的概念
    • 8.2 HNP和SRP协议
    • 8.3 OTG在嵌入式中的应用
  • 9. USB调试技巧
    • 9.1 硬件调试
    • 9.2 协议分析
    • 9.3 常见问题排查
  • 10. 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档