https://space.bilibili.com/489340606/channel/collectiondetail?sid=896957
以下图片来自于沁恒微电子蔡亮工程师的讲课,对USB开发入门很有好处。
1. USB设备的组成结构
一个设备可以有多个配置,但同一时刻只能有一个生效。一个配置可以有多个接口,一个接口可以有多个端点。
1. 主机枚举设备----获取设备描述符
//主机请求-获取设备描述符
0x80,0x06,0x00,0x01,0x00,0x00,0x12,0x00
//设备描述符
0x12, // bLength,设备描述符长度,固定为18字节
0x01, //bDescriptorType,描述符类型为设备描述符
0x10,0x01, //bcdUSB,USB协议的版本号,小端格式,BCD形式,0x0110,即V1.1版本
0x00, //bDeviceClass,设备类,如果值为0,是告知主机设备类在接口描述符中
0x00, //bDeviceSubClass,设备子类,取决于bDeviceClass,bDeviceClass为0时bDeviceSubClass必须为0
0x00, //bDeviceProtocol,设备用的协议,取决于bDeviceSubClass,bDeviceSubClass为0时必须为0
0x08, //bMaxPacketSize0,默认控制传输端点0的单次传输数据包净大小,单位是byte;低速设备只能是8,全速可以是8,16,32,64
0x86,0x1a, //idVendor,制造商的ID号,向USB联盟申请
0xe1,0xe6, //idProduct,产品ID号,制造商自己定
0x00,0x01, //bcdDevice,该USB设备的固件版本号,制造商自己定
0x01, //iManufacturer,制造商信息在字符串描述符中的序号;当非0值时,主机才会去请求。如果没有字符串描述符,该值直接填0即可
0x02, //iProduct,制造商信息在字符串描述符中的序号;当非0值时,主机才会去请求。如果没有字符串描述符,该值直接填0即可
0x00, //iSerialNumber,产品序列号在字符串描述符中的序号;当非0值时,主机才会去请求。如果没有字符串描述符,该值直接填0即可
0x01 // bNumConfi gurations,本设备支持的配置数量。最小为1,为1时说明只支持一种配置。
2. 主机枚举设备----设置设备地址
//主机请求----设置设备地址
0x00.0x05.0x02.0x00.0x00,0x00.0x00.0x00
USB设备插入主机后,默认地址都是0,主机给设备设置地址的范围是 1~127。
在设置新地址后,通讯均使用新地址进行。
当发生USB总线复位或者设备拔出再插入,地址自动回到默认地址0。
3. 主机枚举设备----获取配置描述符
配置描述符(Configuration Descriptor)规定了设备的特征和能力。一般单个配置已经足够了,但在驱动程序的支持下,带有多应用或多电源选择的设备可支持多重配置。且每次只有一个配置被激活。每个配置需要一个配置描述符,其中含有关于设备电源使用及所支持接口数的信息。
主机通过发送Get Descriptor请求,取得配置描述符及其附属描述符。
- bLength 以字节为单位的描述符大小(0x09)。
- bDescriptorType 配置描述符类型,为CONFIGURATION (0x02)。
- wTotalLength 配置返回的数据总长度。包括该配置返回的所有描述符(配置、接口、端点、和专用的类型或者专用的厂商描述符)的总长度。
- bNumInterfaces 这个配置支持的接口数量,最小值为0x01。
- bConfigurationValue 确认Get Configuration 和Set Configuration请求的配置,且必须为0x01或者更高值。取值为0的Set Configuration请求会使设备进入未配置状态(Not Configured state)
- iConfiguration 描述这个配置的字符串描述符索引。若没有字符串描述符,这个字段的值为0。
- bmAttributes 配置特性。
Bit 7:USB1.0协议中表示总线供电(Bus Powered),设置Bit 7 = 1表示由总线供电(Bus Powered)。其他协议该位保留(Reserved),必须设置为1。
Bit 6:自供电(Self-powered)。如果Bits 6 = 1,设备是自供电(Self-powered)的。
Bit 5 :远程唤醒(Remote Wakeup)。如果Bit 5 = 1,设备支持远程唤醒。
Bits 4…0:未使用,保留,必须为0。
- bMaxPower当设备完全运行时,特定配置的USB设备从总线取得的最大功耗。
对于usb2.0,bMaxPower 以2mA为单位。如果设备要求200ma,则bMaxPower = 100 (0x64)。设备可请求的最大总线电流500mA。
对于增强型超高速(Enhanced SuperSpeed)设备,bMaxPower以8mA为单位。如果设备要求200ma,则bMaxPower = 25(0x19)。设备可请求的最大总线电流900mA。
当设备和主机支持USB Power Delivery Rev. 2.0,主机可以从PD Class Specific Descriptors检索设备的电源需求。
//主机请求一获取配置描述符
0x80,0x06,0x00,0x02,0x00,0x00,0x09,0x00
//配置描述符实例
0x09, // bLength,,描述符长度
0x04, // bDescriptorType,描述符类型为配置描述符
0x22,0x00, // wTotalLength,包含配置描述符、接口描述符、hid类描述符、端点描述符的集合的总长度。主机获取该值后会再次发起请求,重新获取完整的配置描述符。主机第一次请求都是只请求9个字节的配置描述符,第二次请求才是包含配置描述符、接口描述符、hid类描述符、端点描述符的集合。两次请求命令相同,只是要请求的字节数不同。
0x01, // bNuInterfaces,该配置支持的接口数量
0x01, // bConfigurationvalue,配置值。在一个设备有多个配置时,主机使用该值以区分某一个配置。
0x00, // iConfiguration,字符串描述符索引
0xA0 // bmittributes,位图信息,每个位有自己的含义。如是否支持唤醒、是否支持BUS供电等。0xA0表示Bus Powered,且Remote Wakeup。
0x32, // MaxPower,设备要消耗的电流值,以2mA为单位。0x32=50,即50*2=100mA。
主机获取配置描述符后,解析到wTotalLength值,即配置描述符集合总长度,会再次向设备请求该长度的配置描述符。
//主机请求----获取配置描述符集合
0x80,0x06,0x00,0x02,0x00,0x00,0x22,0x00//注意,这里请求的长度是整个集合的长度,不是仅限于配置描述符的9bytes
//配置描述符集合
//***************************************配置描述符*************************************//
0x09, // bLength,,描述符长度
0x04, // bDescriptorType,描述符类型为配置描述符
0x22,0x00, // wTotalLength,包含配置描述符、接口描述符、hid类描述符、端点描述符的集合的总长度。主机获取该值后会再次发起请求,重新获取完整的配置描述符。主机第一次请求都是只请求9个字节的配置描述符,第二次请求才是包含配置描述符、接口描述符、hid类描述符、端点描述符的集合。两次请求命令相同,只是要请求的字节数不同。
0x01, // bNuInterfaces,该配置支持的接口数量
0x01, // bConfigurationvalue,配置值。在一个设备有多个配置时,主机使用该值以区分某一个配置。
0x00, // iConfiguration,字符串描述符索引
0xA0 // bmittributes,位图信息,每个位有自己的含义。如是否支持唤醒、是否支持BUS供电等。0xA0表示Bus Powered,且Remote Wakeup。
0x32, // MaxPower,设备要消耗的电流值,以2mA为单位。0x32=50,即50*2=100mA。
//**********************************接口描述符,键盘功能**********************************//
0x09, //bLenght,描述符长度
0x04, //bDescriptorType,描述符类型,0x04为接口描述符
0x00, //bInterfaceNumber,接口数量
0x00, //bAlternateSetting,可选设置
0x01, //bNumbEndpoints,本接口下的端点数量
0x03, //bInterfaceClass,接口类型,0x03为hid类
0x01, //bInterfaceSubClass,接口子类,是否支持BIOS,1支持,0不支持
0x01, //bInterfaceProtocol,接口协议,00其他,01键盘,02鼠标
0x00, //iInterface,接口描述在字符串描述符的索引,0为无该项
//************************************HID类描述符************************************//
0x09, //bLenght,描述符长度
0x21, //bDescriptorType,描述符类型,0x21为HID描述符
0x11,0x01, //bcdHID,HID协议版本号,0x0111,BCD格式,表示V1.11
0x00, //bCountryCode,硬件设备所在国家的国家代码,如果不限制,该字段为0
0x01, //bNumDescriptors,下级描述符的数量。可以是报告描述符,也可以说是物理描述符。该值至少为1,表示至少有一个报告描述符。
0x22, //bDescriptorType,下级描述符的类型。0x22是报告描述符。
0x3f,0x00, //wDescriptorLength,下级描述符的长度,0x3f是63bytes
//************************************端点描述符************************************//
0x07, //bLenght,描述符的长度,7bytes
0x05, //bDescriptorType,描述符的类型,0x05是端点描述符
0x81, //bEndpointAddress,端点的地址,Bit7决定该端点的方向,1是IN,0是OUT(对于控制端点可以忽略);Bit6-4,保留;BIt3-0:端点号;该处设置为0x81,即IN(设备上传主机)的1号端点
0x03, //bmAttributes,端点属性.Bit7-2保留(同步有定义)BIt1-0:00控制,01同步,02批量,03中断。
EPI_IN_SIZE,0x00, //wMaxPacketSize,端点收发信息包的最大长度,单位是字节。#define EPI_IN_SIZE 0x08
0x0A //bInterval,传输间隔 1:每1个帧 2:每2个帧 4:每4个帧 8:每8个帧。对于全速USB,总线间隔是一帧,一帧的长度是 1 毫秒。0x0A设置为10,则表示主机每隔10ms向端点要一次数据。
hid描述符
4. 主机枚举设备----激活设备配置
//主机请求-激活设备配置
0x00,0x09,0x01,0x00,0x00,0x00,0x00,0x00//0x01表示告知设备使用1号配置
4. 主机枚举设备----HID类设备主机常见类命令请求
类命令请求走控制传输通道。
//主机请求----设置HID设备上传速率
0x21,0x0A,0x00,0x00,0x00,0x00,0x00,0x00//0x00表示告知设备在有数据改变的时候再上传
//主机请求----下传报表 (数据)
0x21,0x09,0x00,0x00,0x00,0x00,0x00,0x00//用于点亮键盘上的LED灯,如Caps Lock键灯、Num键灯等
5. 主机枚举设备----获取报表描述符(HID类设备独有)
报表描述符定义了HID类设备的数据格式和用法。主机只有获取了报表描述符,才能正确地解析数据。报表描述赋予了HID类设备更大的弹性,可以组合成各种不同的功能。
报表描述符有自己的格式,与USB其他描述符完全不一致。HID Report 描述符,不像其他的描述符,仅仅是一个简单的一组数据。其主要由 Item 组成的,其定义依赖于 Report。HID 规范,定义了 3 种类型 的 Item:Main Item、Global Item、Local Item。
//主机请求----获取报表描述符
0x81,0x06,0x00,0x22,0x00,0x00,0xFF,0x00
//键盘设备上传报表描述符内容
0x05,0x01,// Usage Page,Global, Generic Desktop
0x09,0x06,//Usage, Local, KeyBoard
0xA1,0x01,// Collection,Main, Application
0x05,0x07,//Usage Page, Global, KeyBoard
//第1字节(8个功能键)
0x19,0xe0,// Local Usage Min (KeyBoard LeftControl)........... (224)
0x29,0xe7,// Local Usage Max (KeyBoard Right GUI)............. (231)
0x15,0x00,// Global Logical Min............................... (0)
0x25,0x01,// Global Logical Max............................... (1)
0x95,0x08,// Global ReportCount............................... (8)
0x75,0x01,// Global ReportSize.................................(1)
0x81,0x02,// Main Input(Data,Var,Abs).............(Data, Variable, asolute)
//第2字节(补充的常量,保留)
0x95,0x01,// Global ReportCount............ (1)
0x75,0x08,// Global ReportSize............. (8)
0x81,0x01,// Main Input...................(Constant)
//第3字节(用于键盘点灯,3bit对应3个灯。注意,本例没有下传端点,所以下传数据走控制端点0。这里定义了下传数据的意义,主机按此发送,设备按此解析并做出相应点灯动作)
0x95,0x03,// Global ReportCount.................. (3)
0x75,0x01,// Global ReportSize................... (1)
0x05,0x08,//Usage Page ..........................( LEDs )
0x19,0x01,//Usage Minimum........................ (1)
0x29,0x03.//Usage Maximum.........................(3)
0x91,0x02,//Main Output...........................(Data, Variable, solute)
//第3字节(再补5bit,将上面变成1个字节)
0x95,0x05,// Global ReportCount.................. (5)
0x75,0x01,// Global ReportSize................... (1)
0x91,0x01,// Main Output..........................(Constant)
//第4-9字节(6个字节,数组形式。键盘上剩余101个按键,键盘是阵列方式,不是一个按键占一个bit)
0x95,0x06,// Global ReportCount................. (6)
0x75,0x08,// Global ReportSize.................. (8)
0x15,0x00,// Global Logical Min................. (0)
0x25,0x65,//Global Logical Max.................. (101)
0x05,0x07,//Usage Page........................... (Keyboard/Keypad)
0x19,0x00,// Local Usage Min..................... (0)
0x29,0x65,// Local Usage Max..................... (101)
0x81,0x00,// Main Input......................... (Data,Ary)
0xc0 // Main, End collection
- Main Item
HID 规范定义了 5 种 Main Item Tag:
与数据关联的 Input、Output、Feature;
Input Item:描述由一个或多个物理控件提供的输入数据的信息。
Output Item:描述由一个或多个物理控件提供的输出数据的信息。
Feature Item:描述设备输入和输出不用于最终用户 — 例如,软件功能或控件面板切换。
与数据无关的Collection、End Collection:
Collection Item:标识两个或多个数据(input、output或feature)之间的关系。
End Collection:当 Collection 项打开一个数据集合时,End Collection 项关闭一个集合
Main Item 定义如下:其中bit7~bit2 表示 Item Tag 和 Type,nn 表示Item 后跟的data 域数据长度。比如nn为01,说明后跟1个字节数据;nn为10,说明后跟2个字节数据。
Input/Output/Feature
bit0:表示是数据还是常量
bit1:表示是数组还是变量字段
bit2:表示是绝对数值(基于固定原点)还是相对数值(指示上次报告值变化)
bit3:指示当达到极高或极低值时数据是否“翻滚”
bit4:表示来自设备的原始数据是否经过某种方式处理,不再代表测量的数据与报告的数据之间的线性关系
bit5:指示控件是否具有在用户未与控件进行物理交互时将返回的首选状态。
bit6:指示控件是否处于不发送有意义数据的状态。空状态的一种可能用途是用于需要用户与控件进行物理交互以使其报告有用的数据。
bit7:保留
bit8:指示控件发出固定大小的字节流。数据字段的内容由应用程序决定。缓冲区的内容不会解释为单个数字量。由缓冲字节项定义的报表数据必须在8位边界上对齐。
bit31-9:保留
Collection/End Collection
Physical:物理集合用于表示在一个几何点收集的数据点的一组数据项。这对于可能需要将测量或感测数据集与单个点相关联的感测设备很有用。它并不表示一组数据值来自一个设备,例如键盘。在报告多个传感器位置的设备的情况下,物理集合用于显示哪些数据来自每个单独的传感器。
Application:用于识别单个设备中服务于不同条目的的项目组。常见的例子是键盘或鼠标。带有集成指针设备的键盘可以定义为两个不同的应用程序集合。数据报告通常(但不一定)与应用程序集合相关联(每个应用程序至少有一个报告 ID)
Logcal:当一组数据项形成复合数据结构时,将使用逻辑集合
Report:定义一个包含报表中所有字段的逻辑集合。此集合中将包含唯一的报告 ID。应用程序可以轻松确定设备是否支持某个功能。请注意,可以为报告集合声明任何有效的报告 ID 值。
Name dArray:命名数组是包含选择器用法数组的逻辑集合。对于给定的功能,类似设备使用的选择器集可能会有所不同。在记录硬件寄存器时,字段的命名是常见的做法。要确定设备是否支持特定功能(如状态),应用程序可能必须先查询几个已知的状态选择器用法,然后才能确定设备是否支持状态。Named Array 用法允许对包含选择器的 Array 字段进行命名,因此应用程序只需查询 Status 用法即可确定设备支持状态信息。
Usage Switch:是一个逻辑集合,用于修改它所包含的用法的含义。
Usage Modifier:修改附加到包含集合的用法的含义。用法通常为控件定义单一操作模式。使用修饰符允许扩展控件的操作模式。
07~7f:扩展
80~ff:厂商自定义
- Global Item
全局项目主要用来选择用途页(Usage Page),定义数据域的长度(Report Count)、数量(Report Size)、报告ID(ReportId)等。
全局项目描述对后续的所有项目有效,除非遇到有新的全局项目。
Usage Page(用途页) Logical Minimum(逻辑最小值) Logical Maxinum(逻辑最大值) Physical Minimum(物理最小值) Physical Maximum(物理最大值) Report Size:数据域大小,表示每个数据域有多少位 Report Count:数据域有多少个数据域。 ReportId:报告ID Unit Exponent Unit:单位 PUSH:将全局项状态表的副本放置在堆栈上 POP:用堆栈中的顶部结构替换全局项状态表。
Global Item 定义如下:其中bit7~bit2 表示 Item Tag 和 Type,nn 表示Item 后跟的data 域数据长度。
Usage Page:指定设备功能。 Logical Minimum与 Logical
Maximum项目:定义报表的变量(Variable)或阵列(Array) 数据的限制范围,此限制范围以逻辑单位来表示。 Physical
Minimum和Physical Maximum:定义变量或数组的限制范围,此范围以物理单位来表示 Uint
Exponent:定义数值是基于10的基数 Unit:单位 Report Size:指定报表数据区域占的位数 Report
Count:报表数据区域数目 Report ID:报表ID Push:将global 项目状态表送入堆栈 Pop:从堆栈中恢复项目状态表
- Local Item
Local item标记定义控件的特征。这些项目不会影响到下一个main item。如果一个主项定义了多个控件,则它前面可能有几个类似的local item。
Usage Usage Minimum Usage Maximum
Local Item 定义如下:其中bit7~bit2 表示 Item Tag 和 Type,nn 表示Item 后跟的data 域数据长度。
Usage:用法索引值,表示对于项目或者集合建议的用法。用于当一个项目描述多个控制,对每一个变量和数组元素都有建议的用法 Usage
Minimum与 Usage Maximum项目:定义阵列和位图中控制第一个和最后一个用法 Designator
Index:确定用于控制的实体,指向物理描述符的目标 Designator Minimum与 和 Designator
Maximum项目:定义阵列和位图中控制起始和终止索引 String Index:确定字符串描述符索引值 String Minimum与 和
String Maximum项目:定义阵列和位图中字符串索引最小值和最大值
Delimiter:定义一组Local 项目的开始和结束。1=开始, 0=结束。
注意:报表上传字节数大于端点一次传输最大字节数时,会自动分包传输。USB主机会等待字节数达到报表字节数时才会从驱动层上传数据至应用层解析。