USB基础知识扫盲
前言
本文将从USB的插入检测、身份识别、数据传输三个方面对USB通讯整个过程扫盲,其中有些知识点的详细信息会放在文章最下面的附录中供查看,从而保证文章的整体简洁。在进入主题之前,首先了解一下USB的一些基础知识
USB的分类
-
按接口类型分
- 控制器/主机(controller/host)
- 设备(peripheral)
- OTG(on-the-go),通过id线确定作为主机还是作为设备
-
按照USB速度分
- 低速(low speed)
- 全速(full speed)
- 高速(high speed)
一、USB插入检测
USB接口一般是4根线,VCC GND DM(D-) DP(D+)
- A:USB是如何检测到设备插入的
-
Q:主机端将DM DP接入下拉电阻;设备端根据不同速度,将DM DP的某一根接上拉电阻,插入时通过压差即可判定是否插入
- 低速设备:在DM线上接入上拉
- 全速设备:在DP线上接入上拉
- 高速设备:在DP线上接入上拉,在主机对设备进行复位后进一步的确认,详情
二、USB身份识别
对于插入的USB设备,主机需要发送比较短的请求来确认设备的身份、类型、速度等信息,这个过程称之为枚举
2.1 描述符
设备的“身份”信息存储在描述符中。每个USB设备中都有如下描述符。需要注意的是
- 一个USB设备只能有一个设备描述符
- 其他描述符都可以有多个,对应复合设备,例如USB键鼠
-
设备描述符(device description)
-
配置描述符(config description)
-
接口描述符 (interface description)
- 端点描述符 (endpoint description)
- 端点描述符
-
接口描述符
- 端点描述符
-
-
配置描述符
-
接口描述符
- 端点描述符
-
-
2.2 枚举过程
- 等待稳定:主机通过电平差检测到设备,等待100ms让设备电平趋于稳定
- 首次复位:HUB发起复位,让设备进入初始的地址0模式
-
首次查询设备描述符:GET_DESCRIPTOR 主机查询设备描述符,只要前8字节 ==>
80 06 01 00 00 00 12 00
- 二次复位:在接收到设备描述符前8个字节后,再次重启设备
-
设置地址:SET_ADDRESS 主机下发设置地址命令,设备获取新地址 ==>
00 05 01 00 00 00 00 00
-
二次查询设备描述符:GET_DEVICE_DESCRPTOR获取整个18字节的设备描述符 ==>
80 06 01 00 00 00 12 00
-
获取配置描述符:GET_CONFIGURATION 获取9字节配置描述符 ==>
80 06 02 00 00 00 09 00
- 完成配置:SET_CONFIGURATION,
2.3 描述符详解
搞懂了描述符,调好了枚举,就完成了USB通讯的一大半,下面就对描述符进行详细的探索
设备描述符 | 18 | 配置描述符 | 9 | 接口描述符 | 9 | 终点描述符 | 7 |
bLength | 1 | bLength | 1 | bLength | 1 | bLength | 1 |
bDescriptorType | 1 | bDescriptorType | 1 | bDescriptorType | 1 | bDescriptorType | 1 |
bcdUSB | 2 | wTotalLength | 2 | bNumInterfaces | 1 | bEndpointAddress | 1 |
bDeviceClass | 1 | bNumInterfaces | 1 | bAlterateSetting | 1 | bmAttributes | 1 |
bDeviceSubClass | 1 | bConfigurationValue | 1 | bNumEndpoints | 1 | wMaxPacketSize | 2 |
bDeviceProtocol | 1 | iConfiguration | 1 | bInterfaceClass | 1 | bInterval | 1 |
bMaxPackeerSize0 | 1 | bmAttributes | 1 | bInterSubfaceClass | 1 | ||
idVendor | 2 | bMaxPower | 1 | bInterfaceProtocol | 1 | ||
idProduct | 2 | iInterface | 1 | ||||
bcdDevice | 2 | ||||||
iManufacturer | 1 | ||||||
iProduct | 1 | ||||||
iSerialNumber | 1 | ||||||
iNumConfigurations | 1 |
附录
F1、USB通讯协议详解
学习协议我个人觉得应该还是从整体流程向下详细到具体协议,上来就看最基本单元/协议会让人一头雾水,这段附录的大体流程也是如此。传输概念->传输流程->基础传输单元->传输流程控制->
F1.1 USB通讯概念
- 在USB的通讯中,有传输(transfer),事务(Transaction),包(packet)三级。包是最基础的传输单元,与TCP/IP协议中的MAC层协议作用相同。
- 在一次传输中,由多次事务组成,每次的事务又由多个包组成
- 与众多协议相同,较高级别的协议的报文是基于/内嵌在低级协议的报文当中的,在USB中也不例外,例如,包中预留了DATA位,其目的就是填写报文
-
传输(transfer): 控制(control)、中断(interrupt)、批量(bulk),同步(Isochronous)
-
事务(transaction):传输方向、流程控制
-
包(packet):
- 令牌(Token):IN、OUT、SETUP、SOF,前导包,用于确认本次事务的方向、目的等,是每次事务必须要有的包。
- 数据(data):DATA0、DATA1、DATA2、MDATA,夹带数据
- 握手(handshake):ACK、NAK、STALL、NYSET,作为应答、状态回复
-
-
F1.2 一次完整传输过程
下图为一次完整的获取设备描述符的过程
-
传输:获取设备描述符
-
设置事务:主机向设备下发 ->
80 06 00 01 00 00 40 00
- 令牌包 --> :SETUP类型
- 数据包 --> :由于令牌是SETUP类型,所以主机紧接着下发数据包,DATA0类型
- 握手包 <-- :设备收到数据包,回复应答
-
输入事务:设备向主机上发 ->
12 01 00 01 DC 00 00 10 71 04 F0 FF 00 01 00 00
- 令牌包 --> :IN类型,主机准备好,设备可以上发数据
- 数据包 <-- :DATA1类型:DATA0与DATA1在发送数据时需要交替,DATA中夹带设备描述符
- 握手包 --> :主机收到回复,向设备应答
-
输出事务:无
- 令牌包 --> :OUT类型
- 数据包 --> :DATA0类型,再一次切换为DATA0,没有需要发送给设备的
- 握手包 <-- :设备收到数据包,回复应答
-
F1.3 基础传输单元:传输基本单元 -- 包(packet)
不同包的组成部分如下表所示,数字为每一个字段所代表位数。结合上面的完整流程可以更输入的理解包的概念。
Token 令牌包 | SYNC | PID | ADDR | ENDP | CRC5 |
8/32 | 8 | 7 | 4 | 5 | |
Data 数据包 | SYNC | PID | DATA | CRC16 | |
8/32 | 8 | 0-1023 | 16 | ||
Handshake 握手包 | SYNC | PID | |||
8/32 | 8 | ||||
SOF 起始包 | SYNC | Frame Number | CRC5 | ||
8/32 | 11 | 5 |
- SYNC字段:为固定,FS/LS为8位,HS为32位
- PID字段:决定了该包的类型,例如Token包的SETUP,IN,OUT,数据包的DATA0,DATA1,握手包的ACK,NAK等
- ADDR字段/ENDP字段:前面说过,一个设备只有一个设备描述符和多个终点描述符,这两个字段就可以在多个设备中准确的找到目标设备以及设备中的目标终点,类似TCP/IP中的IP与PORT
-
SOF包:在Token包之前发送,Frame Number为主机内部自增序列号,不断循环
- FS/LS:每1ms发送一次
- HS:每125us发送一次
- EOF:EOF不属于字段或者数据,而是在每个包最后加上2个数据位宽的SE0信号(DM DP都为低),用于表示包的结束
F1.4 传输流程控制 -- 事务(Transaction)
在分析那个设备描述符的流程时会发现,必须在每次事务的开始,要使用Token(令牌包)确认该次事务的传输“基调”。原因在于,USB是半双工,DM DP并非独立工作,所以采用"三段式"通讯,保证总线不会冲突
- 令牌包(Token):主机发起
- 数据包(Data):根据令牌包中的方向,由主机发起(OUT/SETUP)或者设备发起(IN)数据包
- 握手包(Handshake):数据包接收方发起握手包,返回状态,包含ACK NAK STALL状态等。
- 不能中断:USB的每次事务是“堵塞”,必须该事务完整后才能做其他事务
- 状态判断:之前的举例当中,每次传输都是成功的回复了ACK,可以看到,上图中,事务发起了三次IN的Token包,设备才发送了下一帧数据。不会因为设备忙就退出事务。
- 流程控制: 下发/接收数据、控制进出方向、确认回复
F1.5 传输(Transfer)
终于说到顶层部分了,有点耐心马上就说完了,首先传输分为以下几种方式,对应不同需求
传输方式 | 令牌包 | 数据包 | 握手包 | 特点及医用 | 特殊性 |
控制 | ✔ | ✔ | ✔ | 枚举过程中使用 | 多次事务,简单的下发命令+回复需要2次事务6个包 |
批量 | ✔ | ✔ | ✔ | 数据量大,要求准确性,常用于U盘等设备 | 多次事务,DATA0/DATA1交替 |
中断 | ✔ | ✔ | ✔ | 数据量小,要求固定频率,常用于USB键盘鼠标 | 与批量相似,主机会按照固定间隔向设备发令牌包 |
同步 | ✔ | ✔ | × | 数据量大,要求低延迟,常用于USB摄像头、声卡 | 无握手包的批量传输 |
F1.5.1 控制传输(Control Transfer)
控制传输常用于USB枚举阶段,读取设备描述符、设置地址等少量频繁的工作。上面举的获取完整设备描述符就是一个非常典型的控制传输过程,分为以下几个阶段
- 建立阶段:SETUP事务,发下指定命令,设备应答
- 数据过程:设备等待主机发起IN事务,并将数据上传,主机回复应答
- 状态过程:确认完成,这个阶段传输方向必须与数据传输阶段相反,数据传输是IN,确认就应该用OUT。
F1.5.2 批量传输(Bulk Transfer)
- 区分方向,有批量读与批量写,不能同时执行
- 一次批量传输由多次事务组成
- 传输过程中数据包类型DATA0/DATA1不断切换,如果发生错误,则主机会要求重传
F1.5.3 中断传输(Interrupt Transfer)
中断传输在数据传输方面与批量传输相同,重点在于,主机对中断传输设备必须保持固定扫描间隔,在设备描述符中标注了该扫面间隔。USB总线即使被其他设备批量传输占用,也会在完成中间抽空向中断传输设备下发令牌
F1.5.4 同步传输(Isochronous Transfer)
因为要求实时性,对数据准确性不是很敏感,所以同步传输中的事务是没有握手包的,如下图
F2 USB通讯请求 Request
命令 | bmRequestType | bRequest | wValue | wIndex | wLength | Data |
GET_STATUS | 0 | 无 | ||||
CLEAR_FEATURE | 1 | 无 | ||||
SET_FEATURE | 3 | |||||
SET_ADDRESS | 5 | 设备地址 | 0 | 0 | 无 | |
GET_DESCRIPTOR | 80H | 6 | 描述符类型+索引 | 0/languageID | 描述符长度 | 描述符 |
SET_DESCRIPTOR | 7 | |||||
GET_CONFIGURATION | 8 | |||||
SET_CONFIGURATION | 00H | 9 | 00xxH 配置值,高字节固定00 | 0 | 0 | 无 |
GET_INTERFACE | 10 | |||||
SET_INTERFACE | 11 | |||||
GET_STATUS | 12 |
bmRequest字段
D7 | D6-D5 | D4-D0 |
0:OUT 1:IN |
00:标准请求 01:类请求 10:用户自定义 |
00:设备 01:接 02:端 03:其他 |
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。