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

STM32实现USB的CDC+MSC+AUDIO的USB复合设备

在 STM32F407 上开发 USB 功能时,STM32Cube 默认生成的代码通常只能支持单一设备类型,例如仅能作为虚拟串口(CDC)、存储设备(MSC)或音频设备(Audio)之一使用。
然而,在实际应用中,我们常常希望设备能够“一心多用”,比如:

  • 既能作为 U 盘 与电脑进行文件传输(MSC);

  • 又能提供 虚拟串口 用于调试和数据通信(CDC);

  • 同时还能充当 音频输出设备 播放声音(Audio)。

要实现这样的多功能设备,就需要将 CDC、MSC 和 Audio 三种 USB 类合并到同一个 USB 描述符中,从而让主机在枚举时能同时识别并挂载这些功能。本文将介绍具体的实现方法和步骤。

1. 分别生成和调试单设备

使用 STM32CubeMX 分别生成 CDC、MSC 和 Audio 的单独工程,并确保能正常运行:

  • CDCAudio 的代码基本可以直接使用;

  • MSC 需要在 usbd_storage_if.c 中补充存储层实现(可参考我之前的文章)。

2. 合并工程代码

将 CDC、MSC、Audio 三个类相关文件合并到同一个工程中,主要包含以下内容:


  • CDC 相关

    Middlewares\ST\STM32_USB_Device_Library\Class\CDC
    USB_DEVICE\App\usbd_cdc_if.c/h

  • MSC 相关

    Middlewares\ST\STM32_USB_Device_Library\Class\MSC
    USB_DEVICE\App\usbd_storage_if.c/h
    
  • Audio 相关

    Middlewares\ST\STM32_USB_Device_Library\Class\AUDIO
    USB_DEVICE\App\usbd_audio_if.c/h
    

我这里是基于 Audio 工程 添加 MSC 和 CDC 模块的。
另外,在 USB_DEVICE\Target\usbd_conf.h 中增加:

#define MSC_MEDIA_PACKET 512U

3. 分配端点号

确保各个类的 IN/OUT 端点号不冲突。
STM32F407 除了 EP0,只有 EP1、EP2、EP3 可用,需合理分配:

  • CDC

    
    
    #define CDC_IN_EP    0x81U  /* EP1 IN  */
    #define CDC_OUT_EP   0x01U  /* EP1 OUT */
    #define CDC_CMD_EP   0x82U  /* EP2 CMD */
    
  • MSC

    
    
    #define MSC_EPIN_ADDR   0x83U
    #define MSC_EPOUT_ADDR  0x03U
    
  • Audio

    #define AUDIO_OUT_EP    0x02U
    

4. 配置 USB FIFO

USB_DEVICE\Target\usbd_conf.cUSBD_LL_Init 函数中,重新分配 FIFO 空间。
具体设置可参考下图


具体用法参考链接:usb fifo设置

5. 启用复合设备支持


添加全局的宏USE_USBD_COMPOSITE

6. 配置支持多个 Class 和 Interface

usbd_conf.h 中增加:

#define USBD_MAX_SUPPORTED_CLASS   3
#define USBD_MAX_NUM_INTERFACES    6

说明:

  • CDC Class + MSC Class + AUDIO Class 一共3个Class

  • CDC: Control + Data 共 2 个接口

  • MSC: 1 个接口

  • Audio: Control + Streaming 共 2 个接口

  • 合计至少 5 个接口,这里配置为 6 更保险。

7. 定义配置描述符大小和接口数量

usbd_conf.h 中根据宏开关分别定义:


#define USBD_AUDIO_CMPSIT_ENABLE        1
#define USBD_MSC_CMPSIT_ENABLE          1
#define USBD_CDC_CMPSIT_ENABLE          1#if (USBD_AUDIO_CMPSIT_ENABLE == 1)
#define USB_CMPSIT_AUDIO_CONFIG_DESC_SIZ (USB_AUDIO_CONFIG_DESC_SIZ-9+8)
#define USBD_AUDIO_INTERFACES_NUM       2
#else
#define USB_CMPSIT_AUDIO_CONFIG_DESC_SIZ 0
#define USBD_AUDIO_INTERFACES_NUM      0
#endif#if (USBD_MSC_CMPSIT_ENABLE == 1)
#define USB_CMPSIT_MSC_CONFIG_DESC_SIZ  (USB_MSC_CONFIG_DESC_SIZ-9+8)
#define USBD_MSC_INTERFACES_NUM         1
#else
#define USB_CMPSIT_MSC_CONFIG_DESC_SIZ 0
#define USBD_MSC_INTERFACES_NUM      0
#endif#if (USBD_CDC_CMPSIT_ENABLE == 1)
#define USB_CMPSIT_CDC_CONFIG_DESC_SIZ (USB_CDC_CONFIG_DESC_SIZ-9+8)
#define USBD_CDC_INTERFACES_NUM         2
#else
#define USB_CMPSIT_CDC_CONFIG_DESC_SIZ 0
#define USBD_CDC_INTERFACES_NUM      0
#endif

8. 实现复合类接口

CubeMX 生成的代码中已经有复合设备的相关框架,只需:

  • 定义宏 USE_USBD_COMPOSITE

  • 新建 usbd_composite.c/h,实现 USBD_CMPSIT 结构体和 USBD_CMPSIT_AddClass() 函数。

这样就能让主机正确识别并挂载 CDC + MSC + Audio 三个功能。




usbd_composite.c内容如下:

