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

USB2.0枚举流程(以鼠标为例)——从零开始学习USB2.0协议(四)

1 总线枚举流程介绍

USB 2.0枚举的重要性在于它实现了即插即用功能,让主机自动识别、配置和管理设备,确保设备无需用户干预就能正常工作。

以下是 USB 2.0 枚举过程的概括步骤:

在这里插入图片描述
当一个设备连接到一个供电的端口时,将会有下列过程:

  1. 设备连接与电源稳定
    • 设备连接到供电的USB端口,HUB检测到端口电压变化,通过其中断端点向主机报告连接事件。
    • 主机发送Get_Port_Status请求给HUB以获取更多连接细节。
    • HUB通过检测总线空闲时差分线的电压来判断设备速度(低速、全速或高速)。此检测在复位操作之前完成。
  2. 端口复位与设备速度确认
    • 主机等待至少100ms,确保连接稳定和设备电源稳定。
    • 主机发送Set_Port_Feature请求,要求HUB对设备端口进行复位。HUB将数据线(D+和D-)驱动为低电平,持续至少10ms。
    • 复位期间,HUB不会影响其他端口。
    • 对于高速设备,初始以全速运行。如果HUB支持高速,会进行高速检测(通过检查设备是否在复位过程中发出Chirp信号)。如果设备支持高速,则切换到高速模式;否则保持全速。
  3. 设备默认状态与通信建立
    • 复位完成后,设备进入默认状态,使用默认地址0,端点0进行通信。此时设备可从总线获取最大100mA电流。
    • 主机通过反复发送Get_Port_Status请求,确认复位完成。
  4. 初始设备描述符获取
    • 主机向默认地址0发送Get_Descriptor请求,获取设备描述符。设备描述符中包含了端点0的最大包长度(位于第8字节)等重要信息。
    • 首次控制传输完成后,主机会要求HUB再次对设备进行复位,使设备进入确定状态。
  5. 地址分配
    • 主机发送Set_Address请求,为设备分配一个唯一的地址。设备使用新地址,进入地址状态。
  6. 详细设备信息获取
    • 主机使用新地址再次发送Get_Descriptor请求,获取设备描述符(包括设备类型、VID、PID、配置个数等)。
    • 主机获取配置描述符(通常为9字节),然后根据配置描述符中的配置集合总长度,获取完整的配置集合(包括配置描述符、接口描述符、端点描述符等)。
    • 根据需要,主机可能还会获取字符串描述符。
  7. 驱动匹配与加载
    • 主机解析描述符,根据设备信息选择合适的驱动程序。对于复合设备,通常需要匹配接口。
    • 主机将设备添加到USB总线设备列表,USB总线遍历驱动列表,调用match函数进行匹配。匹配成功后,绑定驱动。
  8. 设备配置
    • 设备驱动根据设备信息,发送Set_Configuration请求,选择设备的某个配置作为工作配置。
    • 设备使能所选配置的接口,进入配置状态。

2 模拟鼠标的枚举实现

2.1 鼠标外设程序

程序是基于公司开发的USB IP接口,在FPGA开发板上实现了模拟鼠标的功能,通过:
1、向串口中发送指令字符
2、控制USB向windows主机发送命令,移动鼠标光标。

2.1.1 任务分析

基于对前面对USB2.0协议的理解,可以来做一个鼠标的demo,下面可以来简单规划下整个任务。

  1. 目标

模拟鼠标上下左右移动和左右键,用向uart输入wsad字符来代替光标移动,输入c空格代替左右按键。

  1. 实现

为了实现主要在于:
1、构建描述符,解析主机设备请求,按照流程完成枚举
2、hid鼠标事件上报

2.1.2 描述符构建

鼠标本身属于HID设备的子类,和大多数的USB设备一样,HID设备也有USB设备的一些标准描述符,如设备描述符、配置描述符、接口描述符、端点找述符

但HID设备也有一些特殊描述符,如HID描述符报告描述符物理描述符
这里简单说明下,后面可能会针对HID设备进行详细介绍。
在这里插入图片描述
说明:

  • HID设备的设备类型不是在设备类型中定义,而是在接口描述符中定义。设备描述符中的bDeviceClass和bDeviceSubClass字段不用于标识属于HID类的设备。而是在接口描述符中使用bInterfaceClass和bInterfaceSubClass字段。
  • 接口描述符中的bInterfaceSubClass仅用于区分是否支持boot启动,bInterfaceProtocol也只在bInterfaceSubClass有效时用于区分boot起动的设备类型。
  • HID设备在非boot模式下,设备的类型是由报告描述符来定义的。一个报告描述符可以包含多个应用(调备)类型。
  • 物理描述符physical descriptor是可选的
  • 报告描述符report descriptor是必须的
  • 报告描述符的个数和各个报告描述符的长度在HID描述符中定义。
  • 报告描述符的获取是通过发向接口的标准请求实现的。

