MM32 USB复合设备讲解
0 2023-03-16

在上一节我们介绍了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 usbd_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)
usbd_adc_init();
#endif
#if (USBD_MSC_ENABLE)       
#if !(defined(MSC_BL)) &&  defined(DRAG_N_DROP_SUPPORT)
//change descriptors here
if (config_ram_get_disable_msd() == 1 || flash_algo_valid()==0 ){
usbd_if_num -= USBD_MSC_ENABLE;
USB_CONFIGURATION_DESCRIPTOR * usb_conf_desc = (USB_CONFIGURATION_DESCRIPTOR *)USBD_ConfigDescriptor;
usb_conf_desc->bNumInterfaces = usbd_if_num;
U16 usb_wtotal_len = USBD_WTOTALLENGTH_MAX - (USBD_MSC_DESC_LEN     * USBD_MSC_ENABLE);
usb_conf_desc->wTotalLength = usb_wtotal_len;
USBD_ConfigDescriptor[usb_wtotal_len] = 0;
USBD_HID_DescriptorOffset -= USBD_MSC_ENABLE * USBD_MSC_DESC_LEN;
#if (USBD_HS_ENABLE == 1)
usb_conf_desc = (USB_CONFIGURATION_DESCRIPTOR *)USBD_ConfigDescriptor_HS;
usb_conf_desc->bNumInterfaces = usbd_if_num;
usb_conf_desc->wTotalLength = usb_wtotal_len;
USBD_ConfigDescriptor_HS[usb_wtotal_len] = 0;
#endif         
} else
#endif
{
usbd_msc_if_num = if_num  ;
desc_ptr  = msc_desc_fill(&USBD_ConfigDescriptor[desc_ptr], &USBD_ConfigDescriptor_HS[desc_ptr], usbd_msc_if_num);
usbd_msc_init();
}
#endif //#if (USBD_MSC_ENABLE)  
#if (USBD_CDC_ACM_ENABLE)
usbd_cdc_acm_cif_num = if_num  ;
usbd_cdc_acm_dif_num = if_num  ;
desc_ptr  = acm_cdc_desc_fill(&USBD_ConfigDescriptor[desc_ptr], &USBD_ConfigDescriptor_HS[desc_ptr], usbd_cdc_acm_cif_num);
USBD_CDC_ACM_Initialize();
#endif
#if (USBD_HID_ENABLE) 
usbd_hid_if_num = if_num  ;
desc_ptr  = hid_desc_fill(&USBD_ConfigDescriptor[desc_ptr], &USBD_ConfigDescriptor_HS[desc_ptr], usbd_hid_if_num);
usbd_hid_init();
#endif
#if (USBD_WEBUSB_ENABLE)
usbd_webusb_if_num = if_num  ;   
desc_ptr  = webusb_desc_fill(&USBD_ConfigDescriptor[desc_ptr], &USBD_ConfigDescriptor_HS[desc_ptr], usbd_webusb_if_num);
#endif
#if (USBD_BULK_ENABLE)
usbd_bulk_if_num = if_num  ;  
desc_ptr  = bulk_desc_fill(&USBD_ConfigDescriptor[desc_ptr], &USBD_ConfigDescriptor_HS[desc_ptr], usbd_bulk_if_num);
usbd_bulk_init();
#endif
#if (USBD_CLS_ENABLE)
usbd_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 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) //#else
HID_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)
#endif
CDC_ACM_DESC_IF0
CDC_ACM_EP_IF0
CDC_ACM_DESC_IF1
CDC_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)
#endif
CDC_ACM_DESC_IF0
CDC_ACM_EP_IF0_HS
CDC_ACM_DESC_IF1
CDC_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_DESC
MSC_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_DESC
MSC_EP_HS
};
pD = config_desc_hs;
memcpy(pD, msc_desc_hs, sizeof(msc_desc_hs));
((USB_INTERFACE_DESCRIPTOR *)pD)->bInterfaceNumber = if_num;
#endif
return sizeof(msc_desc);
}
#if (USBD_WEBUSB_ENABLE)
//WEBUSB的描述配置填充
static U16 webusb_desc_fill(U8 * config_desc, U8 * config_desc_hs, U8 if_num) {
U8 * pD = 0;
const U8 webusb_desc[] = {
WEBUSB_DESC
};
pD = config_desc;
memcpy(pD, webusb_desc, sizeof(webusb_desc));
((USB_INTERFACE_DESCRIPTOR *)pD)->bInterfaceNumber = if_num;
#if (USBD_HS_ENABLE == 1)
pD = config_desc_hs;
memcpy(pD, webusb_desc, sizeof(webusb_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!"
#endif
return sizeof(webusb_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_DESC
BULK_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_DESC
BULK_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!"
#endif
return sizeof(bulk_desc);
}
#endif
 

在使用MM32复合设备功能之前先调用USB初始化函数来初始化USB协议栈。

 

int main(void)

{

// USB Device Initialization and connect

usbd_init();

usbd_connect(__TRUE);

while (!usbd_configured())   // Wait for USB Device to configure

{

}

while (1)

{      

……

}

}

然后和之前一样实现HID、WINUSB、CDC的函数接口即可,这样我们就完成MM32 MCU的复合设备功能,将程序下载到板子中,USB插上电脑,电脑上会枚举出复合设备,如下显示:


图2 设备管理器复合设备枚举列表

从图上可以看到MM32实现的复合设备在电脑上同时显示出HID、WINUSB以及CDC。对于MM32的复合设备来说,和单一设备一样简单方便,使用时只需要加入各自功能的代码即可。 

声明: 本文转载自其它媒体或授权刊载,目的在于信息传递,并不代表本站赞同其观点和对其真实性负责,如有新闻稿件和图片作品的内容、版权以及其它问题的,请联系我们及时删除。(联系我们,邮箱:evan.li@aspencore.com )
0
评论
  • 相关技术文库
  • 手机
  • 消费电子
  • 快充
  • USB
  • 分析触摸屏的电磁干扰源

      OFweek电子工程网,开发具有触摸屏人机界面的移动手持设备是一项复杂的设计挑战,尤其是对于投射式电容触摸屏设计来说更是如此,它代表了当前多点触摸界面的主流

    06-01
  • Android 安卓手机电池省电技巧

    对于Android用户来说,设备的续航可能是一个令人困扰的难题。在使用不当的情况下,多少会出现充电三小时,使用五分钟的揪心现象。在不更换手机、不刷机、保持既定使

    05-30
  • USB鼠标造成电脑死机的几种原因解析

      usb鼠标造成电脑死机原因  一、病毒的原因。  二、硬件的问题。比如电源  三、软件的问题。比如CPU测温监控软件。  四、系统文件丢失,误操作造成的。 

    05-30
  • 同样是2GB内存 手机怎么会比电脑卡顿?

       影响电子设备应用体验最直接的因素就是硬件配置,相同的配置也可能会带来不同的应用体验。不过,很多网友疑问道,手机和电脑同样是2GB的内存,为何手机

    05-29
  • 智能手机快速充电技术标准分支

      你听说过“充电5分钟,通话两小时”吗?没错,这句话讲的就是快速充电技术所发挥的作用。在2015年,支持快速充电技术的移动设备越来越多,如果一部手机

    05-29
  • 智能手机续航解决方案

      体验痛点意味着市场机遇,在终端硬件竞争进入平稳期后,电池续航能力成为一个重要的焦点,终端产业链上下游各方竞相研发低功耗新技术与新方案。本文从产业链视角,剖析

    05-29
  • 投射电容式触摸传感技术,各种典型设计难题

      投射电容式触摸传感技术正在快速发展以满足现代应用对高级用户接口的需求。除了这些应用之外,采样多点触摸和手势检测的消费类产品很受欢迎,它们同样刺激了投射电容式

    05-29
  • 手机电池安全使用的几大误区

      手机早已是人类必备的数字工具之一,且使用频率最为频繁,所以其电池寿命也关乎到使用体验。相对其他电子产品来说,手机电池爆炸、起火的新闻最多,当然一方面是因为基

    05-29
  • 你了解无源驱动(PMOLED)技术吗?

      典型的PM-OLED由玻璃基板、ITO(铟锡氧化物)阳极(Anode)、有机发光层(EmittingMaterialLayer)与阴极(Cathode)等所

    05-28
  • IPS屏幕和AMOLED屏幕分析

    有些手机上面的这块屏幕几乎占据了手机的一半成本,比如最近流行的曲面屏幕手机,一不小心碎了,你说心疼不。屏幕的好坏也间接的影像了消费者的审美和购买欲。手机屏幕呢,

    05-27
  • iPhone 5/6 电池拆解

      就在刚刚,小编分享了自己拆解iPhone5和iPhone6电池的过程,不过,此次操作并没有进行非常深度的拆解。下面,一起来看看苹果在电池制作工艺方面的细节吧

    05-27
  • OLED的这些常用术语你必须了解的

      OLED作为显示技术历史进程中已经被钦定的下一代“市场主宰”,目前已经和液晶技术开始了“权力交接”。首先在高端智能手机市场和高端平板电视市场,OLED技术已

    05-27
下载排行榜
更多
广告