完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
在上一节我们介绍了MM32 MCU的USB模拟U盘功能,通过四个章节把常用的USB设备功能编程一一做了介绍,通常来说,往往希望我们USB设备多个功能可以一起工作,因此我们本节我们讲解USB复合设备。 在USB Specification Revision 2.0定义USB复合装置(Composite Device)为具有多个独立控制接口(Interface)的装置。复合装置是一个具有多个功能和配置多个接口的组合设备,但是其只具有一个唯一的设备(Device)地址,透过相对应的USB设备驱动程式(Device Driver)来与主机通讯。如果复合装置中有单一功能对应多个接口的类别就必须额外使用接口关联描述元(Interface Association Descriptor, IAD),让设备中的不同介面与其相同功能建立关联。 接口(Interface) 复合装置只有一套PID/VID,透过不同的接口定义为不同的类别来实现多个功能。 USB设备必须额外使用接口关联描述元(IAD)来定义多重介面功能的复合装置。复合装置的接口描述元(Interface Descriptor)定义接口的每一个功能,USB主机透过此描述元来得知USB设备可用的功能。 端点(Endpoints) 复合装置的Endpoint分配,除了控制端点用于标准、特定类别和供应商特定的请求,其他Endpoint是给复合装置内的个别功能来使用,使用的传输方式与应用的需求有关。 描述元(Descriptor) 装置描述元(device descriptor) 组态描述元(configuration descriptor) 接口描述元(interface descriptor) 特定的类别描述元(class-specific descriptors) 端点描述元(endpoint descriptor) 字串描述元(String descriptor) 一个USB装置的功能被使用之前,主机会先向USB装置取得组态资料,然后再由USB装置将这些描述元的资料当作组态资料一起传给主机,组态描述元除了本身组态描述元外,另外包含接口描述元、类别描述元和端点描述元,而HID类别则会有特定的类别描述元,此描述元会交代报告(report)描述元和实体(physical)描述元; 本节我们来讲解如何在MM32 MCU实现USB复合设备功能,在前面我们介绍了MM32 实现HID、WINUSB、CDC和MSC功能,MM32系列MCU的USB功能有4个端点,所以我们可以自由组合上述的功能在一起,本节我们实现HID、WINUSB和CDC复合设备。 本次我们采用MM32L373 miniboard作为测试开发板。为了方便大家使用MM32 MCU的复合设备功能,我们已经封装好全部代码,用户不需要自己配置那些麻烦的描述符的关系等,参数已经函数处理好了,用户只需要配置只需要知道用之前的单一设备函数即可。 软件资源如下: 对于MM32 MCU的复合设备,我们可以配置一些参数来作为识别。 #define USBD_STRDESC_MAN L"MM32" #define USBD_STRDESC_PROD L"MM32 Composite" //产品名称#define USBD_HID_STRDESC L"MM32 HID"#define USBD_BULK_STRDESC L"MM32 WINUSB" 参数设置如上可以看到电脑上显示的设备名称,如下: 图1 设备显示名称 根据这些参数,通过封装好的函数直接处理符合设备之间的关系,函数如下: //MM32 USB复合设备类型初始化函数void u***d_class_init(void){ U8 if_num = 0;U16 desc_ptr = 0;desc_ptr += start_desc_fill(&USBD_ConfigDescriptor[desc_ptr], &USBD_ConfigDescriptor_HS[desc_ptr], if_num); #if (USBD_ADC_ENABLE)u***d_adc_init();#endif#if (USBD_MSC_ENABLE) #if !(defined(MSC_BL)) && defined(DRAG_N_DROP_SUPPORT)//change descriptors hereif (config_ram_get_disable_msd() == 1 || flash_algo_valid()==0 ){u***d_if_num -= USBD_MSC_ENABLE;USB_CONFIGURATION_DESCRIPTOR * u***_conf_desc = (USB_CONFIGURATION_DESCRIPTOR *)USBD_ConfigDescriptor;u***_conf_desc->bNumInterfaces = u***d_if_num;U16 u***_wtotal_len = USBD_WTOTALLENGTH_MAX - (USBD_MSC_DESC_LEN * USBD_MSC_ENABLE);u***_conf_desc->wTotalLength = u***_wtotal_len;USBD_ConfigDescriptor[u***_wtotal_len] = 0;USBD_HID_DescriptorOffset -= USBD_MSC_ENABLE * USBD_MSC_DESC_LEN;#if (USBD_HS_ENABLE == 1)u***_conf_desc = (USB_CONFIGURATION_DESCRIPTOR *)USBD_ConfigDescriptor_HS;u***_conf_desc->bNumInterfaces = u***d_if_num;u***_conf_desc->wTotalLength = u***_wtotal_len;USBD_ConfigDescriptor_HS[u***_wtotal_len] = 0;#endif } else#endif{u***d_msc_if_num = if_num++;desc_ptr += msc_desc_fill(&USBD_ConfigDescriptor[desc_ptr], &USBD_ConfigDescriptor_HS[desc_ptr], u***d_msc_if_num);u***d_msc_init();}#endif //#if (USBD_MSC_ENABLE) #if (USBD_CDC_ACM_ENABLE)u***d_cdc_acm_cif_num = if_num++;u***d_cdc_acm_dif_num = if_num++;desc_ptr += acm_cdc_desc_fill(&USBD_ConfigDescriptor[desc_ptr], &USBD_ConfigDescriptor_HS[desc_ptr], u***d_cdc_acm_cif_num);USBD_CDC_ACM_Initialize();#endif#if (USBD_HID_ENABLE) u***d_hid_if_num = if_num++;desc_ptr += hid_desc_fill(&USBD_ConfigDescriptor[desc_ptr], &USBD_ConfigDescriptor_HS[desc_ptr], u***d_hid_if_num);u***d_hid_init();#endif#if (USBD_WEBUSB_ENABLE)u***d_webu***_if_num = if_num++; desc_ptr += webu***_desc_fill(&USBD_ConfigDescriptor[desc_ptr], &USBD_ConfigDescriptor_HS[desc_ptr], u***d_webu***_if_num);#endif#if (USBD_BULK_ENABLE)u***d_bulk_if_num = if_num++; desc_ptr += bulk_desc_fill(&USBD_ConfigDescriptor[desc_ptr], &USBD_ConfigDescriptor_HS[desc_ptr], u***d_bulk_if_num);u***d_bulk_init();#endif#if (USBD_CLS_ENABLE)u***d_cls_init();#endif}//初始的描述配置填充static U16 start_desc_fill(U8 * config_desc, U8 * config_desc_hs, U8 if_num) {U8 * pD = 0;const U8 start_desc[] = { /* Configuration 1 */USB_CONFIGUARTION_DESC_SIZE, // bLength USB_CONFIGURATION_DESCRIPTOR_TYPE, // bDescriptorType WBVAL(USBD_WTOTALLENGTH_MAX), // wTotalLength USBD_IF_NUM_MAX, // bNumInterfaces 0x01, // bConfigurationValue: 0x01 is used to select this configuration 0x00, // iConfiguration: no string to describe this configuration USBD_CFGDESC_BMATTRIBUTES | // bmAttributes (USBD_POWER << 6),USBD_CFGDESC_BMAXPOWER // bMaxPower, device power consumption };pD = config_desc;memcpy(pD, start_desc, sizeof(start_desc));#if (USBD_HS_ENABLE == 1)pD = config_desc_hs;memcpy(pD, start_desc, sizeof(start_desc));#endifreturn sizeof(start_desc);}//HID的描述配置填充static U16 hid_desc_fill(U8 * config_desc, U8 * config_desc_hs, U8 if_num) {U8 * pD = 0;const U8 hid_desc[] = {HID_DESC#if ((USBD_HID_EP_INTOUT != 0) && (USBD_HID_EP_INTIN != 0))HID_EP_INOUT#elif (USBD_HID_EP_INTIN != 0)HID_EP_IN#elif (USBD_HID_EP_INTOUT != 0)HID_EP_OUT#endif};pD = config_desc;memcpy(pD, hid_desc, sizeof(hid_desc));((USB_INTERFACE_DESCRIPTOR *)pD)->bInterfaceNumber = if_num;#if (USBD_HS_ENABLE == 1) const U8 hid_desc_hs[] = {HID_DESC#if ((USBD_HID_EP_INTOUT != 0) && (USBD_HID_EP_INTIN != 0))HID_EP_INOUT_HS#elif (USBD_HID_EP_INTIN != 0) //#elseHID_EP_IN_HS#elif (USBD_HID_EP_INTOUT != 0)HID_EP_OUT_HS#endif};pD = config_desc_hs;memcpy(pD, hid_desc_hs, sizeof(hid_desc_hs));((USB_INTERFACE_DESCRIPTOR *)pD)->bInterfaceNumber = if_num;#endif return sizeof(hid_desc);}//CDC的描述配置填充static U16 acm_cdc_desc_fill(U8 * config_desc, U8 * config_desc_hs, U8 if_num) {U8 * pD = 0;const U8 cdc_desc[] = {#if (USBD_MULTI_IF)CDC_ACM_DESC_IAD(0, 2)#endifCDC_ACM_DESC_IF0CDC_ACM_EP_IF0CDC_ACM_DESC_IF1CDC_ACM_EP_IF1};pD = config_desc;memcpy(pD, cdc_desc, sizeof(cdc_desc));#if (USBD_MULTI_IF)((USB_INTERFACE_ASSOCIATION_DESCRIPTOR *)pD)->bFirstInterface = if_num;pD += USB_INTERFACE_ASSOC_DESC_SIZE;#endif ((USB_INTERFACE_DESCRIPTOR *)pD)->bInterfaceNumber = if_num;pD += USB_INTERFACE_DESC_SIZE + CDC_HEADER_SIZE + CDC_CALL_MANAGEMENT_SIZE + CDC_ABSTRACT_CONTROL_MANAGEMENT_SIZE;((UNION_FUNCTIONAL_DESCRIPTOR*)pD)->bMasterInterface = if_num;((UNION_FUNCTIONAL_DESCRIPTOR*)pD)->bSlaveInterface0 = if_num + 1;pD += CDC_UNION_SIZE + USB_ENDPOINT_DESC_SIZE;((USB_INTERFACE_DESCRIPTOR *)pD)->bInterfaceNumber = if_num + 1; #if (USBD_HS_ENABLE == 1) const U8 cdc_desc_hs[] = {#if (USBD_MULTI_IF)CDC_ACM_DESC_IAD(0, 2)#endifCDC_ACM_DESC_IF0CDC_ACM_EP_IF0_HSCDC_ACM_DESC_IF1CDC_ACM_EP_IF1_HS};pD = config_desc_hs;memcpy(pD, cdc_desc_hs, sizeof(cdc_desc_hs));#if (USBD_MULTI_IF)((USB_INTERFACE_ASSOCIATION_DESCRIPTOR *)pD)->bFirstInterface = if_num;pD += USB_INTERFACE_ASSOC_DESC_SIZE;#endif ((USB_INTERFACE_DESCRIPTOR *)pD)->bInterfaceNumber = if_num;pD += USB_INTERFACE_DESC_SIZE + CDC_HEADER_SIZE + CDC_CALL_MANAGEMENT_SIZE + CDC_ABSTRACT_CONTROL_MANAGEMENT_SIZE;((UNION_FUNCTIONAL_DESCRIPTOR*)pD)->bMasterInterface = if_num;((UNION_FUNCTIONAL_DESCRIPTOR*)pD)->bSlaveInterface0 = if_num + 1;pD += CDC_UNION_SIZE + USB_ENDPOINT_DESC_SIZE;((USB_INTERFACE_DESCRIPTOR *)pD)->bInterfaceNumber = if_num +1 ;#endif //(USBD_HS_ENABLE == 1) return sizeof(cdc_desc);}//MSC的描述配置填充static U16 msc_desc_fill(U8 * config_desc, U8 * config_desc_hs, U8 if_num) {U8 * pD = 0;const U8 msc_desc[] = { MSC_DESCMSC_EP};pD = config_desc;memcpy(pD, msc_desc, sizeof(msc_desc));((USB_INTERFACE_DESCRIPTOR *)pD)->bInterfaceNumber = if_num;#if (USBD_HS_ENABLE == 1) const U8 msc_desc_hs[] = { MSC_DESCMSC_EP_HS};pD = config_desc_hs;memcpy(pD, msc_desc_hs, sizeof(msc_desc_hs));((USB_INTERFACE_DESCRIPTOR *)pD)->bInterfaceNumber = if_num;#endifreturn sizeof(msc_desc);}#if (USBD_WEBUSB_ENABLE)//WEBUSB的描述配置填充static U16 webu***_desc_fill(U8 * config_desc, U8 * config_desc_hs, U8 if_num) {U8 * pD = 0;const U8 webu***_desc[] = {WEBUSB_DESC};pD = config_desc;memcpy(pD, webu***_desc, sizeof(webu***_desc));((USB_INTERFACE_DESCRIPTOR *)pD)->bInterfaceNumber = if_num;#if (USBD_HS_ENABLE == 1)pD = config_desc_hs;memcpy(pD, webu***_desc, sizeof(webu***_desc));((USB_INTERFACE_DESCRIPTOR *)pD)->bInterfaceNumber = if_num;#endif#if (USBD_WINUSB_ENABLE)pD = USBD_WinUSBDescriptorSetDescriptor + WINUSB_DESCRIPTOR_SET_HEADER_SIZE; ((WINUSB_FUNCTION_SUBSET_HEADER*)pD)->bFirstInterface = if_num;#else#error "WEBUSB requires WINUSB!"#endifreturn sizeof(webu***_desc); }#endif#if (USBD_BULK_ENABLE)//BUCK USB的描述配置填充static U16 bulk_desc_fill(U8 * config_desc, U8 * config_desc_hs, U8 if_num) {U8 * pD = 0;const U8 bulk_desc[] = { BULK_DESCBULK_EP};pD = config_desc;memcpy(pD, bulk_desc, sizeof(bulk_desc)); ((USB_INTERFACE_DESCRIPTOR *)pD)->bInterfaceNumber = if_num;#if (USBD_HS_ENABLE == 1) const U8 bulk_desc_hs[] = { BULK_DESCBULK_EP_HS};pD = config_desc_hs;memcpy(pD, bulk_desc_hs, sizeof(bulk_desc_hs)); ((USB_INTERFACE_DESCRIPTOR *)pD)->bInterfaceNumber = if_num;#endif#if (USBD_WINUSB_ENABLE)pD = USBD_WinUSBDescriptorSetDescriptor + WINUSB_DESCRIPTOR_SET_HEADER_SIZE;#if (USBD_WEBUSB_ENABLE)pD += WINUSB_FUNCTION_SUBSET_HEADER_SIZE + WINUSB_FEATURE_COMPATIBLE_ID_SIZE + DEVICE_INTERFACE_GUIDS_FEATURE_LEN;#endif ((WINUSB_FUNCTION_SUBSET_HEADER*)pD)->bFirstInterface = if_num;#else#error "BULK interfaces requires WINUSB!"#endifreturn sizeof(bulk_desc);}#endif 在使用MM32复合设备功能之前先调用USB初始化函数来初始化USB协议栈。 int main(void) { // USB Device Initialization and connect u***d_init(); u***d_connect(__TRUE); while (!u***d_configured()) // Wait for USB Device to configure { } while (1) { …… } } 然后和之前一样实现HID、WINUSB、CDC的函数接口即可,这样我们就完成MM32 MCU的复合设备功能,将程序下载到板子中,USB插上电脑,电脑上会枚举出复合设备,如下显示: |
|
相关推荐
|
|
只有小组成员才能发言,加入小组>>
2248个成员聚集在这个小组
加入小组灵动微电子MM32全系列MCU产品应用手册,库函数和例程和选型表
11676 浏览 3 评论
【MM32 eMiniBoard试用连载】+基于OLED12864的GUI---U8G2
5922 浏览 1 评论
【MM32 eMiniBoard试用连载】移植RT-Thread至MM32L373PS
10953 浏览 0 评论
【MM32 eMiniBoard测评报告】+ 开箱 + 初探
4571 浏览 1 评论
灵动微课堂(第106讲) | MM32 USB功能学习笔记 —— WinUSB设备
4297 浏览 1 评论
[MM32软件] MM32F002使用内部flash存储数据怎么操作?
964浏览 1评论
792浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-24 06:49 , Processed in 0.585553 second(s), Total 59, Slave 44 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号