就上面的描述来看,要实现对USB2.0鼠标的注册,就要完成设备、配置、接口、HID、报告等描述符的。
我们可以根据HID对描述符的要求来构建下。

  1. 设备描述符
const unsigned char DevDes[18]=
{0x12, /* bLength */0x01, /* bDescriptorType : device_descriptor */0x10,0x01, /* bcdUSB usb2.0 = 0x0200 usb1.1 = 0x0110 usb3.11 = 0x0311 */0x00, /* bDeviceClass */0x00, /* bDeviceSubClass */0x00, /* bDeviceProtocol */0x40, /* bMaxPacketSize */0x66,0x66, /* idVendor : 2 Bytes */0x88,0x88, /* idProduct : 2 Bytes */0x01,0x00, /* bcdDevice rel. 1.00 */0x01, /* Index of manufacturer string */0x02, /* Index of product string */0x00, /* Index of serial number string */0x01  /* bNumConfigurations */
};
  1. 配置描述符

这里将配置描述符、接口描述符、类特殊描述符、端点描述符一同放到配置描述符。

是因为正常的主机获取配置描述符过程:
1、第一次先获取9字节长度的配置描述符,然后根据配置描述符中配置集合的长度,再次获取配置描述符集合(不同主机程序可能有些差异)。
2、第二次获取的时候,设备会将配置描述符、接口描述符、类特殊描述符、端点描述符等一并返回。
后面抓包会看到该流程。

const unsigned char ConDes[9 + 9 + 9 + 7]=
{// Configuation Descriptor0x09, // bLength0x02, // bDescpriptorTypesizeof(ConDes) & 0xFF,(sizeof(ConDes)) >> 8 & 0xFF, // wTotalLength0x01, // bNumInterfaces0x01, // bConfigurationValue0x00, // iConfiguration0x80, // bmAttributes0x32, // bMaxPower : 100mA// Interface Descriptor0x09, // bLength0x04, // bDescriptorType0x00, // bInterfaceNumber0x00, // bAlternateSetting0x01, // bNumEndpoints(not include Ep0)0x03, // bInterfaceClass (Hid)0x01, // bInterfaceSubClass0x02, // bInterfaceProtocol : mouse0x00, // iInterface// HID descriptor0x09, // bLength0x21, // bDescriptorType0x11,0x01, // bcdHID : 0x01110x00, // bContryCode0x01, // bNumDescriptor0x22, // bDescriptorTypesizeof(ReportDescriptor) & 0xFF,(sizeof(ReportDescriptor) >> 8) & 0xFF, // wDescriptorLength// Endpoint Descriptor0x07, // bLength0x05, // bDescriptorType0x81, // Ep1 : In0x03, // bmAttributes : Interrupt0x04,0x00, // wMaxPackeSize : 0x00040xA  // bInterval : 10ms
};
  1. 字符描述符
/* langDes */
const unsigned char LangDes[]=
{0x04, // bLength0x03,	// bDescriptorType0x09,0x04 // wLANGID(0x0409:english)
};/* Manuf */
const unsigned char Manuf_Des[]=
{0x12,0x03,0x68, 0x00, //hezaizai0x65, 0x00,0x7A, 0x00,0x61, 0x00,0x69, 0x00,0x7A, 0x00,0x61, 0x00,0x69, 0x00,
};
/* product Des */
const unsigned char Prod_Des[]=
{0x32,0x03,0x44, 0x00, // Demo USB2.0 optical Mouse0x65, 0x00,0x6D, 0x00,0x6F, 0x00,0x20, 0x00,0x55, 0x00,0x53, 0x00,0x42, 0x00,0x32, 0x00,0x2E, 0x00,0x30, 0x00,0x20, 0x00,0x6F, 0x00,0x70, 0x00,0x74, 0x00,0x69, 0x00,0x63, 0x00,0x61, 0x00,0x6C, 0x00,0x20, 0x00,0x4D, 0x00,0x6F, 0x00,0x75, 0x00,0x73, 0x00,0x65, 0x00,
};
/* product ser */
const unsigned char SerDes[18] =
{0x12,0x03,0x32, 0x00, // 202510240x30, 0x00,0x32, 0x00,0x35, 0x00,0x31, 0x00,0x30, 0x00,0x32, 0x00,0x34, 0x00,
};

