USB Compound Device,USB复合设备
USB Composite Device,USB组合设备
USB Composite Device,不内嵌Hub,几个设备的PID和VID都是相同。
此文均在STM32F767IGTx已实现普通custom HID工程上做解释。
//usb配置描述符
/* USB CUSTOM_HID device Configuration Descriptor */
__ALIGN_BEGIN static uint8_t USBD_CUSTOM_HID_CfgDesc[USB_CUSTOM_HID_CONFIG_DESC_SIZ] __ALIGN_END =
{
0x09, /* bLength: 长度,设备字符串的长度为9字节 */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: 类型,配置描述符的类型 */
USB_CUSTOM_HID_CONFIG_DESC_SIZ, /* wTotalLength: 配置描述符的总长度 */
0x00,
0x01, /*bNumInterfaces: 配置所支持的接口数量1个*/
0x01, /*bConfigurationValue: 该配置的值*/
0x00, /*iConfiguration: 该配置的字符串的索引值,该值为0表示没有字符串*/
0xC0, /*bmAttributes: bus powered */
0x32, /*MaxPower 100 mA: 从总线上获得的最大电流为100mA*/
/************** 接口描述符 ****************/
/* 09 */
0x09, /*bLength: 长度,接口描述符的长度为9字节 */
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: 接口描述符的类型*/
0x00, /*bInterfaceNumber: 该接口的编号*/
0x00, /*bAlternateSetting: 该接口的备用编号*/
0x02, /*bNumEndpoints: 该接口所使用的端点数*/
0x03, /*bInterfaceClass: 该接口所使用的类为HID*/
0x00, /*bInterfaceSubClass : 该接口所用的子类 1=BOOT, 0=no boot*/
0x00, /*nInterfaceProtocol : 该接口使用的协议 0=none, 1=keyboard, 2=mouse*/
0, /*iInterface: 该接口字符串的索引*/
/******************** HID描述符 *************************/
/* 18 */
0x09, /*bLength: HID描述符的长度为9字节*/
CUSTOM_HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID的描述符类型*/
0x11, /*bCUSTOM_HIDUSTOM_HID: HID协议的版本*/
0x01,
0x00, /*bCountryCode: 国家代号*/
0x01, /*bNumDescriptors: 下级描述符的数量*/
0x22, /*bDescriptorType: 下级描述符的类型*/
USBD_CUSTOM_HID_REPORT_DESC_SIZE,/*wItemLength: 下一集描述符的长度*/
0x00,
/******************** 输入端点描述符 ********************/
/* 27 */
0x07, /*bLength: 端点描述符的长度为7字节*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType: 端点描述符的类型*/
CUSTOM_HID_EPIN_ADDR, /*bEndpointAddress: 该端点(输入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端点号*/
0x03, /*bmAttributes: 端点的属性为为中断端点. D0~D1表示传输类型:0(控制传输),
1(等时传输),2(批量传输),3(中断传输) 非等时传输端点:D2~D7:保留为0 等时传输端点:
D2~D3表示同步的类型:0(无同步),1(异步),2(适配),3(同步) D4~D5表示用途:0(数据端点),
1(反馈端点),2(暗含反馈的数据端点),3(保留),D6~D7:保留,*/
CUSTOM_HID_EPIN_SIZE, /*wMaxPacketSize: 该端点支持的最大包长度 */
0x00,
0x01,//0x20, /*bInterval: 轮询间隔*/
/* 34 */
/******************** 输出端点描述符 ********************/
0x07, /* bLength: 端点描述符的长度为7字节 */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: 端点描述符的类型 */
CUSTOM_HID_EPOUT_ADDR, /*bEndpointAddress: 该端点(输入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端点号*/
0x03, /* bmAttributes: 端点的属性为为中断端点. D0~D1表示传输类型:0(控制传输),1(等时传输),
2(批量传输),3(中断传输) 非等时传输端点:D2~D7:保留为0 等时传输端点:
D2~D3表示同步的类型:0(无同步),1(异步),2(适配),3(同步) D4~D5表示用途:0(数据端点),
1(反馈端点),2(暗含反馈的数据端点),3(保留),D6~D7:保留 */
CUSTOM_HID_EPOUT_SIZE, /* wMaxPacketSize: 该端点支持的最大包长度 */
0x00,
0x01,//0x20, /* bInterval: 轮询间隔 */
/* 41 */
} ;
在这个配置描述符里可以注意到bNumInterfaces,详细查询可以查到这个意思是该配置包含的接口数,一般配置均配置的1,如果配置大于1,则为复合设备,即多个接口。
可以看到最开始的9个字节是整个USB的描述,而后面都和接口相关,所以尝试直接复制后面的接口描述符、HID描述符、输入端点描述符、输出端点描述符,并修改相关的接口地址和描述符总长度。但测试发现枚举失败,PC未能识别。
查了网上的资料后发现第二个接口的HID描述符并不需要再添加,于是删除。修改第二个接口的编号bInterfaceNumber,并将报告描述符的数据也安全复制一份放入报告描述符内,继续测试,依然枚举失败,确实不可能就这么简单。
再查资料发现网上说标准设备描述符和报告描述符都不需要修改,只需要修改配置描述符即可,于是将报告描述符复原。另外发现网上成功例子中的第二个接口没有添加HID描述符,也将HID描述符删除,修改好相应的大小。配置描述符中第3个字节wTotalLength为配置描述符的总长度,也需要修改为正确的大小。配置描述符中bAlternateSetting为接口的编号,两个接口的编号不应相同。
配置好后测试枚举成功,接口1正常,但接口2不正常,如下图所示。
以下为修改好的配置描述符
__ALIGN_BEGIN static uint8_t USBD_CUSTOM_HID_CfgDesc[64] __ALIGN_END =
{
0x09, /* bLength: 长度,设备字符串的长度为9字节 */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: 类型,配置描述符的类型 */
0x40,//USB_CUSTOM_HID_CONFIG_DESC_SIZ, /* wTotalLength: 配置描述符的总长度 */
0x00,
0x02,//0x01, /*bNumInterfaces: 配置所支持的接口数量1个*/
0x01, /*bConfigurationValue: 该配置的值*/
0x00, /*iConfiguration: 该配置的字符串的索引值,该值为0表示没有字符串*/
0xC0, /*bmAttributes: bus powered */
0x32, /*MaxPower 100 mA: 从总线上获得的最大电流为100mA*/
/************** 接口描述符 ****************/
/* 09 */
0x09, /*bLength: 长度,接口描述符的长度为9字节 */
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: 接口描述符的类型*/
0x00, /*bInterfaceNumber: 该接口的编号*/
0x00, /*bAlternateSetting: 该接口的备用编号*/
0x02, /*bNumEndpoints: 该接口所使用的端点数*/
0x03, /*bInterfaceClass: 该接口所使用的类为HID*/
0x00, /*bInterfaceSubClass : 该接口所用的子类 1=BOOT, 0=no boot*/
0x00, /*nInterfaceProtocol : 该接口使用的协议 0=none, 1=keyboard, 2=mouse*/
0, /*iInterface: 该接口字符串的索引*/
/******************** HID描述符 *************************/
/* 18 */
0x09, /*bLength: HID描述符的长度为9字节*/
CUSTOM_HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID的描述符类型*/
0x11, /*bCUSTOM_HIDUSTOM_HID: HID协议的版本*/
0x01,
0x00, /*bCountryCode: 国家代号*/
0x01, /*bNumDescriptors: 下级描述符的数量*/
0x22, /*bDescriptorType: 下级描述符的类型*/
USBD_CUSTOM_HID_REPORT_DESC_SIZE,/*wItemLength: 下一集描述符的长度*/
0x00,
/******************** 输入端点描述符 ********************/
/* 27 */
0x07, /*bLength: 端点描述符的长度为7字节*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType: 端点描述符的类型*/
CUSTOM_HID_EPIN_ADDR, /*bEndpointAddress: 该端点(输入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端点号*/
0x03, /*bmAttributes: 端点的属性为为中断端点. D0~D1表示传输类型:0(控制传输),
1(等时传输),2(批量传输),3(中断传输) 非等时传输端点:D2~D7:保留为0 等时传输端点:
D2~D3表示同步的类型:0(无同步),1(异步),2(适配),3(同步) D4~D5表示用途:0(数据端点),
1(反馈端点),2(暗含反馈的数据端点),3(保留),D6~D7:保留,*/
CUSTOM_HID_EPIN_SIZE, /*wMaxPacketSize: 该端点支持的最大包长度 */
0x00,
0x01,//0x20, /*bInterval: 轮询间隔*/
/* 34 */
/******************** 输出端点描述符 ********************/
0x07, /* bLength: 端点描述符的长度为7字节 */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: 端点描述符的类型 */
CUSTOM_HID_EPOUT_ADDR, /*bEndpointAddress: 该端点(输入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端点号*/
0x03, /* bmAttributes: 端点的属性为为中断端点. D0~D1表示传输类型:0(控制传输),1(等时传输),
2(批量传输),3(中断传输) 非等时传输端点:D2~D7:保留为0 等时传输端点:
D2~D3表示同步的类型:0(无同步),1(异步),2(适配),3(同步) D4~D5表示用途:0(数据端点),
1(反馈端点),2(暗含反馈的数据端点),3(保留),D6~D7:保留 */
CUSTOM_HID_EPOUT_SIZE, /* wMaxPacketSize: 该端点支持的最大包长度 */
0x00,
0x01,//0x20, /* bInterval: 轮询间隔 */
/* 41 */
/************** 接口描述符 ****************/
/* 41 */
0x09, /*bLength: 长度,接口描述符的长度为9字节 */
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: 接口描述符的类型*/
0x01,//0x00, /*bInterfaceNumber: 该接口的编号*/
0x00, /*bAlternateSetting: 该接口的备用编号*/
0x02, /*bNumEndpoints: 该接口所使用的端点数*/
0x03, /*bInterfaceClass: 该接口所使用的类为HID*/
0x00, /*bInterfaceSubClass : 该接口所用的子类 1=BOOT, 0=no boot*/
0x00, /*nInterfaceProtocol : 该接口使用的协议 0=none, 1=keyboard, 2=mouse*/
1,//0, /*iInterface: 该接口字符串的索引*/
#if 0
/******************** HID描述符 *************************/
/* 50 */
0x09, /*bLength: HID描述符的长度为9字节*/
CUSTOM_HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID的描述符类型*/
0x11, /*bCUSTOM_HIDUSTOM_HID: HID协议的版本*/
0x01,
0x00, /*bCountryCode: 国家代号*/
0x01, /*bNumDescriptors: 下级描述符的数量*/
0x22, /*bDescriptorType: 下级描述符的类型*/
USBD_CUSTOM_HID_REPORT_DESC_SIZE,/*wItemLength: 下一集描述符的长度*/
0x00,
#endif
/******************** 输入端点描述符 ********************/
/* 59 */
0x07, /*bLength: 端点描述符的长度为7字节*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType: 端点描述符的类型*/
0x82,//CUSTOM_HID_EPIN_ADDR, /*bEndpointAddress: 该端点(输入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端点号*/
0x03, /*bmAttributes: 端点的属性为为中断端点. D0~D1表示传输类型:0(控制传输),
1(等时传输),2(批量传输),3(中断传输) 非等时传输端点:D2~D7:保留为0 等时传输端点:
D2~D3表示同步的类型:0(无同步),1(异步),2(适配),3(同步) D4~D5表示用途:0(数据端点),
1(反馈端点),2(暗含反馈的数据端点),3(保留),D6~D7:保留,*/
CUSTOM_HID_EPIN_SIZE, /*wMaxPacketSize: 该端点支持的最大包长度 */
0x00,
0x01,//0x20, /*bInterval: 轮询间隔*/
/* 66 */
/******************** 输出端点描述符 ********************/
0x07, /* bLength: 端点描述符的长度为7字节 */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: 端点描述符的类型 */
0x02,//CUSTOM_HID_EPOUT_ADDR, /*bEndpointAddress: 该端点(输入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端点号*/
0x03, /* bmAttributes: 端点的属性为为中断端点. D0~D1表示传输类型:0(控制传输),1(等时传输),
2(批量传输),3(中断传输) 非等时传输端点:D2~D7:保留为0 等时传输端点:
D2~D3表示同步的类型:0(无同步),1(异步),2(适配),3(同步) D4~D5表示用途:0(数据端点),
1(反馈端点),2(暗含反馈的数据端点),3(保留),D6~D7:保留 */
CUSTOM_HID_EPOUT_SIZE, /* wMaxPacketSize: 该端点支持的最大包长度 */
0x00,
0x01,//0x20, /* bInterval: 轮询间隔 */
/* 73 */
};
接口2调试好后再添加后续修改内容。
接口2显示不正常原因为配置描述符不正确,多个interface中每个interface都应该包含接口描述符、HID描述符、端点描述符,上面看的配置描述符的第二个interface中没有包含HID描述符,所以正确的配置描述符应为如下所示。
__ALIGN_BEGIN static uint8_t USBD_CUSTOM_HID_CfgDesc[73] __ALIGN_END =
{
0x09, /* bLength: 长度,设备字符串的长度为9字节 */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: 类型,配置描述符的类型 */
0x49,//0x40,//USB_CUSTOM_HID_CONFIG_DESC_SIZ, /* wTotalLength: 配置描述符的总长度 */
0x00,
0x02,//0x01, /*bNumInterfaces: 配置所支持的接口数量1个*/
0x01, /*bConfigurationValue: 该配置的值*/
0x00, /*iConfiguration: 该配置的字符串的索引值,该值为0表示没有字符串*/
0xC0, /*bmAttributes: bus powered */
0x32, /*MaxPower 100 mA: 从总线上获得的最大电流为100mA*/
/************** 接口描述符 ****************/
/* 09 */
0x09, /*bLength: 长度,接口描述符的长度为9字节 */
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: 接口描述符的类型*/
0x00, /*bInterfaceNumber: 该接口的编号*/
0x00, /*bAlternateSetting: 该接口的备用编号*/
0x02, /*bNumEndpoints: 该接口所使用的端点数*/
0x03, /*bInterfaceClass: 该接口所使用的类为HID*/
0x00, /*bInterfaceSubClass : 该接口所用的子类 1=BOOT, 0=no boot*/
0x00, /*nInterfaceProtocol : 该接口使用的协议 0=none, 1=keyboard, 2=mouse*/
0, /*iInterface: 该接口字符串的索引*/
/******************** HID描述符 *************************/
/* 18 */
0x09, /*bLength: HID描述符的长度为9字节*/
CUSTOM_HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID的描述符类型*/
0x11, /*bCUSTOM_HIDUSTOM_HID: HID协议的版本*/
0x01,
0x00, /*bCountryCode: 国家代号*/
0x01, /*bNumDescriptors: 下级描述符的数量*/
0x22, /*bDescriptorType: 下级描述符的类型*/
USBD_CUSTOM_HID_REPORT_DESC_SIZE,/*wItemLength: 下一集描述符的长度*/
0x00,
/******************** 输入端点描述符 ********************/
/* 27 */
0x07, /*bLength: 端点描述符的长度为7字节*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType: 端点描述符的类型*/
CUSTOM_HID_EPIN_ADDR, /*bEndpointAddress: 该端点(输入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端点号*/
0x03, /*bmAttributes: 端点的属性为为中断端点. D0~D1表示传输类型:0(控制传输),
1(等时传输),2(批量传输),3(中断传输) 非等时传输端点:D2~D7:保留为0 等时传输端点:
D2~D3表示同步的类型:0(无同步),1(异步),2(适配),3(同步) D4~D5表示用途:0(数据端点),
1(反馈端点),2(暗含反馈的数据端点),3(保留),D6~D7:保留,*/
CUSTOM_HID_EPIN_SIZE, /*wMaxPacketSize: 该端点支持的最大包长度 */
0x00,
0x01,//0x20, /*bInterval: 轮询间隔*/
/* 34 */
/******************** 输出端点描述符 ********************/
0x07, /* bLength: 端点描述符的长度为7字节 */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: 端点描述符的类型 */
CUSTOM_HID_EPOUT_ADDR, /*bEndpointAddress: 该端点(输入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端点号*/
0x03, /* bmAttributes: 端点的属性为为中断端点. D0~D1表示传输类型:0(控制传输),1(等时传输),
2(批量传输),3(中断传输) 非等时传输端点:D2~D7:保留为0 等时传输端点:
D2~D3表示同步的类型:0(无同步),1(异步),2(适配),3(同步) D4~D5表示用途:0(数据端点),
1(反馈端点),2(暗含反馈的数据端点),3(保留),D6~D7:保留 */
CUSTOM_HID_EPOUT_SIZE, /* wMaxPacketSize: 该端点支持的最大包长度 */
0x00,
0x01,//0x20, /* bInterval: 轮询间隔 */
/* 41 */
/************** 接口描述符 ****************/
/* 41 */
0x09, /*bLength: 长度,接口描述符的长度为9字节 */
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: 接口描述符的类型*/
0x01,//0x00, /*bInterfaceNumber: 该接口的编号*/
0x00, /*bAlternateSetting: 该接口的备用编号*/
0x02, /*bNumEndpoints: 该接口所使用的端点数*/
0x03, /*bInterfaceClass: 该接口所使用的类为HID*/
0x00, /*bInterfaceSubClass : 该接口所用的子类 1=BOOT, 0=no boot*/
0x00, /*nInterfaceProtocol : 该接口使用的协议 0=none, 1=keyboard, 2=mouse*/
1,//0, /*iInterface: 该接口字符串的索引*/
#if 1
/******************** HID描述符 *************************/
/* 50 */
0x09, /*bLength: HID描述符的长度为9字节*/
CUSTOM_HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID的描述符类型*/
0x11, /*bCUSTOM_HIDUSTOM_HID: HID协议的版本*/
0x01,
0x00, /*bCountryCode: 国家代号*/
0x01, /*bNumDescriptors: 下级描述符的数量*/
0x22, /*bDescriptorType: 下级描述符的类型*/
USBD_CUSTOM_HID_REPORT_DESC_SIZE,/*wItemLength: 下一集描述符的长度*/
0x00,
#endif
/******************** 输入端点描述符 ********************/
/* 59 */
0x07, /*bLength: 端点描述符的长度为7字节*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType: 端点描述符的类型*/
0x82,//CUSTOM_HID_EPIN_ADDR, /*bEndpointAddress: 该端点(输入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端点号*/
0x03, /*bmAttributes: 端点的属性为为中断端点. D0~D1表示传输类型:0(控制传输),
1(等时传输),2(批量传输),3(中断传输) 非等时传输端点:D2~D7:保留为0 等时传输端点:
D2~D3表示同步的类型:0(无同步),1(异步),2(适配),3(同步) D4~D5表示用途:0(数据端点),
1(反馈端点),2(暗含反馈的数据端点),3(保留),D6~D7:保留,*/
CUSTOM_HID_EPIN_SIZE, /*wMaxPacketSize: 该端点支持的最大包长度 */
0x00,
0x01,//0x20, /*bInterval: 轮询间隔*/
/* 66 */
/******************** 输出端点描述符 ********************/
0x07, /* bLength: 端点描述符的长度为7字节 */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: 端点描述符的类型 */
0x02,//CUSTOM_HID_EPOUT_ADDR, /*bEndpointAddress: 该端点(输入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端点号*/
0x03, /* bmAttributes: 端点的属性为为中断端点. D0~D1表示传输类型:0(控制传输),1(等时传输),
2(批量传输),3(中断传输) 非等时传输端点:D2~D7:保留为0 等时传输端点:
D2~D3表示同步的类型:0(无同步),1(异步),2(适配),3(同步) D4~D5表示用途:0(数据端点),
1(反馈端点),2(暗含反馈的数据端点),3(保留),D6~D7:保留 */
CUSTOM_HID_EPOUT_SIZE, /* wMaxPacketSize: 该端点支持的最大包长度 */
0x00,
0x01,//0x20, /* bInterval: 轮询间隔 */
/* 73 */
};
另附上报告描述符
__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
0x06, 0x99, 0xff, // Usage Page (HS_HID_DEMO_HID_USAGE_PAGE)
0x09, 0x01, // Usage (HS_HID_DEMO_HID_USAGE_1)
0xA1, 0x01, // Collection (Application)
0x75, 0x08, // Report Size (8)
0x95, 0x40, // Report Count (64)
0x15, 0x00, // Logical Minimum (0)
0x25, 0xFF, // Logical Maximum (255)
0x19, 0, // Usage Minimum (HS_HID_DEMO_HID_USAGE_0)
0x29, 0x3f, // Usage Maximum (HS_HID_DEMO_HID_USAGE_63)
0x81, 0x02, // Input (Data,Var,Abs)
0x19, 0, // Usage Minimum (HS_HID_DEMO_HID_USAGE_0)
0x29, 0x3f, // Usage Maximum (HS_HID_DEMO_HID_USAGE_63)
0x91, 0x02, // Output (Data,Var,Abs)
0xC0 // End Collection
//28
};
参考资料:http://www.stm32cube.com/question/540