前言:本篇博客为手把手教学的 USB 2.0 协议栈类精品博客,该专栏博客侧重针对 USB 2.0 协议进行讲解。第 4 篇重点为 USB 2.0 协议中的配置描述符 Configuration Descriptors 进行讲解,并结合 CH32V307 与 STM32 代码进行 Configuration Descriptors 分析。USB 协议栈是嵌入式工程研发过程中很大的坑,USB 协议栈非常冗杂且深奥,但它在工程项目中却至关重要,希望这篇博文能给读者朋友的工程项目给予些许帮助,Respect!
Universal Serial Bus 版本:
USB Descriptor:
推荐网址
USB官网:Front Page | USB-IF
USB中文网:USB中文网 (usbzh.com)
沁恒WCH官网:首页 - 南京沁恒微电子股份有限公司 (wch.cn)
一、配置描述符
USB 描述符其实就是 C 语言里面的结构体 Structure 和数组 Array,数组包含的信息说明当前的设备具有哪些特征。USB 描述符有设备描述符、配置描述符、接口描述符、端点描述符、字符串描述符,HID 设备有 HID 描述符、报告描述符和物理描述符。我们先学会每个描述符的细节,作者会进行非常详细的讲解,后期在回顾 USB 枚举的时候会通过抓包和波形来学习每一个描述符在总线上的作用,并且会介绍每一个描述符是在什么时候以哪种方式在总线上进行传输的,本篇博客是重点学习 USB Configuration Descriptors 的组成。
一个USB设备至少有一个或者多个配置,这一点可以从设备描述符的最后一项 bNumConfigurations 得到,但是当前只能选择其中一种配置,每一种配置都对应一个配置描述符集合,为什么说是一个集合呢,因为这个配置包括:标准配置描述符 Configuration Descriptors、接口描述符 Interface Descriptors、端点描述符 Endpoint Descriptors,如果是 HID 设备还会包括 HID 描述符,我们今天讲的是标准配置描述符,后期会讲解其他的描述符。标准配置描述符只有 9 个字节,组成如下:
二、MCU配置描述符代码
2.1 STM32代码
/* USB Configuration Descriptor */
const uint8_t CustomHID_ConfigDescriptor[CUSTOMHID_SIZ_CONFIG_DESC] =
{
/* 标准配置描述符 */
0x09, /* bLength: Configuation Descriptor size */
USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
CUSTOMHID_SIZ_CONFIG_DESC, /* wTotalLength low : Bytes returned */
0x00, /* wTotalLength high: Bytes returned */
0x01, /* bNumInterfaces: 1 interface */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing the configuration*/
0xC0, /* bmAttributes: Bus powered */
/*Bus powered: 7th bit, Self Powered: 6th bit, Remote wakeup: 5th bit, reserved: 4..0 bits */
0x96, /* MaxPower 300 mA: this current is used for detecting Vbus */
/* 接口描述符 */
/************** Descriptor of Custom HID interface ****************/
/* 09 */
0x09, /* bLength: Interface Descriptor size */
USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: Interface descriptor type */
0x00, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints 此接口有两个端点 */
0x03, /* bInterfaceClass: HID */
0x00, /* bInterfaceSubClass : 1=BOOT, 0=no boot */
0x00, /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */
0, /* iInterface: Index of string descriptor */
/* HID描述符(后续讲解)*/
/******************** Descriptor of Custom HID HID ********************/
/* 18 */
0x09, /* bLength: HID Descriptor size */
HID_DESCRIPTOR_TYPE, /* bDescriptorType: HID */
0x10, /* bcdHID: HID Class Spec release number */
0x01,
0x00, /* bCountryCode: Hardware target country 国家代码 */
0x01, /* bNumDescriptors: Number of HID class descriptors to follow
类别描述符数目(至少有一个报表描述符)*/
0x22, /* bDescriptorType 报告描述符 */
CUSTOMHID_SIZ_REPORT_DESC, /* wItemLength: Total length of Report descriptor 报告描述符大小 */
0x00, /* 标志类别描述符说明结束 */
/* 端点1描述符 */
/******************** Descriptor of Custom HID endpoints ******************/
/* 27 */
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
0x82, /* bEndpointAddress: Endpoint Address (IN) */
// bit 3...0 : the endpoint number
// bit 6...4 : reserved
// bit 7 : 0(OUT), 1(IN)
0x03, /* bmAttributes: Interrupt endpoint */
0x40, /* wMaxPacketSize: 64 Bytes max */
0x00,
0x02, /* bInterval: Polling Interval (2 ms) */
/* 34 */
/* 端点2描述符 */
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
/* Endpoint descriptor type */
0x01, /* bEndpointAddress: */
/* Endpoint Address (OUT) */
0x03, /* bmAttributes: Interrupt endpoint */
0x40, /* wMaxPacketSize: 64 Bytes max */
0x00,
0x02, /* bInterval: Polling Interval (2 ms) */
/* 41 */
}; /* CustomHID_ConfigDescriptor */
0x09
: 描述符的长度(9字节),这是整个配置描述符的大小。
USB_CONFIGURATION_DESCRIPTOR_TYPE
: 描述符类型,对于配置描述符,这个值通常是0x02
。
CUSTOMHID_SIZ_CONFIG_DESC, 0x00
: 总长度(Total Length)的低字节和高字节。wTotalLength
字段表示整个配置描述符集的大小,包括所有接口、端点和设备类特定的描述符。这里只显示了低字节,高字节设置为0x00
,意味着总长度不超过256字节。
0x01
: 接口数量(bNumInterfaces),表示这个配置中定义的接口数量为1。
0x01
: 配置值(bConfigurationValue),这是一个唯一的值,当主机请求设备改变配置时,它将使用这个值。
0x00
: 配置描述符字符串索引(iConfiguration),它指向一个字符串描述符,描述了这个配置。如果该值为0x00
,则没有字符串描述符描述配置。
0xC0
: 属性(bmAttributes),这是一个位字段,用于描述配置的供电属性和其他特性:
- 第7位(位7)设置为
1
,表示设备是总线供电的(Bus powered)。- 第6位(位6)设置为
0
,表示设备不是自供电的(Self powered)。- 第5位(位5)设置为
0
,表示设备不支持远程唤醒(Remote wakeup)。- 第4到第0位(位4…0)保留,必须设置为
0
。
0x96
: 最大功耗(MaxPower),以2mA为单位的最大功耗值。0x96
转换为十进制是150,所以最大功耗是150 * 2mA = 300mA
。这表示设备在总线供电模式下最多可以消耗300mA的电流。
2.2 沁恒CH32V307代码
/* Configuration Descriptor(HS) */
const uint8_t MyCfgDescr_HS[ ] =
{
/* Configuration Descriptor */
0x09, // bLength
0x02, // bDescriptorType
0x29, 0x00, // wTotalLength
0x01, // bNumInterfaces
0x01, // bConfigurationValue
0x00, // iConfiguration (String Index)
0x80, // bmAttributes
0x23, // bMaxPower 70mA
/* Interface Descriptor */
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x00, // bInterfaceNumber 0
0x00, // bAlternateSetting
0x02, // bNumEndpoints 2
0x03, // bInterfaceClass
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface (String Index)
/* HID Descriptor */
0x09, // bLength
0x21, // bDescriptorType
0x11, 0x01, // bcdHID
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType
0x22, 0x00, // wDescriptorLength
/* Endpoint Descriptor */
0x07, // bLength
0x05, // bDescriptorType
0x01, // bEndpointAddress: OUT Endpoint 1
0x03, // bmAttributes
0x00, 0x02, // wMaxPacketSize
0x01, // bInterval: 1mS
/* Endpoint Descriptor */
0x07, // bLength
0x05, // bDescriptorType
0x82, // bEndpointAddress: IN Endpoint 2
0x03, // bmAttributes
0x00, 0x02, // wMaxPacketSize
0x01, // bInterval: 1mS
};
0x09
: 描述符的长度(9字节),这是整个配置描述符的大小。
0x02
: 描述符类型(bDescriptorType),对于配置描述符,这个值通常是0x02
。
0x29, 0x00
: 总长度(wTotalLength),表示整个配置描述符集的大小,包括所有接口、端点和设备类特定的描述符。这里0x29
是低字节,0x00
是高字节,所以总长度是0x0029
(41字节)。
0x01
: 接口数量(bNumInterfaces),表示这个配置中定义的接口数量为1。
0x01
: 配置值(bConfigurationValue),这是一个唯一的值,当主机请求设备改变配置时,它将使用这个值。
0x00
: 配置描述符字符串索引(iConfiguration),它指向一个字符串描述符,描述了这个配置。如果该值为0x00
,则没有字符串描述符描述配置。
0x80
: 属性(bmAttributes),这是一个位字段,用于描述配置的供电属性和其他特性:
- 第7位(位7)设置为
1
,表示设备是自供电的(Self powered)。- 第6位(位6)设置为
0
,表示设备不是总线供电的(Bus powered)。- 第5位(位5)设置为
0
,表示设备不支持远程唤醒(Remote wakeup)。- 第4到第0位(位4…0)保留,必须设置为
0
。
0x23
: 最大功耗(bMaxPower),以2mA为单位的最大功耗值。0x23
转换为十进制是35,所以最大功耗是35 * 2mA = 70mA
。这表示设备在自供电模式下最多可以消耗70mA的电流。
三、配置描述符组成详解
3.1 bLength
USB Configuration Descriptors 配置描述符的长度(一般长度为 9 字节)。
3.2 bDescriptorType
bDescriptorType 代表了本描述符的类型,设备描述符为 0x01。所有的描述符类型表示如下图,大家以后也可以速查:
3.3 wTotalLength
配置描述符集合总长度,也就是说总共有多少个字节。第二节讲了,配置描述符是以集合的形式,集合里包含了标准配置描述符,接口描述符,端点描述符,HID 描述符,wTotalLength 就是配置描述符集合的长度。
3.4 bNumInterfaces
当前配置下面有多少个接口,单一功能设备只有一个接口,如鼠标或者键盘,如果是复合设备,如果是鼠键一体的设备,那么它可能有两个接口,其实一个接口对应于一种功能,如果我们在软件实现的时候,把鼠标键盘当做一个功能实现的时候,那就是一个接口的设备(也就是单功能设备)。
3.5 bConfigurationValue
前面说了,一个 USB 设备可能有多个配置,但是当前只能选择一种配置。bConfigurationValue 就是当前配置的标识,如果主机想选择哪种配置,是通过标识去选择的,后面枚举的时候会详细讲解。
3.6 iConfiguration
描述该配置的字符串的索引值,如果没有字符串,那这个值就是 0。
3.7 bmAttributes
在这个配置下,设备的一些特性。D7 是保留位,默认为 1;D6 表示供电方式,0 是自供电,1 是总线供电;D5 表示是否支持远程唤醒,为 1 表示设备支持远程唤醒;D4~D0 保留,默认为 0。大家可能会问为什么不把这些特性放在设备描述符里面,这些可都是设备的属性啊,在这里要告诉大家,USB设备会有多种配置,每种配置下的设备属性是不一样的,主机可以灵活的选择使用那种配置,从而实现对应配置上的功能,这样增加了总线设计的灵活性和可配置性,从而达到通用性。
Keyboard 和 Mouse 等消费电子可能需要将该参数项设置为支持远程唤醒!
3.8 bMaxPower
在这个配置下,设备需要的电流,单位是 2ma。如果一个设备耗电量 100ma,那么本字节设置为 0x32 即可。
四、粉丝交流群
嵌入式交流群 1 群:958820627(可能已满);嵌入式交流群 2 群:876919359(可能已满);嵌入式交流群 3 群:957431539(推荐加入)。欢迎加群,有问题可以群内分享技术交流,秋招和春招会有诸多大厂内推码或者内推名额推荐。希望大家友好讨论技术知识!!!