2.1.3 设备请求解析

  1. 设备请求格式封装

按照设备请求格式,代码封装如下:

typedef	union _REQUEST_PACK {unsigned char buffer[8];struct {unsigned char bmReuestType;unsigned char bRequest;uint16_t wValue;uint16_t wIndx;uint16_t wLength;} r;
} mREQUEST_PACKET, *mpREQUEST_PACKET;

2、根据设备请求格式解析

void handle_usb_request(mREQUEST_PACKET USB_request)
{setUpStage = 1;if (USB_request.r.bmReuestType & 0x80) {printf("dir: GET\n");} else {printf("dir: SET\n");}switch ((request.r.bmReuestType >> 5) & 0x3) {case 0:printf("std request->");handle_std_request(USB_request); //handle standard requestbreak;case 1:printf("class request->");break;case 2:printf("manufacture request->");break;case 3:printf("reserve req!\r\n");break;}
}// only handle GET_DESCRIPTOR and SET_ADDRESS req
void handle_std_request(mREQUEST_PACKET USB_request)
{switch (USB_request.r.bRequest) {case GET_STATUS:printf("GET_STATUS->");break;case CLEAR_FEATURE:printf("CLEAR_FEATURE->");break;case SET_FEATURE:printf("SET_FEATURE->");break;case SET_ADDRESS:printf("SET_ADDRESS:\r\n");USB_SetDevAddress(USB_request.r.wValue);break;case GET_DESCRIPTOR:printf("GET_DESCRIPTOR->");handle_get_desc_request(USB_request);break;case SET_DESCRIPTOR:printf("SET_DESCRIPTOR->");break;case GET_CONFIGURATION:printf("GET_CONFIGURATION->");break;case SET_CONFIGURATION:printf("SET_CONFIGURATION->");break;case GET_INTERFACE:printf("GET_INTERFACE->");break;case SET_INTERFACE:printf("SET_INTERFACE->");break;case SYNCH_FRAME:printf("SYNCH_FRAME->");break;}
}
  1. 根据设备请求类型处理描述符
void handle_get_desc_request(mREQUEST_PACKET USB_request)
{int len;unsigned char req_type = USB_request.r.wValue >> 8;unsigned char req_index = USB_request.r.wValue & 0xF;switch (req_type) {case 1:printf("device desc id(%d)\n",req_index);VarSetupDescr = DevDes;mVarSetupLength = MIN(sizeof(DevDes), SetupMaxLength);break;case 2:printf("cfg desc id(%d)\n",req_index);VarSetupDescr = ConDes;mVarSetupLength = MIN(sizeof(ConDes), SetupMaxLength);break;case 3:printf("string desc:");if ((USB_request.r.wValue & 0xff) == 0) {printf("LangDes\n");VarSetupDescr = LangDes;mVarSetupLength = MIN(sizeof(LangDes), SetupMaxLength);} else if ((USB_request.r.wValue & 0xff) == 1) {printf("Manuf_Des id(%d)\n",req_index);VarSetupDescr = Manuf_Des;mVarSetupLength = MIN(sizeof(Manuf_Des), SetupMaxLength);} else if ((USB_request.r.wValue & 0xff) == 2) {printf("Prod_Des id(%d)\n",req_index);VarSetupDescr = Prod_Des;mVarSetupLength = MIN(sizeof(Prod_Des), SetupMaxLength);} else if ((USB_request.r.wValue & 0xff) == 3) {printf("SerDes id(%d)\n",req_index);VarSetupDescr = SerDes;mVarSetupLength = MIN(sizeof(SerDes), SetupMaxLength);} else {printf("other\n");}break;case 4:printf("interface desc id(%d)\n",req_index);break;case 5:printf("endpoint desc id(%d)\n",req_index);break;case 6:printf("qualifier desc id(%d)\n",req_index);VarSetupDescr = DeviceQualifierDesc;mVarSetupLength = MIN(sizeof(DeviceQualifierDesc), SetupMaxLength);break;case 0x22:printf("hid desc\n");VarSetupDescr = ReportDescriptor;mVarSetupLength = MIN(sizeof(ReportDescriptor), SetupMaxLength);break;default:printf("other desc\n");}UsbEp0Up();
}
  1. uart 模拟鼠标行为
void USART0_IRQHandler(void)
{uint8_t USART0_receive_ch;if (USART_GetITStatus(USART0, USART_STATUS_RXIP) == SET) {USART0_receive_ch = USART_ReceiveData(USART0);// 鼠标移动指令if (USART0_receive_ch == 'a') {buf_mouse[1] = -10;  // X位移:左移buf_mouse[2] = 0;    // Y位移:不变printf("left\n");need_release = 0;   // 移动事件不需要释放,所以清除need_release(防止点击事件未完成)} else if (USART0_receive_ch == 'd') {buf_mouse[1] = 10;   // X位移:右移buf_mouse[2] = 0;    // Y位移:不变printf("right\n");need_release = 0;} else if (USART0_receive_ch == 'w') {buf_mouse[1] = 0;    // X位移:不变buf_mouse[2] = -10;  // Y位移:上移printf("up\n");need_release = 0;} else if (USART0_receive_ch == 's') {buf_mouse[1] = 0;    // X位移:不变buf_mouse[2] = 10;   // Y位移:下移printf("down\n");need_release = 0;}// 鼠标按键指令else if (USART0_receive_ch == 'c') {// 左键点击:设置按钮状态bit0为1buf_mouse[0] = 0x01;  // 按钮状态:左键按下buf_mouse[1] = 0;     // X位移:不变buf_mouse[2] = 0;     // Y位移:不变printf("left click\n");need_release = 1;    // 标记需要发送释放报告} else if (USART0_receive_ch == ' ') {// 右键点击:设置按钮状态bit1为1buf_mouse[0] = 0x02;  // 按钮状态:右键按下buf_mouse[1] = 0;     // X位移:不变buf_mouse[2] = 0;     // Y位移:不变printf("right click\n");need_release = 1;} else if (USART0_receive_ch == 'x') {// 中键点击:设置按钮状态bit2为1buf_mouse[0] = 0x04;  // 按钮状态:中键按下buf_mouse[1] = 0;     // X位移:不变buf_mouse[2] = 0;     // Y位移:不变printf("middle click\n");need_release = 1;} else if (USART0_receive_ch == 'r') {// 释放所有按键buf_mouse[0] = 0x00;  // 按钮状态:所有键释放buf_mouse[1] = 0;     // X位移:不变buf_mouse[2] = 0;     // Y位移:不变printf("release all buttons\n");need_release = 0;     // 不需要再释放}mouse_data_ready = 1;}
}

2.1.4 最终注册情况

  1. 打开windows设备管理器,可以看到注册成功的鼠标设备
    在这里插入图片描述
    2、window 端通过USB Device Tree查看注册信息如下
        +++++++++++++++++ Device Information ++++++++++++++++++
Device Description       : HID-compliant mouse
Device Path 1            : \\?\HID#VID_6666&PID_8888#7&ee10cea&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} (GUID_DEVINTERFACE_HID)
Device Path 2            : \\?\HID#VID_6666&PID_8888#7&ee10cea&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd} (GUID_DEVINTERFACE_MOUSE)
Kernel Name              : \Device\0000015b
Device ID                : HID\VID_6666&PID_8888\7&EE10CEA&0&0000
Hardware IDs             : HID\VID_6666&PID_8888&REV_0001 HID\VID_6666&PID_8888 HID\VID_6666&UP:0001_U:0002 HID_DEVICE_SYSTEM_MOUSE HID_DEVICE_UP:0001_U:0002 HID_DEVICE
Driver KeyName           : {4d36e96f-e325-11ce-bfc1-08002be10318}\0002 (GUID_DEVCLASS_MOUSE)
Driver                   : \SystemRoot\System32\drivers\mouhid.sys (Version: 10.0.19041.1  Date: 2019-12-07  Company: Microsoft Corporation)
Driver Inf               : C:\Windows\inf\msmouse.inf
Legacy BusType           : PNPBus
Class                    : Mouse
Class GUID               : {4d36e96f-e325-11ce-bfc1-08002be10318} (GUID_DEVCLASS_MOUSE)
Service                  : mouhid
Enumerator               : HID
Location Info            : -
Address                  : 1
Manufacturer Info        : Microsoft
Capabilities             : 0xA0 (SilentInstall, SurpriseRemovalOK)
Status                   : 0x0180200A (DN_DRIVER_LOADED, DN_STARTED, DN_DISABLEABLE, DN_NT_ENUMERATOR, DN_NT_DRIVER)
First Install Date       : 2025-09-17 11:18:39
Last Arrival Date        : 2025-10-09 17:53:44
EnhancedPowerMgmtEnabled : 0
Power State              : D0 (supported: D0, D3, wake from D0)
+++++++++++++ Mouse Information ++++++++++++++
Input Data Queue Length  : 2
Mouse Identifier         : 256
Number of Buttons        : 3
Sample Rate              : 0
++++++++++++++ HID Information +++++++++++++++
Manufacturer             : hezaizai
Product                  : Demo USB2.0 optical Mouse
UsagePage                : 0x01 (Generic Desktop Controls)
Usage                    : 0x02 (Mouse)

Note:
USB Device Tree下载地址: https://www.uwe-sieber.de/files/UsbTreeView_Win32.zip

3、视频效果演示

uart-mouse test

3 枚举流程详细说明

当前市面上已有多种便捷的USB抓包分析工具,同时也可使用Wireshark等软件工具进行USB报文捕获与分析,用于排查连接过程中的问题。

除上述方法外,我们还可通过示波器或逻辑分析仪对USB通信进行底层信号采集与分析。本项目将采用逻辑分析仪对模拟鼠标设备的完整枚举流程进行抓包,并深入解析报文内容。

需要说明的是,实际枚举过程可能与其他技术文档描述存在差异,这主要源于主机端枚举策略的动态调整。为深入验证设备兼容性,后续计划还将开发定制化主机端测试程序,并移植常用USB协议栈以完善功能。

通过这种硬件级的报文分析方式,能够更深入地理解USB通信机制,为后续设备开发与问题排查提供有力支持。

另外,逻辑分析仪截图,前面章节比较详细,后面有一些删减(因为很多都差不多)。

3.0 总线复位

在这里插入图片描述

  1. device上电后,拉高dp(d+)总线(高速/全速上拉接在dp,低速接在dm)
  2. hub检测到电压变化并将信息反馈给host
  3. host之后会发送一个Get_Port_Status
  4. hub会将设备速度类型回复给host
  5. 在device拉高100ms稳定后,host会发出Set_Port_Feature请求让hub 复位其管理的端口
  6. hub通过驱动数据线到复位状态(D+和 D-全为低电平),并持续至少 10ms。当然,hub 不会把这样的复位信号发送给其他已有设备连接的端口,所以其他连在该 hub 上的设备看不到复位信号

另外USB热插拔的实现机制也正是基于此。

3.1 Get DevDesc控制传输

3.1.1 SETUP事务

在这里插入图片描述

  1. host->device SETUP令牌包
  2. host->device Data0数据包
    80 06 00 01 00 00 40 00(小端格式)
    在这里插入图片描述
0x80,        // bmRequestType: Dir: D2H, Type: Standard, Recipient: Device
0x06,        // bRequest (Get Descriptor)
0x00,        // wValue[0:7]  Desc Index: 0
0x01,        // wValue[8:15] Desc Type: (Device)
0x00, 0x00,  // wIndex Language ID: 0x00
0x40, 0x00,  // wLength = 64

该报文表示,主机请求设备返回一个描述符(0x06),描述符类型为设备描述符(0x01),长度为64字节(0x40)

  1. device->host ACK握手包

3.1.2 IN事务

在这里插入图片描述

  1. host->device IN令牌包
  2. device->host Data1数据包
0x12,        // bLength
0x01,        // bDescriptorType (Device)
0x10, 0x01,  // bcdUSB 1.10
0x00,        // bDeviceClass (Use class information in the Interface Descriptors)
0x00,        // bDeviceSubClass 
0x00,        // bDeviceProtocol 
0x40,        // bMaxPacketSize0 64
0x66, 0x66,  // idVendor 0x6666
0x88, 0x88,  // idProduct 0x8888
0x01, 0x00,  // bcdDevice 0.01
0x01,        // iManufacturer (String Index)
0x02,        // iProduct (String Index)
0x00,        // iSerialNumber (String Index)
0x01,        // bNumConfigurations 1
  1. host->device ACK握手包

3.1.3 OUT事务

在这里插入图片描述

  1. host->device OUT令牌包
  2. host->device Data1数据包
  3. device->host ACK握手包

3.1.4 总结

这里会发现在host获取device描述符的过程分为三个阶段:

  1. 建立阶段
    主机发送一个8字节的请求包(Setup Packet)。这个包包含了请求类型(GET_DESCRIPTOR)、请求值、索引以及最重要的 wLength(40)
  2. 数据阶段
    根据请求,设备向主机回复数据(18字节描述符)
  3. 状态阶段
    主机发送一个空数据包,表示数据传输完成

建立阶段一定是DATA0 数据包,之后如果有数据阶段,将进行翻转,变成 DATA1,并且在每次正确数据传输后都会进行一次翻转,这个机制用于保证数据被正确接收,而不是发送方发送的重复数据包(如果对方没有正确接收数据,DATAx不会翻转)。
在状态阶段,一律使用 DATA 1进行回复,状态阶段的数据包中的数据为空,也就是说不携带任何数据。

当完成了以上几个阶段,一次控制传输才算完成。

3.2 总线复位

当完成第一次的控制传输后,系统会要求 hub 对设备进行再一次的复位操作。再次复位的目的是使设备进入一个确定的状态。
在这里插入图片描述

3.3 Set_Address控制传输

3.3.1 SETUP事务

在这里插入图片描述

  1. host->device SETUP令牌包
  2. host->device Data0数据包
0x00,        // bmRequestType: Dir: H2D, Type: Standard, Recipient: Device
0x05,        // bRequest (Set Address)
0x02, 0x00,  // wValue Device Addr: 2
0x00, 0x00,  // wIndex = 0x00
0x00, 0x00,  // wLength = 0
  1. device->host ACK握手包

3.3.2 IN事务

在这里插入图片描述

  1. host->device IN令牌包
  2. device->host Data1数据包
  3. host->device ACK握手包

Set_Address控制传输的信息通过SETUP令牌包以及传输完了,就不用额外的数据传输,只有建立阶段和状态阶段,而不需要数据阶段了。

device会将收到的addr配置到对应的寄存器,后续host也会将通过该addr来和device进行数据交互

3.4 Get DevDesc控制传输

再次获取device的描述信息,收发包同3.1一样。

不同的是这次发出读取18bytes数据SETUP令牌包(第一次请求读取64bytes的描述信息)

3.5 Get CfgDesc控制传输

这里是第一次获取device的配置信息,主要是获取配置描述符长度,后续会再发一次,获取完整配置描述符信息。

我用的windows主机第一次请求长度是256bytes,其他主机也有9bytes;但这里不关键,不影响配置描述符长度的获取,也不影响后续配置描述符的获取。

3.5.1 SETUP事务

在这里插入图片描述

  1. host->device SETUP令牌包
    可以看到此时的Addr = 2
  2. host->device Data0数据包
    0x80, // bmRequestType: Dir: D2H, Type: Standard, Recipient: Device
    0x06, // bRequest (Get Descriptor)
    0x00, // wValue[0:7] Desc Index: 0
    0x02, // wValue[8:15] Desc Type: (Configuration)
    0x00, 0x00, // wIndex Language ID: 0x00
    0xFF, 0x00, // wLength = 255
  3. device->host ACK握手包

3.5.2 IN事务

在这里插入图片描述

  1. host->device IN令牌包
  2. device->host Data1数据包
0x09,        // bLength: 描述符长度=9字节
0x02,        // bDescriptorType: 配置描述符类型(0x02)
0x22, 0x00,  // wTotalLength: 配置信息总长度=34字节(0x0022)
0x01,        // bNumInterfaces: 接口数量=1
0x01,        // bConfigurationValue: 配置值=1(用于SET_CONFIGURATION)
0x00,        // iConfiguration: 配置字符串索引=0(无字符串)
0x80,        // bmAttributes: 属性//   - 位7: 1=总线供电//   - 位6: 0=不支持远程唤醒//   - 位5-0: 保留
0x32,        // bMaxPower: 最大功耗=100mA(0x32=50, 单位2mA→100mA)0x09,        // bLength: 描述符长度=9字节
0x04,        // bDescriptorType: 接口描述符类型(0x04)
0x00,        // bInterfaceNumber: 接口编号=0
0x00,        // bAlternateSetting: 备用设置=0
0x01,        // bNumEndpoints: 端点数量=1(除了默认控制端点0)
0x03,        // bInterfaceClass: 接口类=0x03(HID类)
0x01,        // bInterfaceSubClass: 接口子类=0x01(启动接口)
0x02,        // bInterfaceProtocol: 接口协议=0x02(鼠标)
0x00,        // iInterface: 接口字符串索引=0(无字符串)0x09,        // bLength: 描述符长度=9字节
0x21,        // bDescriptorType: HID描述符类型(0x21)
0x11, 0x01,  // bcdHID: HID规范版本=1.11
0x00,        // bCountryCode: 国家代码=0(不支持本地化)
0x01,        // bNumDescriptors: 下级描述符数量=1
0x22,        // bDescriptorType[0]: 报告描述符类型(0x22)
0x34, 0x00,  // wDescriptorLength[0]: 报告描述符长度=52字节(0x0034)0x07,        // bLength: 描述符长度=7字节
0x05,        // bDescriptorType: 端点描述符类型(0x05)
0x81,        // bEndpointAddress: 端点地址//   - 位7: 1=IN方向(设备到主机)//   - 位3-0: 0001=端点1
0x03,        // bmAttributes: 端点属性//   - 位1-0: 11=中断传输
0x04, 0x00,  // wMaxPacketSize: 最大包大小=4字节
0x0A,        // bInterval: 轮询间隔=10ms
  1. host->devece ACK握手包

3.5.3 OUT事务

  1. host->device OUT令牌包
  2. host->device Data1数据包
  3. device->host ACK握手包

本次的控制传输同样分为建立阶段,数据阶段和状态阶段

3.6 Get StrDesc控制传输

在host获取字符描述符前,一般会先获取下字符支持的语言,方便主机根据字符编码来解析显示。
这里获取的是厂商信息。

3.6.1 SETUP事务

在这里插入图片描述

0x80,        // bmRequestType: Dir: D2H, Type: Standard, Recipient: Device
0x06,        // bRequest (Get Descriptor)
0x00,        // wValue[0:7]  Desc Index: 0
0x03,        // wValue[8:15] Desc Type: (String)
0x00, 0x00,  // wIndex Language ID: 0x00
0xFF, 0x00,  // wLength = 255

3.6.2 IN事务

在这里插入图片描述

0x04          //字符长度
0x03          //字符串描述符
0x09,0x04  //支持语言为英语

3.6.3 OUT事务在这里插入图片描述

3.6.4 SETUP事务

在这里插入图片描述

0x80,        // bmRequestType: Dir: D2H, Type: Standard, Recipient: Device
0x06,        // bRequest (Get Descriptor)
0x02,        // wValue[0:7]  Desc Index: 2
0x03,        // wValue[8:15] Desc Type: (String)
0x09, 0x04,  // wIndex Language ID: 0x0409
0xFF, 0x00,  // wLength = 255

3.6.5 IN事务

在这里插入图片描述

3.6.6 OUT事务

在这里插入图片描述

3.7 Get DevDesc控制传输

再次获取设备描述符,同3.4节。

3.8 Get CfgDesc控制传输

这里是第二次获取device的配置描述符,和第一次(3.5节)不一样支持在于,该次设备请求的数据长度为第一次上报的34bytes

3.9 SET Configure

用于​​激活​​设备的一个指定配置,使device从"寻址状态"进入"配置状态",device可以启用相应的端点(端点可以开始接收/发送报告)

3.9.1 SETUP事务

在这里插入图片描述

0x00,        // bmRequestType: Dir: H2D, Type: Standard, Recipient: Device
0x09,        // bRequest (Set Config)
0x01, 0x00,  // wValue Config Num: 1
0x00, 0x00,  // wIndex = 0x00
0x00, 0x00,  // wLength = 0

3.9.2 IN事务

在这里插入图片描述

3.10 SET IDLE

属于 HID类特定请求,当输入数据没有变化时,不要频繁报告,只有在数据发生变化时,或者达到指定的空闲时间后才发送报告

3.10.1 SETUP事务

在这里插入图片描述

0x21,        // bmRequestType: Dir: H2D, Type: Class, Recipient: Interface
0x0A,        // bRequest
0x00, 0x00,  // wValue[0:15] = 0x00
0x00, 0x00,  // wIndex = 0x00
0x00, 0x00,  // wLength = 0

在这里插入图片描述

3.11 Get HID Report控制传输

3.11.1 SETUP事务

在这里插入图片描述

0x81,        // bmRequestType: Dir: D2H, Type: Standard, Recipient: Interface
0x06,        // bRequest (Get Descriptor)
0x00,        // wValue[0:7]  Desc Index: 0
0x22,        // wValue[8:15] Desc Type: (HID Report)
0x00, 0x00,  // wIndex Language ID: 0x00
0x74, 0x00,  // wLength = 116

3.11.2 IN事务

在这里插入图片描述

0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
0x09, 0x02,        // Usage (Mouse)
0xA1, 0x01,        // Collection (Application)
0x09, 0x01,        //   Usage (Pointer)
0xA1, 0x00,        //   Collection (Physical)
0x05, 0x09,        //     Usage Page (Button)
0x19, 0x01,        //     Usage Minimum (0x01)
0x29, 0x03,        //     Usage Maximum (0x03)
0x15, 0x00,        //     Logical Minimum (0)
0x25, 0x01,        //     Logical Maximum (1)
0x75, 0x01,        //     Report Size (1)
0x95, 0x03,        //     Report Count (3)
0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x05,        //     Report Size (5)
0x95, 0x01,        //     Report Count (1)
0x81, 0x03,        //     Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
0x09, 0x30,        //     Usage (X)
0x09, 0x31,        //     Usage (Y)
0x09, 0x38,        //     Usage (Wheel)
0x15, 0x81,        //     Logical Minimum (-127)
0x25, 0x7F,        //     Logical Maximum (127)
0x75, 0x08,        //     Report Size (8)
0x95, 0x03,        //     Report Count (3)
0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              //   End Collection
0xC0,              // End Collection

3.11.6 OUT事务

在这里插入图片描述

3.12 Get StrDesc控制传输

后续可能还会获取其他的字符描述符,包括:厂商信息、设备信息等。

篇幅原因,就不再这里累述了,可以参考3.6。

4 中断传输

鼠标、键盘等人机交互设备(HID)的特点是​​数据量小​​,但要求​​主机能及时响应​​。中断传输正是为这种场景设计的:主机保证会以固定的时间间隔去主动询问(轮询)设备是否有数据要上报。

通过端点描述符可以获取到信息:允许鼠标设备以​​最高100Hz的频率​​,通过​​端点1​​向主机发送​​最多4字节​​的移动和按键数据。

下面简单抓取下执行上下左右时的波形

可以看出此时上报的ADDR=2,EP=1。


  • 在这里插入图片描述

  • 在这里插入图片描述

  • 在这里插入图片描述

  • 在这里插入图片描述

参考

HID 简介
HID规范
HID Usage Tables

http://www.dtcms.com/a/524607.html

相关文章:

  • hot100练习-17
  • 光伏发电建模与性能分析:从半导体物理到输出功率预测
  • 浙江正规网站建设配件网站seo优化分析
  • 设计师赚钱的网站创新的常州做网站
  • vue3的props的使用
  • 【Trae+AI】和Trae学习搭建App_03:后端API开发原理与实践(已了解相关知识的可跳过)
  • List of Keys (Keyboard,Mouse and Controller)
  • 门户网站怎样做wordpress清新模板
  • 沈阳有资质做网站的公司公司自有网站工信备案
  • 园林设计公司网站昆山网站建设网站
  • 【Linux】systemd 服务管理详解
  • Python哪个Excel库最好用?
  • 瓦力机器人-编码电机控制(基于树莓派5)
  • dw做网站怎么上线大良用户网站建设
  • Node.js 进阶:掌握高性能服务器开发的核心技术
  • Elasticsearch 的 SQL 与 DSL 转换
  • 快速做网站的软件腾讯企业邮箱电脑版登录入口
  • API测试工具进化:从Postman到Apipost的全局参数管理革命
  • 数据结构——排序的超级详解(Java版)
  • C# 加密解密字符方法Cryptography
  • 教做详情页的网站关键词优化公司电话
  • 中企动力科技股份有限公司网站梵克雅宝官网官方网
  • 自己电脑做电影网站中国建设教育协会培训中心
  • 第三章、React项目国际化介绍(`react-i18next`)
  • RHCA - DO374 | Day03:通过自动化控制器运行剧本
  • 深圳微商城网站建设温州 网站开发
  • 何谓RSS
  • 【SpringCloud】Eureka
  • 网站后台尺寸一般做多大的如何把优酷视频放到网站上
  • 帝国建站模板淘宝网络营销案例分析