#include "usbd_composite.h"
#include "usbd_def.h"
#include "usbd_cdc.h"
#include "usbd_msc.h"
#include "usbd_audio.h"#include "usbd_core.h"
#include "usbd_desc.h"
#include "usbd_conf.h"
#include <stdio.h>
#include "log_debug.h"#define USBD_LOG log_debug
// #define USBD_LOG#define USBD_PRODUCT_XSTR(s) USBD_PRODUCT_STR(s)
#define USBD_PRODUCT_STR(s) #s#if 1
#define USBD_CMPSIT_VID     1155
#define USBD_CMPSIT_LANGID_STRING     1033
#define USBD_CMPSIT_MANUFACTURER_STRING     "wenyz@wolfGroup"
#define USBD_CMPSIT_PID_FS     22336
#define USBD_CMPSIT_PRODUCT_STRING_FS     "STM32 composite product"
#define USBD_CMPSIT_CONFIGURATION_STRING_FS     "Composite Config"
#define USBD_CMPSIT_INTERFACE_STRING_FS     "Composite Interface"
#else
#define USBD_CMPSIT_VID     1155
#define USBD_CMPSIT_LANGID_STRING     1033
#define USBD_CMPSIT_MANUFACTURER_STRING     "STMicroelectronics"
#define USBD_CMPSIT_PID_FS     22336
#define USBD_CMPSIT_PRODUCT_STRING_FS     "STM32 Virtual ComPort"
#define USBD_CMPSIT_CONFIGURATION_STRING_FS     "CDC Config"
#define USBD_CMPSIT_INTERFACE_STRING_FS     "CDC Interface"
#endif#define USBD_CDC_INTERFACE_STRING_FS     "CDC Interface"
#define USBD_MSC_INTERFACE_STRING_FS     "MSC Interface"
#define USBD_AUDIO_INTERFACE_STRING_FS     "AUDIO Interface"#define AUDIO_SAMPLE_FREQ(frq) \(uint8_t)(frq), (uint8_t)((frq >> 8)), (uint8_t)((frq >> 16))#define AUDIO_PACKET_SZE(frq) \(uint8_t)(((frq * 2U * 2U) / 1000U) & 0xFFU), (uint8_t)((((frq * 2U * 2U) / 1000U) >> 8) & 0xFFU)#ifdef USE_USBD_COMPOSITE
#define AUDIO_PACKET_SZE_WORD(frq)     (uint32_t)((((frq) * 2U * 2U)/1000U))
#endif /* USE_USBD_COMPOSITE  */uint8_t * USBD_CMPSIT_FS_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t * USBD_CMPSIT_FS_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t * USBD_CMPSIT_FS_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t * USBD_CMPSIT_FS_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t * USBD_CMPSIT_FS_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t * USBD_CMPSIT_FS_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t * USBD_CMPSIT_FS_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
#if (USBD_LPM_ENABLED == 1)
uint8_t * USBD_CMPSIT_FS_USR_BOSDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
#endif /* (USBD_LPM_ENABLED == 1) */USBD_DescriptorsTypeDef usbCmpsitFS_Desc =
{USBD_CMPSIT_FS_DeviceDescriptor
, USBD_CMPSIT_FS_LangIDStrDescriptor
, USBD_CMPSIT_FS_ManufacturerStrDescriptor
, USBD_CMPSIT_FS_ProductStrDescriptor
, USBD_CMPSIT_FS_SerialStrDescriptor
, USBD_CMPSIT_FS_ConfigStrDescriptor
, USBD_CMPSIT_FS_InterfaceStrDescriptor
#if (USBD_LPM_ENABLED == 1)
, USBD_CMPSIT_FS_USR_BOSDescriptor
#endif /* (USBD_LPM_ENABLED == 1) */
};__ALIGN_BEGIN uint8_t USBD_CMPSIT_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
{0x12,						/*bLength */USB_DESC_TYPE_DEVICE, 	  /*bDescriptorType*/
#if (USBD_LPM_ENABLED == 1)0x01, 					  /*bcdUSB */ /* changed to USB version 2.01in order to support LPM L1 suspendresume test of USBCV3.0*/
#else0x00, 					  /*bcdUSB */
#endif /* (USBD_LPM_ENABLED == 1) */0x02,// Notify OS that this is a composite device0xEF, 					  /*bDeviceClass*/0x02, 					  /*bDeviceSubClass*/0x01, 					  /*bDeviceProtocol*/USB_MAX_EP0_SIZE, 		  /*bMaxPacketSize*/LOBYTE(USBD_CMPSIT_VID), 		  /*idVendor*/HIBYTE(USBD_CMPSIT_VID), 		  /*idVendor*/LOBYTE(USBD_CMPSIT_PID_FS),		  /*idProduct*/HIBYTE(USBD_CMPSIT_PID_FS),		  /*idProduct*/0x00, 					  /*bcdDevice rel. 2.00*/0x02, 					  /* bNumInterfaces */USBD_IDX_MFC_STR, 		  /*Index of manufacturer  string*/USBD_IDX_PRODUCT_STR, 	  /*Index of product string*/USBD_IDX_SERIAL_STR,		  /*Index of serial number string*/USBD_MAX_NUM_CONFIGURATION  /*bNumConfigurations*/};#if (USBD_LPM_ENABLED == 1)
__ALIGN_BEGIN uint8_t USBD_CMPSIT_FS_BOSDesc[USB_SIZ_BOS_DESC] __ALIGN_END =
{0x5,USB_DESC_TYPE_BOS,0xC,0x0,0x1,  /* 1 device capability*//* device capability*/0x7,USB_DEVICE_CAPABITY_TYPE,0x2,0x2,  /* LPM capability bit set*/0x0,0x0,0x0
};
#endif /* (USBD_LPM_ENABLED == 1) *//** USB lang identifier descriptor. */
__ALIGN_BEGIN uint8_t USBD_CMPSIT_LangIDDesc[USB_LEN_LANGID_STR_DESC] __ALIGN_END =
{USB_LEN_LANGID_STR_DESC,USB_DESC_TYPE_STRING,LOBYTE(USBD_CMPSIT_LANGID_STRING),HIBYTE(USBD_CMPSIT_LANGID_STRING)
};/* Internal string descriptor. */
__ALIGN_BEGIN uint8_t USBD_CMPSIT_StrDesc[USBD_MAX_STR_DESC_SIZ] __ALIGN_END;__ALIGN_BEGIN uint8_t USBD_CMPSIT_StringSerial[USB_SIZ_STRING_SERIAL] __ALIGN_END = {USB_SIZ_STRING_SERIAL,USB_DESC_TYPE_STRING,
};uint8_t * USBD_CMPSIT_FS_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{USBD_LOG("USBD_CMPSIT_FS_DeviceDescriptor, speed %d, length %d\n", speed, *length);UNUSED(speed);*length = sizeof(USBD_CMPSIT_FS_DeviceDesc);return USBD_CMPSIT_FS_DeviceDesc;
}uint8_t * USBD_CMPSIT_FS_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{USBD_LOG("USBD_CMPSIT_FS_LangIDStrDescriptor, speed %d, length %d\n", speed, *length);UNUSED(speed);*length = sizeof(USBD_CMPSIT_LangIDDesc);return USBD_CMPSIT_LangIDDesc;
}
uint8_t * USBD_CMPSIT_FS_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{USBD_LOG("USBD_CMPSIT_FS_ManufacturerStrDescriptor, speed %d, length %d\n", speed, *length);UNUSED(speed);USBD_GetString((uint8_t *)USBD_CMPSIT_MANUFACTURER_STRING, USBD_CMPSIT_StrDesc, length);return USBD_CMPSIT_StrDesc;
}uint8_t * USBD_CMPSIT_FS_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{USBD_LOG("USBD_CMPSIT_FS_ProductStrDescriptor, speed %d, length %d\n", speed, *length);if(speed == 0){USBD_GetString((uint8_t *)USBD_CMPSIT_PRODUCT_STRING_FS, USBD_CMPSIT_StrDesc, length);}else{USBD_GetString((uint8_t *)USBD_CMPSIT_PRODUCT_STRING_FS, USBD_CMPSIT_StrDesc, length);}return USBD_CMPSIT_StrDesc;
}static void IntToUnicode(uint32_t value, uint8_t * pbuf, uint8_t len)
{uint8_t idx = 0;for (idx = 0; idx < len; idx++){if (((value >> 28)) < 0xA){pbuf[2 * idx] = (value >> 28) + '0';}else{pbuf[2 * idx] = (value >> 28) + 'A' - 10;}value = value << 4;pbuf[2 * idx + 1] = 0;}
}
static void Get_CMPSIT_SerialNum(void)
{USBD_LOG("Get_CMPSIT_SerialNum\n");uint32_t deviceserial0, deviceserial1, deviceserial2;deviceserial0 = *(uint32_t *) DEVICE_ID1;deviceserial1 = *(uint32_t *) DEVICE_ID2;deviceserial2 = *(uint32_t *) DEVICE_ID3;deviceserial0 += deviceserial2;if (deviceserial0 != 0){IntToUnicode(deviceserial0, &USBD_CMPSIT_StringSerial[2], 8);IntToUnicode(deviceserial1, &USBD_CMPSIT_StringSerial[18], 4);}
}
uint8_t * USBD_CMPSIT_FS_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{USBD_LOG("USBD_CMPSIT_FS_SerialStrDescriptor, speed %d, length %d\n", speed, *length);UNUSED(speed);*length = USB_SIZ_STRING_SERIAL;/* Update the serial number string descriptor with the data from the unique* ID */Get_CMPSIT_SerialNum();/* USER CODE BEGIN USBD_FS_SerialStrDescriptor *//* USER CODE END USBD_FS_SerialStrDescriptor */return (uint8_t *) USBD_CMPSIT_StringSerial;
}uint8_t * USBD_CMPSIT_FS_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{USBD_LOG("USBD_CMPSIT_FS_InterfaceStrDescriptor, speed %d, length %d\n", speed, *length);if(speed == 0){USBD_GetString((uint8_t *)USBD_CMPSIT_INTERFACE_STRING_FS, USBD_CMPSIT_StrDesc, length);}else{USBD_GetString((uint8_t *)USBD_CMPSIT_INTERFACE_STRING_FS, USBD_CMPSIT_StrDesc, length);}return USBD_CMPSIT_StrDesc;
}uint8_t * USBD_CMPSIT_FS_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{USBD_LOG("USBD_CMPSIT_FS_ConfigStrDescriptor, speed %d, length %d\n", speed, *length);if(speed == USBD_SPEED_HIGH){USBD_GetString((uint8_t *)USBD_CMPSIT_CONFIGURATION_STRING_FS, USBD_CMPSIT_StrDesc, length);}else{USBD_GetString((uint8_t *)USBD_CMPSIT_CONFIGURATION_STRING_FS, USBD_CMPSIT_StrDesc, length);}return USBD_CMPSIT_StrDesc;
}#if (USBD_LPM_ENABLED == 1)
/*** @brief  Return the BOS descriptor* @param  speed : Current device speed* @param  length : Pointer to data length variable* @retval Pointer to descriptor buffer*/
uint8_t * USBD_CMPSIT_FS_USR_BOSDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{UNUSED(speed);*length = sizeof(USBD_CMPSIT_FS_BOSDesc);return (uint8_t*)USBD_CMPSIT_FS_BOSDesc;
}
#endif /* (USBD_LPM_ENABLED == 1) */__ALIGN_BEGIN static uint8_t USBD_CMPSIT_CfgDesc[USB_CMPSIT_CONFIG_DESC_SIZ] __ALIGN_END =
{/* Configuration Descriptor */0x09,                         /* bLength: Configuration Descriptor size */USB_DESC_TYPE_CONFIGURATION,  /* bDescriptorType: Configuration */LOBYTE(USB_CMPSIT_CONFIG_DESC_SIZ), /* wTotalLength */HIBYTE(USB_CMPSIT_CONFIG_DESC_SIZ),USBD_CMPSIT_NUM_INTERFACES,                         /* bNumInterfaces: CDC(2)+MSC(1)+Audio(2)=5 interfaces */0x01,                         /* bConfigurationValue */0x00,                         /* iConfiguration */
#if (USBD_SELF_POWERED == 1U)0xC0,                         /* bmAttributes: Self Powered */
#else0x80,                         /* bmAttributes: Bus Powered */
#endifUSBD_MAX_POWER,               /* MaxPower */#if USBD_CDC_CMPSIT_ENABLE/* CDC IAD */0x08,0x0B,CDC_COMM_ITF_NBR,         /* bFirstInterface */USBD_CDC_INTERFACES_NUM,         /* bInterfaceCount */0x02,         /* bFunctionClass: Communication Interface Class */0x02,         /* bFunctionSubClass: Abstract Control Model */0x01,         /* bFunctionProtocol: AT commands */0x06,         /* iFunction *//* CDC Communication Interface Descriptor */0x09,USB_DESC_TYPE_INTERFACE,CDC_COMM_ITF_NBR,         /* bInterfaceNumber */0x00,0x01,         /* One endpoint: CDC_CMD_EP */0x02,0x02,0x01,0x06,/* CDC Header Functional Descriptor */0x05,0x24,0x00,0x10, 0x01,/* CDC Call Management Functional Descriptor */0x05,0x24,0x01,0x00,0x01,         /* bDataInterface *//* CDC ACM Functional Descriptor */0x04,0x24,0x02,0x02,/* CDC Union Functional Descriptor */0x05,0x24,0x06,0x00,         /* Master interface */0x01,         /* Slave interface *//* CDC Command Endpoint Descriptor */0x07,USB_DESC_TYPE_ENDPOINT,CDC_CMD_EP,0x03,LOBYTE(CDC_CMD_PACKET_SIZE),HIBYTE(CDC_CMD_PACKET_SIZE),CDC_FS_BINTERVAL,/* CDC Data Interface Descriptor */0x09,USB_DESC_TYPE_INTERFACE,CDC_DATA_ITF_NBR,         /* bInterfaceNumber */0x00,0x02,         /* Two endpoints: IN/OUT */0x0A,0x00,0x00,0x06,/* CDC Data OUT Endpoint Descriptor */0x07,USB_DESC_TYPE_ENDPOINT,CDC_OUT_EP,0x02,LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),0x00,/* CDC Data IN Endpoint Descriptor */0x07,USB_DESC_TYPE_ENDPOINT,CDC_IN_EP,0x02,LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),0x00,
#endif /* USBD_MSC_CMPSIT_ENABLE */#if USBD_MSC_CMPSIT_ENABLE/* MSC IAD */0x08,0x0B,MSC_STD_ITF_NBR,        /* bFirstInterface: MSC interface */USBD_MSC_INTERFACES_NUM,0x08,0x06,0x50,0x07,/* MSC Interface Descriptor */0x09,USB_DESC_TYPE_INTERFACE,MSC_STD_ITF_NBR,        /* MSC interface number */0x00,0x02,        /* Two endpoints */0x08,0x06,0x50,0x07,/* MSC IN Endpoint */0x07,USB_DESC_TYPE_ENDPOINT,MSC_EPIN_ADDR,0x02,LOBYTE(MSC_MAX_FS_PACKET),HIBYTE(MSC_MAX_FS_PACKET),0x00,/* MSC OUT Endpoint */0x07,USB_DESC_TYPE_ENDPOINT,MSC_EPOUT_ADDR,0x02,LOBYTE(MSC_MAX_FS_PACKET),HIBYTE(MSC_MAX_FS_PACKET),0x00,
#endif /* USBD_MSC_CMPSIT_ENABLE */#if USBD_AUDIO_CMPSIT_ENABLE/******** IAD to associate the two SPKR interfaces */0x08,                        /* bLength */0x0B,                        /* bDescriptorType */AUDIO_SPKR_AC_ITF_NBR,      /* bFirstInterface */USBD_AUDIO_INTERFACES_NUM,                        /* bInterfaceCount */USB_DEVICE_CLASS_AUDIO,      /* bFunctionClass */AUDIO_SUBCLASS_AUDIOCONTROL, /* bFunctionSubClass */AUDIO_PROTOCOL_UNDEFINED,    /* bFunctionProtocol */0x00,                        /* iFunction (Index of string descriptor describing this function) *//* USB Speaker Standard interface descriptor */AUDIO_INTERFACE_DESC_SIZE,   /* bLength */USB_DESC_TYPE_INTERFACE,     /* bDescriptorType */AUDIO_SPKR_AC_ITF_NBR,      /* bInterfaceNumber */0x00,                        /* bAlternateSetting */0x00,                        /* bNumEndpoints */USB_DEVICE_CLASS_AUDIO,      /* bInterfaceClass */AUDIO_SUBCLASS_AUDIOCONTROL, /* bInterfaceSubClass */AUDIO_PROTOCOL_UNDEFINED,    /* bInterfaceProtocol */AUDIO_SPKR_STR_DESC_IDX,    /* iInterface *//* 09 byte*//* USB Speaker Class-specific AC Interface Descriptor */AUDIO_INTERFACE_DESC_SIZE,       /* bLength */AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */AUDIO_CONTROL_HEADER,            /* bDescriptorSubtype */0x00, /* 1.00 */                 /* bcdADC */0x01,0x27, /* wTotalLength = 39*/0x00,0x01,                  /* bInCollection */AUDIO_SPKR_AS_ITF_NBR, /* baInterfaceNr *//* 09 byte*//* USB Speaker Input Terminal Descriptor */AUDIO_INPUT_TERMINAL_DESC_SIZE,  /* bLength */AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */AUDIO_CONTROL_INPUT_TERMINAL,    /* bDescriptorSubtype */0x01,                            /* bTerminalID */0x01,                            /* wTerminalType AUDIO_TERMINAL_USB_STREAMING   0x0101 */0x01,0x00, /* bAssocTerminal */0x01, /* bNrChannels */0x00, /* wChannelConfig 0x0000  Mono */0x00,0x00, /* iChannelNames */0x00, /* iTerminal *//* 12 byte*//* USB Speaker Audio Feature Unit Descriptor */0x09,                            /* bLength */AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */AUDIO_CONTROL_FEATURE_UNIT,      /* bDescriptorSubtype */AUDIO_STREAMING_CTRL,            /* bUnitID */0x01,                            /* bSourceID */0x01,                            /* bControlSize */AUDIO_CONTROL_MUTE,              /* bmaControls(0) */0x00,                            /* bmaControls(1) */0x00,                            /* iTerminal *//* 09 byte*//*USB Speaker Output Terminal Descriptor */0x09,                            /* bLength */AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */AUDIO_CONTROL_OUTPUT_TERMINAL,   /* bDescriptorSubtype */0x03,                            /* bTerminalID */0x01,                            /* wTerminalType  0x0301*/0x03,0x00, /* bAssocTerminal */0x02, /* bSourceID */0x00, /* iTerminal *//* 09 byte*//* USB Speaker Standard AS Interface Descriptor - Audio Streaming Zero Bandwidth *//* Interface 1, Alternate Setting 0                                             */AUDIO_INTERFACE_DESC_SIZE,     /* bLength */USB_DESC_TYPE_INTERFACE,       /* bDescriptorType */AUDIO_SPKR_AS_ITF_NBR,        /* bInterfaceNumber */0x00,                          /* bAlternateSetting */0x00,                          /* bNumEndpoints */USB_DEVICE_CLASS_AUDIO,        /* bInterfaceClass */AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */AUDIO_PROTOCOL_UNDEFINED,      /* bInterfaceProtocol */0x00,                          /* iInterface *//* 09 byte*//* USB Speaker Standard AS Interface Descriptor - Audio Streaming Operational *//* Interface 1, Alternate Setting 1                                           */AUDIO_INTERFACE_DESC_SIZE,     /* bLength */USB_DESC_TYPE_INTERFACE,       /* bDescriptorType */AUDIO_SPKR_AS_ITF_NBR,         /* bInterfaceNumber */0x01,                          /* bAlternateSetting */0x01,                          /* bNumEndpoints */USB_DEVICE_CLASS_AUDIO,        /* bInterfaceClass */AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */AUDIO_PROTOCOL_UNDEFINED,      /* bInterfaceProtocol */0x00,                          /* iInterface *//* 09 byte*//* USB Speaker Audio Streaming Interface Descriptor */AUDIO_STREAMING_INTERFACE_DESC_SIZE, /* bLength */AUDIO_INTERFACE_DESCRIPTOR_TYPE,     /* bDescriptorType */AUDIO_STREAMING_GENERAL,             /* bDescriptorSubtype */0x01,                                /* bTerminalLink */0x01,                                /* bDelay */0x01,                                /* wFormatTag AUDIO_FORMAT_PCM  0x0001 */0x00,/* 07 byte*//* USB Speaker Audio Type III Format Interface Descriptor */0x0B,                               /* bLength */AUDIO_INTERFACE_DESCRIPTOR_TYPE,    /* bDescriptorType */AUDIO_STREAMING_FORMAT_TYPE,        /* bDescriptorSubtype */AUDIO_FORMAT_TYPE_I,                /* bFormatType */0x02,                               /* bNrChannels */0x02,                               /* bSubFrameSize :  2 Bytes per frame (16bits) */16,                                 /* bBitResolution (16-bits per sample) */0x01,                               /* bSamFreqType only one frequency supported */AUDIO_SAMPLE_FREQ(USBD_AUDIO_FREQ), /* Audio sampling frequency coded on 3 bytes *//* 11 byte*//* Endpoint 1 - Standard Descriptor */AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */USB_DESC_TYPE_ENDPOINT,            /* bDescriptorType */AUDIO_OUT_EP,                     /* bEndpointAddress 1 out endpoint */USBD_EP_TYPE_ISOC,                 /* bmAttributes */AUDIO_PACKET_SZE(USBD_AUDIO_FREQ), /* wMaxPacketSize in Bytes (Freq(Samples)*2(Stereo)*2(HalfWord)) */AUDIO_FS_BINTERVAL,                /* bInterval */0x00,                              /* bRefresh */0x00,                              /* bSynchAddress *//* 09 byte*//* Endpoint - Audio Streaming Descriptor*/AUDIO_STREAMING_ENDPOINT_DESC_SIZE, /* bLength */AUDIO_ENDPOINT_DESCRIPTOR_TYPE,     /* bDescriptorType */AUDIO_ENDPOINT_GENERAL,             /* bDescriptor */0x00,                               /* bmAttributes */0x00,                               /* bLockDelayUnits */0x00,                               /* wLockDelay */0x00,/* 07 byte*/
#endif /* USBD_AUDIO_CMPSIT_ENABLE */
};__ALIGN_BEGIN static uint8_t USBD_CMPSIT_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] __ALIGN_END =
{USB_LEN_DEV_QUALIFIER_DESC,USB_DESC_TYPE_DEVICE_QUALIFIER,0x00,0x02,0x00,0x00,0x00,0x40,0x01,0x00,
};static uint8_t *USBD_CMPSIT_GetFSCfgDesc(uint16_t *length);
static uint8_t *USBD_CMPSIT_GetHSCfgDesc(uint16_t *length);
static uint8_t *USBD_CMPSIT_GetOtherSpeedCfgDesc(uint16_t *length);
uint8_t *USBD_CMPSIT_GetDeviceQualifierDescriptor(uint16_t *length);/* This structure is used only for the Configuration descriptors and Device Qualifier */
USBD_ClassTypeDef  USBD_CMPSIT =
{NULL, /* Init, */NULL, /* DeInit, */NULL, /* Setup, */NULL, /* EP0_TxSent, */NULL, /* EP0_RxReady, */NULL, /* DataIn, */NULL, /* DataOut, */NULL, /* SOF,  */NULL,NULL,
#ifdef USE_USB_HSUSBD_CMPSIT_GetHSCfgDesc,
#elseNULL,
#endif /* USE_USB_HS */USBD_CMPSIT_GetFSCfgDesc,USBD_CMPSIT_GetOtherSpeedCfgDesc,USBD_CMPSIT_GetDeviceQualifierDescriptor,
#if (USBD_SUPPORT_USER_STRING_DESC == 1U)NULL,
#endif /* USBD_SUPPORT_USER_STRING_DESC */
};#ifdef USE_USBD_COMPOSITE
void USBD_CMPSIT_AddClass(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass, USBD_CompositeClassTypeDef classtype, uint8_t *EpAddr)
{USBD_LOG("USBD_CMPSIT_AddClass, classId %d, type %d\r\n",pdev->classId,classtype);switch(classtype) {case CLASS_TYPE_CDC:{pdev->tclasslist[pdev->classId].ClassType = CLASS_TYPE_CDC;pdev->tclasslist[pdev->classId].Active = 1U;pdev->tclasslist[pdev->classId].NumEps = 3;pdev->tclasslist[pdev->classId].Eps[0].add = CDC_CMD_EP;pdev->tclasslist[pdev->classId].Eps[0].type = USBD_EP_TYPE_INTR;pdev->tclasslist[pdev->classId].Eps[0].size = CDC_CMD_PACKET_SIZE;pdev->tclasslist[pdev->classId].Eps[0].is_used = 1U;pdev->tclasslist[pdev->classId].Eps[1].add = CDC_OUT_EP;pdev->tclasslist[pdev->classId].Eps[1].type = USBD_EP_TYPE_BULK;pdev->tclasslist[pdev->classId].Eps[1].size = CDC_DATA_FS_MAX_PACKET_SIZE;pdev->tclasslist[pdev->classId].Eps[1].is_used = 1U;pdev->tclasslist[pdev->classId].Eps[2].add = CDC_IN_EP;pdev->tclasslist[pdev->classId].Eps[2].type = USBD_EP_TYPE_BULK;pdev->tclasslist[pdev->classId].Eps[2].size = CDC_DATA_FS_MAX_PACKET_SIZE;pdev->tclasslist[pdev->classId].Eps[2].is_used = 1U;pdev->tclasslist[pdev->classId].NumIf = USBD_CDC_INTERFACES_NUM;pdev->tclasslist[pdev->classId].Ifs[0] = CDC_COMM_ITF_NBR;pdev->tclasslist[pdev->classId].Ifs[1] = CDC_DATA_ITF_NBR;	}break;case CLASS_TYPE_MSC:{pdev->tclasslist[pdev->classId].ClassType = CLASS_TYPE_MSC;pdev->tclasslist[pdev->classId].Active = 1U;pdev->tclasslist[pdev->classId].NumEps = 2;pdev->tclasslist[pdev->classId].Eps[0].add = MSC_EPIN_ADDR;pdev->tclasslist[pdev->classId].Eps[0].type = USBD_EP_TYPE_BULK;pdev->tclasslist[pdev->classId].Eps[0].size = MSC_MAX_FS_PACKET;pdev->tclasslist[pdev->classId].Eps[0].is_used = 1U;pdev->tclasslist[pdev->classId].Eps[1].add = MSC_EPOUT_ADDR;pdev->tclasslist[pdev->classId].Eps[1].type = USBD_EP_TYPE_BULK;pdev->tclasslist[pdev->classId].Eps[1].size = MSC_MAX_FS_PACKET;pdev->tclasslist[pdev->classId].Eps[1].is_used = 1U;pdev->tclasslist[pdev->classId].NumIf = USBD_MSC_INTERFACES_NUM;pdev->tclasslist[pdev->classId].Ifs[0] = MSC_STD_ITF_NBR;						}break;case CLASS_TYPE_AUDIO:{pdev->tclasslist[pdev->classId].ClassType = CLASS_TYPE_AUDIO;pdev->tclasslist[pdev->classId].Active = 1U;pdev->tclasslist[pdev->classId].NumEps = 1;pdev->tclasslist[pdev->classId].Eps[0].add = AUDIO_OUT_EP;pdev->tclasslist[pdev->classId].Eps[0].type = USBD_EP_TYPE_ISOC;pdev->tclasslist[pdev->classId].Eps[0].size = AUDIO_OUT_PACKET;//AUDIO_PACKET_SZE_WORD(USBD_AUDIO_FREQ);pdev->tclasslist[pdev->classId].Eps[0].is_used = 1U;pdev->tclasslist[pdev->classId].NumIf = USBD_AUDIO_INTERFACES_NUM;pdev->tclasslist[pdev->classId].Ifs[0] = AUDIO_SPKR_AC_ITF_NBR;pdev->tclasslist[pdev->classId].Ifs[1] = AUDIO_SPKR_AS_ITF_NBR;  			}break;default:break;}pdev->tclasslist[pdev->classId].CurrPcktSze = 0U;}#endifuint8_t * USBD_UsrStrDescriptor(struct _USBD_HandleTypeDef *pdev, uint8_t index,  uint16_t *length)
{USBD_LOG("USBD_UsrStrDescriptor, index %d, length %d\r\n", index, *length);*length = 0;//printf("index=%d\r\n",index);/*  if (USBD_IDX_MICROSOFT_DESC_STR == index) {*length = sizeof (USBD_MS_OS_StringDescriptor);return USBD_MS_OS_StringDescriptor;} else if (USBD_IDX_ODRIVE_INTF_STR == index) {USBD_GetString((uint8_t *)USBD_PRODUCT_XSTR(NATIVE_STRING), USBD_StrDesc, length);return USBD_StrDesc;}  */if (USBD_IDX_CDC_INTF_STR == index) {USBD_GetString((uint8_t *)USBD_CDC_INTERFACE_STRING_FS, USBD_CMPSIT_StrDesc, length);return USBD_CMPSIT_StrDesc;}  else if (USBD_IDX_MSC_INTF_STR == index) {    USBD_GetString((uint8_t *)USBD_MSC_INTERFACE_STRING_FS, USBD_CMPSIT_StrDesc, length);return USBD_CMPSIT_StrDesc;}  else if (USBD_IDX_AUDIO_INTF_STR == index) {    USBD_GetString((uint8_t *)USBD_AUDIO_INTERFACE_STRING_FS, USBD_CMPSIT_StrDesc, length);return USBD_CMPSIT_StrDesc;}  return NULL;
}static uint8_t *USBD_CMPSIT_GetFSCfgDesc(uint16_t *length)
{*length = (uint16_t)sizeof(USBD_CMPSIT_CfgDesc);USBD_LOG("USBD_CMPSIT_GetFSCfgDesc length %d\r\n", *length);return USBD_CMPSIT_CfgDesc;
}
static uint8_t *USBD_CMPSIT_GetHSCfgDesc(uint16_t *length)
{USBD_LOG("USBD_CMPSIT_GetHSCfgDesc length %d\r\n", *length);return NULL;
}
static uint8_t *USBD_CMPSIT_GetOtherSpeedCfgDesc(uint16_t *length)
{*length = (uint16_t)sizeof(USBD_CMPSIT_CfgDesc);USBD_LOG("USBD_CMPSIT_GetOtherSpeedCfgDesc length %d\r\n", *length);return USBD_CMPSIT_CfgDesc;
}
uint8_t *USBD_CMPSIT_GetDeviceQualifierDescriptor(uint16_t *length)
{*length = (uint16_t)sizeof(USBD_CMPSIT_DeviceQualifierDesc);USBD_LOG("USBD_CMPSIT_GetDeviceQualifierDescriptor length %d\r\n", *length);return USBD_CMPSIT_DeviceQualifierDesc;
}#ifdef USE_USBD_COMPOSITE
uint8_t USBD_get_composite_class_id(USBD_HandleTypeDef *pdev, uint8_t classType)
{for (uint8_t i = 0; i < USBD_MAX_SUPPORTED_CLASS; i++){if (pdev->tclasslist[i].ClassType == classType){return i;}}return 0xFF;
}uint8_t USBD_CMPST_ClearConfDesc(USBD_HandleTypeDef *pdev)
{UNUSED(pdev);return (uint8_t)USBD_OK;
}
#endif


usbd_composite.h内容如下:
 

#ifndef USBD_COMPOSITE_H
#define USBD_COMPOSITE_H
#include "usbd_def.h"
#include "usbd_cdc.h"
#include "usbd_msc.h"
#include "usbd_audio.h"
#include "usbd_conf.h"#define  USBD_IDX_CDC_INTF_STR                    0x06
#define  USBD_IDX_MSC_INTF_STR                    0x07
#define  USBD_IDX_AUDIO_INTF_STR                  0x08/* Derived counts and sizes */
#define USBD_CMPSIT_NUM_INTERFACES   (USBD_AUDIO_INTERFACES_NUM + USBD_MSC_INTERFACES_NUM + USBD_CDC_INTERFACES_NUM)#define USB_CMPSIT_CONFIG_DESC_SIZ   (9 + USB_CMPSIT_AUDIO_CONFIG_DESC_SIZ + USB_CMPSIT_CDC_CONFIG_DESC_SIZ + USB_CMPSIT_MSC_CONFIG_DESC_SIZ)#define CDC_COMM_ITF_NBR 0x00U
#define CDC_DATA_ITF_NBR 0x01U
#define MSC_STD_ITF_NBR 0x02U
#define AUDIO_SPKR_AC_ITF_NBR 0x03U
#define AUDIO_SPKR_AS_ITF_NBR 0x04U
#define AUDIO_SPKR_STR_DESC_IDX 0x00U
#define AUDIO_STREAMING_CTRL                          0x02Uextern USBD_ClassTypeDef USBD_CMPSIT;
extern USBD_DescriptorsTypeDef usbCmpsitFS_Desc;
#ifdef USE_USBD_COMPOSITE
void USBD_CMPSIT_AddClass(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass, USBD_CompositeClassTypeDef classtype, uint8_t *EpAddr);
uint8_t USBD_get_composite_class_id(USBD_HandleTypeDef *pdev, uint8_t classType);
#endif
#endif

9. 修改初始化函数

最后一步是在 USB_DEVICE\App\usb_device.c 中,调整 MX_USB_DEVICE_Init() 的实现,确保复合设备的类注册和初始化顺序正确。

void MX_USB_DEVICE_Init(void)
{/* USER CODE BEGIN USB_DEVICE_Init_PreTreatment */if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK){log_debug("USB init error\n");Error_Handler();}#ifdef USE_USBD_COMPOSITE
#if USBD_CDC_CMPSIT_ENABLEif(USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS) != USBD_OK){log_debug("USBD_CDC_RegisterInterface error\n");Error_Handler();}if(USBD_RegisterClassComposite(&hUsbDeviceFS, &USBD_CDC,CLASS_TYPE_CDC,0) != USBD_OK){log_debug("USBD_RegisterClassComposite USBD_CDC error\n");Error_Handler();}
#endif #if USBD_MSC_CMPSIT_ENABLEif (USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_Interface_fops_FS) != USBD_OK){log_debug("USBD_MSC_RegisterStorage error\n");Error_Handler();}if(USBD_RegisterClassComposite(&hUsbDeviceFS, &USBD_MSC,CLASS_TYPE_MSC,0) != USBD_OK){log_debug("USBD_RegisterClassComposite USBD_MSC error\n");Error_Handler();}
#endif#if USBD_AUDIO_CMPSIT_ENABLEif (USBD_AUDIO_RegisterInterface(&hUsbDeviceFS, &USBD_AUDIO_fops_FS) != USBD_OK){log_debug("USBD_MSC_RegisterStorage error\n");Error_Handler();}if(USBD_RegisterClassComposite(&hUsbDeviceFS, &USBD_AUDIO,CLASS_TYPE_AUDIO,0) != USBD_OK){log_debug("USBD_RegisterClassComposite USBD_MSC error\n");Error_Handler();}#endifif (USBD_Start(&hUsbDeviceFS) != USBD_OK){log_debug("USBD_Start error\n");Error_Handler();}return;
#endif/* USER CODE END USB_DEVICE_Init_PreTreatment *//* Init Device Library, add supported class and start the library. *//* USER CODE BEGIN USB_DEVICE_Init_PostTreatment *//* USER CODE END USB_DEVICE_Init_PostTreatment */
}/*** @}*//*** @}*/

完成以上修改后,复合设备的代码就准备好了。编译并下载到开发板后,电脑端即可同时识别出 CDC、MSC 和 Audio 三个 USB 功能。

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

相关文章:

  • x265静态编译win10--
  • STM32学习-Keli仿真
  • LeetCode hot 100 解题思路记录(一)
  • 01-搭建后端django项目
  • 深入探索卷积神经网络:从基础到高级架构(一)
  • 【大数据社科交叉方向会议】第六届大数据与社会科学国际学术会议(ICBDSS 2025)
  • 计算机网络 知识点梳理及讲解(二)物理层:编码调制、传输媒体、信道复用、宽带接入等
  • 学习嵌入式的第三十八天——ARM——概述
  • 初级会计【备考】
  • Windows系统忘记用户名密码怎么办
  • 市场部绩效考核关键指标与市场分析
  • 嵌入式 - ARM8
  • MongoDB备份数据库
  • 【OpenGL】LearnOpenGL学习笔记23 - ShadowMap、PCF
  • MongoDB文档规范
  • 让设计、办公、创作效率翻倍的技术文章大纲
  • 能不能写一个linux下类vim的编辑器
  • Linux02: 编辑器nano的常用技巧
  • UDP和TCP对比通俗讲解
  • 【ReText】1.3 Python multiprocessing 库详解
  • Liunx系统下出现“Could not resolve host: mirrorlist.centos.org; 未知的错误”地解决方案
  • CentOS Stream 9安装系统(LVM扩容案例)
  • Docusign AI 全球化:构建安全、合规的多语言协议管理
  • C# 基于halcon的视觉工作流-章37-零件测量
  • 第二部分:VTK核心类详解(第38章 vtkPointData点数据类)
  • 木卫四科技 × 一汽解放商用车开发院: 共驱商用车 AI 研发新程
  • 【C++闯关笔记】STL:stack与queue的学习和使用
  • [HCTF 2018] WarmUp
  • Vue 学习随笔系列二十六 —— 动态表头
  • BIM 可视化运维平台 + IBMS 中央集成系统一体化解决方案:构建虚实融合的智慧运营中枢