本文主要是介绍圈圈教你玩USB(第二版) 笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
第一章 USB概述及协议基础
USB的拓扑结构
USB是一种主从结构的系统。主机叫Host,从机叫Device(设备)
- 主机具有一个或多个USB主控制器(host controller)和根集线器(root hub)。主控制器主要负责数据处理,根集线器提供一个连接主控制器与设备之间的接口和通路。USB集线器(usb hub)作为一类特殊的USB设备,可以对原有USB口进行扩展以获的更多的USB口(不能扩展更多的带宽,带宽是共享一个USB主控制器的)。
- USB的数据交换只能在主机和设备之间,主机与主机、设备与设备之间不能之间互连和交换数据。
- USB OTG中,一个设备可以在从机和主机之间切换,这样可以实现设备与设备之间的连接。物理上通过ID识别线实现(MINI USB)
USB的插入检测机制
- 在USB集线器的每个下游端口的 D+ 和 D- 上,分别接了一个15KΩ的下拉电阻到地。而在USB设备端,在 D+ 或者 D- 上接了一个1.5KΩ的上拉电阻到3.3V的电源
- 全速设备和高速设备,1.5K上拉电阻接D+;低速设备,1.5K上拉电阻接D-
USB描述符及其之间的关系
描述符用于实现让USB主机知道设备的功能以及行为
- 设备描述符Device Descriptor:设备使用的USB协议版本号、设备类型、端点0的最大包大小、厂商ID(VID)和产品ID(PID)、设备版本号、厂商字符串索引、产品字符串索引、设备序列号索引、可能的配置数等
- 配置描述符Configuration Descriptor:配置所包含的接口数、配置的编号、供电方式、是否支持远程唤醒、电流的需求量等
- 接口描述符Interface Descriptor:接口的编号、接口的端点数、接口所使用的类、子类、协议等
- 端点描述符Endpoint Descriptor:端点号及方向、端点的传输类型、最大包长度、查询时间间隔等
- 字符串描述符String Descriptor:提供一些方便人们阅读的信息,它不是必需的
USB的包结构以及传输过程
- USB使用的是LSB在前的方式,即先出来的是最低位数据,接下来是次低位,最后是最高位MSB。
- USB总线数据传输以包为基本单位。包分成不同域,以同步域开始,紧跟包识别符PID,最终以包结束符EOP结束
- 令牌包:用于启动一次USB传输。主机发送一个令牌来通知哪个设备响应以及如何响应
- 输出令牌包:用于通知设备将要输出一个数据包
- 输入令牌包:用于通知设备返回一个数据包
- 建立令牌包:只用在控制传输中,和输出令牌包作用一样。区别在于它只能使用DATA0数据包,且只能发到设备的控制端点,并且设备必须接收,而输出令牌包无这些限制
- 帧起始令牌包:每帧(或微帧)开始以广播形式发送。
- 结构:同步域 | 8位包标识PID | 7位地址 | 4位端点号 | 5位CRC5校验 | EOP
- 数据包:用于传输数据
- 结构:同步域 | 8位包标识PID | 字节0 | 字节1 | ... | 字节N | 16位CRC16校验 | EOP
- 握手包:用于表示一个传输是否被对方确认
- 结构:同步域 | 包标识PID | EOP
- 特殊包:特殊场合用的包。令牌包PRE、SPLIP、PING;握手包ERR
USB的传输类型
- 批量传输:包含批量输出事务、批量输入事务、PING事务;用于在数据量大、对实时性要求不高的场合。如USB打印机,扫描仪,大容量存储设备
- 中断传输:主机保证在不大于设备查询间隔时间内安排一次传输;用于数据量不大,对时间要求较严格的设备。如HID中的鼠标,键盘等
- 等时传输:用于数据量大,对实时性要求高的场合,不保证数据正确性。如音/视频设备
- 控制传输:保证数据传输过程中数据的完整性
第三章 USB鼠标的实现
USB标准请求
- USB协议定义了一个8字节的标准设备请求,主要用在设备的枚举过程中。这8字节的数据是在控制传输的建立过程通过默认控制端点0发出的。在这8字节的数据中,包含了数据过程所需要传输 数据传输的方向、长度以及数据类型等信息。正是由于8字节标准请求的原因,USB协议规定,端点0的最大包长度至少为8字节。也就是说,任何一个USB设备都能够(而且必须要)接收8字节的标准请求
- 数据结构
- 字节0【bmRequestType 】:请求的特性
- D7:数据传输方向。0=主机到设备 ,1=设备到主机
- D6-5:请求的类型。0=标准,1=类,2=厂商,3=保留
- D4-0:请求的接收者。0=设备,1=接口,2=端点,3=其他,4-31=保留
- 字节1【bRequest】:请求代码
- 0:GET_STATUS
- 1:CLEAR_FEATURE
- 3:SET_FEATURE
- 5:SET_ADDRESS(设置地址)主机请求设备使用指定地址的请求,指定的地址就包含在8字节数据中的wValue字段中
- 6:GET_DESCRIPTOR(获取描述符)主机通过发送获取描述符请求获取设备的各种描述符,从而可以获知设备类型、端点情况等信息
- 7:SET_DESCRIPTOR
- 8:GET_CONFIGURATION
- 9:SET_CONFIGURATION(设置配置)与设置地址请求类型,wValue字段标识被指的值
- 10:GET_INTERFACE
- 11:SET_INTERFACE
- 12:SYNCH_FRAME
- 字节2-3【wValue】:意义由请求决定
- 字节4-5【wIndex】:意义由请求决定
- 字节6-7【wLength】:数据过程(如果有)所需要传输的字节数
- 字节0【bmRequestType 】:请求的特性
设备描述符
- 字节0【bLength】:该描述符的长度(18)字节
- 表示该描述符的长度。设备描述符的长度为18字节,即0x12
- 字节1【bDescriptorType】:该描述符类型(设备描述符为0x01)
- 设备描述符DEVICE:1
- 配置描述符CONFIGURATION:2
- 字符串描述符STRING:3
- 接口描述符INTERFACE:4
- 端点描述符ENDPOINT:5
- 字节2-3【bcdUSB】:本设备所使用的USB协议版本
- 用BCD码表示。USB2.0=0x0200,USB1.1=0x0110
- 字节4【bDeviceClass】:类代码
- 由USB协会规定,通常为0,0xFF表示厂商自定义的设备类
- 字节5【bDeviceSubClass】:子类代码
- 由USB协会规定
- 字节6【bDeviceProtocol】:设备所使用的协议
- 字节7【bMaxPackeSize】:端点0最大包长
- 取值可以为8、16、32、64
- 字节8【idVender】:厂商ID,由USB协会分配
- 字节10【idProduct】:产品ID,由厂商分配
- 字节12【bcdDevice】:设备版本号,同一产品升级后,用于区分
- 字节14【iManufacturer】:描述厂商的字符串的索引
- 字节15【iProduct】:描述产品的字符串的索引。插入USB设备后,系统右下角弹出的设备信息
- 字节16【iSerialNumber】:产品序列号字符串的索引
- 字节17【bNumConfigurations】:可能的配置数
设置地址请求的处理
- 每个USB设备都具有一个唯一的设备地址,这个地址是主机在设置地址请求时分配给设备的。设备在收到设置地址请求后,应该返回一个0长度的状态数据包(因为设置地址请求是没有数据过程的),然后等待主机确认这个数据包(即用ACK应答设备)。设备在正确接收到状态数据包的ACK之后,就开始使用新的设备地址了
配置描述符集合
- 配置描述符(每个USB设备至少都要有一个配置描述符,在设备描述符中规定了该设备有多少种配置,每种配置都有一个描述符。)
- 字节0【bLength】:该描述符的长度(9字节)。标准的USB配置描述符的长度为9字节
- 字节1【bDescriptorType】:描述符类型(配置描述符为0x02)
- 字节2-3【wTotalLength】:配置描述符集合总长度。整个配置描述符集合的总长度,包括配置描述符、接口描述符、类特殊描述符(如果有)和端点描述符
- 字节4【bNumInterfaces】:该配置所支持的接口数。通产设备只有一个接口(鼠标),而符合设备具有多个接口(音频设备)
- 字节5【bConfigurationValue】:该配置的值。通常一个USB设备支持多个配置,以此表示激活的配置项
- 字节6【iConfiguration】:描述该配置的字符串的索引值
- 字节7【bmAttributes】:该设备的属性
- D7:保留,必须为1
- D6:供电方式。1表示设备是自供电的,0表示设备是总线供电的
- D5:是否支持远程唤醒。1支持
- D4-0:保留,设置为0
- 字节8【bMaxPower】:设备所需要的电流(单位为2mA)
- 接口描述符(接口描述符不能单独返回,必须附着在配置描述符后一并返回)
- 字节0【bLength】:该描述符长度(9字节)
- 字节1【bDescriptorType】:描述符类型(接口描述符为0x04)
- 字节2【bInterfaceNumber】:该接口的编号(从0开始)
- 字节3【bAlternateSetting】:该接口的备用编号
- 字节4【bNumEndpoints】:该接口所使用的端点数
- 字节5【bInterfaceClass】:该接口所使用的类
- 字节6【bInterfaceSubClass】:该接口所使用的子类
- 字节7【bInterfaceProtocol】:该接口所使用的协议
- 字节8【iInterface】:描述该接口的字符串的索引值
- 端点描述符(端点描述符不能单独返回,必须附着在配置描述符后一并返回)
- 字节0【bLength】:该描述符的长度(7字节)
- 字节1【bDescriptorType】:描述符的类型(端点描述符为0x05)
- 字节2【bEndpointAddress】:该端点的地址
- D7:该端点的传输方向,1输入,0输出
- D6-4:保留,设为0
- D3-0:端点号
- 字节3【bmAttributes】:该端点的属性
- D1-0:该端点的传输类型,0控制传输,1等时传输,2批量传输,3中断传输
- 等时传输时
- D3-2:同步的类型,0无同步,1异步,2适配,3同步
- D5-4:表示用途,0数据端点,1反馈端点,2暗含反馈的数据端点,3保留
- D7-6:保留
- 非等时传输时,D7-2为保留值,设为0
- 字节4-5【wMaxPackeSize】:该端点支持的最大包长度
- 全速和低速模式:D10-0表示最大包长,其他位保留为0
- 高速模式:D12-D11为每帧附加的传输次数
- 字节6【bInterval】:端点的查询时间
- 中断端点时:表示查询的帧间隔数
- 等时传输以及高速模式的中断、批量传输:参看USB协议
- HID描述符(HID类设备在配置描述符中还需要一个HID描述符,它是一个类描述符,应该跟在接口描述符后面)
- 字节0【bLength】:该描述符的长度;长度与下级描述符的个数有关
- 字节1【bDescriptorType】:描述符类型(HID描述符为0x21)
- 字节2-3【bcdHID】:HID协议的版本
- 字节4【bCountyCode】:国家代码(美式键盘,代码33,即0x21)
- 字节5【bNumDescriptors】:下级描述符的数量;至少是1,下级描述符可以是报告描述符或物理描述符
- 字节6【bDescriptorType】:下级描述符的类型;报告描述符0x22,物理描述符0x23
- 字节7-8【wDescriptorLength】:下级描述符的长度
- 字节9【bDescriptorType】:下级描述符的类型(可选)
- 字节10-11【wDescriptorLength】:下级描述符的长度(可选)
- .....
- 语言ID描述符
- 字节0【bLength】:该描述符的长度
- 字节1【bDescriptorType】:描述符的类型(字符串为0x03)
- 字节2-3【wLANGID[0]】:语言ID号0
- ...
- 字节2*n+2【wLANGID[n]】:语言IDn
- 字符串描述符
- 字节0【bLength】:该描述符的长度
- 字节1【bDescriptorType】:描述符的类型(字符串为0x03)
- 字节2-【bString】:UNICODE编码的字符串
- 报告描述符
- 报告描述符,用于描述一个报告的结构以及该报告里面的数据的用途。
- USB HID设备是通过报告(report)来传送数据的,报告有输入报告和输出报告
- 输入报告:是USB设备发送给主机的,例如键盘将按键数据返回给计算机等
- 输出报告:是主机发送给USB设备的,例如键盘上数字键盘锁定灯和大写字母锁定灯的控制等
- 结构(由条目item组成)
- HID协议规定了两种条目:短条目和长条目,仅介绍短条目
- 字节7-4【bTag】:该条目的功能,参看HID协议及HID用途表
- 字节3-2【bType】:表示条目的类型,0主条目(main),1全局条目(global),2局部条目(local),3保留
- 主条目共有5个:Input(输入),Output(输出),Feature(特性),Collection(集合),EndCollection(关集合):用于定义或分组报告的数据域
- 字节1-0【bSize】:表示后面所跟数据的字节数
- 报告的结构主要由报告的字段的长度size、数量count、属性(输入/输出)等决定,报告的用途由HID用途表文档规定。为了方便管理和归类,将用途usage分成了很多用途页usage page;
第五章 用户自定义的USB HID设备的访问
访问HID设备时所用到的相关的函数
- 上位机开发都是调用一些API函数,原型和在MSDN上查找。
- 函数名中包含Hid字样的函数属于hid.lib,包含Setup字样的属于setupapi.lib。这些函数的声明都是标准的C函数格式,因此在C++文件中引用头文件时加上 extern "C",否则会链接不上
- 获取HID设备的接口类GUID的函数
- 原型 HidD_GetHidGuid , 获取HID设备的接口类GUIDI,GUID是一个128位(16字节)的整数,分成不同的段用结构体来保存
- 获取指定类的所有设备信息集合的函数
- 原型 HidDiGetClassDevs,返回ClassGuid指定的所有设备的一个信息集合的句柄;使用完毕后,需要调用 SetupDiDestroyDeviceInfoList 去销毁
- 从设备信息集合中获取一个设备接口信息的函数
- 原型 SetupDiEnumDeviceInterfaces ,从设备信息集合中获取某个设备接口信息
- 获取指定设备接口详细信息的函数
- 原型 SetupDiGetDeviceInterfaceDetail,获取一个指定设备接口的详细信息,例如设备的路径(设备接口名)
- 打开设备的函数
- 原型 CreateFile ,打开指定的设备并返回一个指向该设备的句柄;可以使用该函数的返回句柄来获取设备的属性,以及进行读/写操作
- 获取HID设备属性的函数
- 原型 HidD_GetAttributes ,获取指定设备的属性,例如VID、PID、设备版本号等
- 从设备读取数据的函数
- 原型 ReadFile ,从指定的设备读取数据。对于HID设备,使用该函数只能从中断端点获取报告数据,如果要从控制端点获取报告,则使用函数 HidD_GetInputReport
- 往设备写数据的函数
- 原型 WriteFile ,把指定的数据发送到指定的设备。对于HID设备,使用该函数只能从中断端点发送报告,如果要从控制端点发送报告,则使用函数 HidD_SetOutputReport
- 通过控制端点 0 读取报告的函数
- 原型 HidD_GetInputReport ,调用后驱动程序将发送获取报告的类输入请求,设备在数据阶段通过控制端点0返回报告。若设备在规定时间内未返回报告,函数将超时返回
- 通过控制端点 0 发送报告的函数
- 原型 HidD_SetOutputReport , 调用后驱动程序将发送设置报告的类输出请求,设备在数据阶段通过控制端点0返回报告。若设备在规定时间内未返回报告,函数将超时返回
- 关闭句柄的函数
- 原型 ColseHandle , 关闭已打开的句柄
访问USB HID设备的上位机软件的实现
- 上位机程序编写的思路
- 1、使用HidD_GetHidGuid函数获取HID设备的接口类GUID
- 2、使用SetupDiGetClassDevs函数获取HID类中所有设备的信息集合
- 3、在该设备信息集合中,使用SetupDiEnumDeviceInterfaces函数去获取一个设备的信息。如果调用函数时返回失败,则说明已经到了设备集合的末尾,后面已经没有设备了,此时应该退出查找
- 4、使用SetupDiGetDeviceInterfaceDetail函数获取某个设备的详细信息。要获取某个设备的详细信息,SetupDiGetDeviceInterfaceDetail必须调用两次。第一次调用是为了得到保存设备详细信息需要多大的缓冲区,第二次调用才是真正的获取设备详细信息
- 5、获取设备的详细信息后,就可以使用CreateFile函数打开指定的设备了
- 6、打开设备后,再使用HidD_GetAttributes函数获取设备的属性,在属性中包含了VID、PID以及产品版本号等信息。然后再比较VID、PID以及产品版本号是否和所指定的一只。如果一致,则退出查找,否则切换到下一个设备,然后重复步骤3-6
第六章 USB转串口
USB转出串口的实现方法
第一种:使用用户自定义USB设备,然后开发其驱动程序,由驱动程序生成串口
第二种:使用USB协议规定的CDC类中的抽象控制模型子类中的AT命令协议
使用第一种方法需要用户自行开发驱动程序,比较麻烦,但是灵活性较强;使用第二种方法不需要用户开发驱动程序,只需要提供一个安装驱动的inf文件即可,但是灵活性不强,会受到一些限制
第八章 U盘
类特殊请求
在USB大容量存储设备的Bulk Only Transport协议中,规定了两个类特殊请求:Bulk Only Mass Storage Reset 和 Get Max LUN。前者是复位到命令状态的请求,后者是获取最大逻辑单元请求
Get Max LUN 是发送到接口的类输入请求
Bulk-Only Mass Storage Reset请求是通知设备接下来的批量端点输出数据为命令块封装包CBW(command bloick wrapper)
仅批量传输协议的数据流模型
- 协议规定了数据传输的结构和过程,共分成三个阶段:命令阶段、数据阶段和状态阶段。命令阶段由主机通过批量端点发送一个CBW的结构,在CBW中定义了要操作的命令以及传输数据的方向和数量。数据阶段的传输方向由命令阶段决定,而状态阶段则总是由设备返回该命令完成的状态
- 命令块封包CBW(command bloick wrapper)的结构(共31字节)
- 字节0-3【dCBWSignature】:CBW的标志。固定为字符串“USBC”
- 字节4-7【dCBWTag】:CBW的标签,由主机分配,设备在完成该命令返回状态时,需要在CSW(命令状态封包)中的dCSWTag字段中填入命令dCBWTag
- 字节8-11【dCBWDataTransferLength】:需要在数据阶段传输数据的字节数
- 字节12【bmCBWFlags】:最高位D7表示数据传输方向,0表示从主机到设备。其他位为0
- 字节13
- D7-4:保留,值为0
- D3-0【bCBWLUN】:目标逻辑单元的编号。当有多个逻辑单元时,使用该字段来区分不同的目标单元。
- 字节14
- D7-5:保留,值为0
- D4-0【bCBWCBLength】:CBWCB的长度
- 字节15-30【CBWCB】:需要执行的命令,由选择的子类决定使用哪些命令
- 命令状态封包CSW(Command Status Wrapper)的结构
- 字节0-3【dCSWSingnature】:CSW的标志,固定为字符串“USBS”
- 字节4-7【dCSWTag】:命令状态封包的标签,其值为CBW中的dCBWTag,响应哪个CBW就设置为哪个CBW的dCBWTag
- 字节8-11【dCSWDataResidue】:命令完成时的剩余字节数。表示实际完成传输的字节数与主机在CBW中设置的长度dCBWDataTransferLength之间的差值
- 字节12【bCSWStatus】:命令执行的状态。0表示命令成功执行,1表示命令执行失败,2表示阶段错误
未完。。。
第九章 自定义USB设备及驱动开发
用户自定义USB设备
- 在USB设备类中,规定类代码 0xFF 为用户自定义USB设备。因此只要在设备描述符中将设备类(bDeviceClass字段)改为0xFF,即可实现用户自定义的USB设备
驱动程序开发简介
使用 WDM 开发windows系统驱动
这篇关于圈圈教你玩USB(第二版) 笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!