手把手教你使用USB的CDC+MSC复合设备(基于stm32f407)

news/2025/1/10 20:29:38/

学习

  • 1 实验环境与说明
  • 2 USB CDC
    • 2.1 CDC代码生成
    • 2.2 通信设备(CDC)描述符
      • 2.2.1 设备描述符
      • 2.2.2 配置描述符
    • 2.3 调试
  • 3 USB MSC
    • 3.1 MSC代码生成
    • 3.2 大容量存储设备(MSC)描述符
      • 3.2.1 设备描述符
      • 2.2.2 配置描述符
    • 3.3 调试
  • 4 USB复合设备(CDC+MSC)
    • 4.1 复合设备描述符
    • 4.2 代码修改
    • 4.3 调试
  • 5 后记

  最近对usb有点兴趣,感觉挺好玩的,于是买了本圈圈大神的经典著作-<<圈圈教你玩USB>>,里面使用51单片机+usb芯片对usb的基本知识潺潺道来,做了十个左右的常用案例实验,很有趣,建议大家看看。
  趁热打铁,拿身边的开发板来练练手,探索一下复合设备的好玩方便的地方。

1 实验环境与说明

 实验环境: 

  • 硬件:     STM32F407
  • 编译器:    Keil MDK 5.17
  • 软件生成工具: STM32CubeMX 6.8.1
  • 调试工具:   Jlink

  说明: 本实验所使用的代码基本都是使用CubeMX生成,只有小部分做了些修改和调整来适配当前的实验,关于USB的部分,如描述符、枚举、协议等不会做详细说明,感兴趣的话可以看OO的著作,或者可以跟笔者讨论,后期如果有时间的话,可以将usb的各个章节总结出来。 

  下面会有三个实验循序渐进,实验1是USB CDC部分,实验2是USB MSC部分,实验3是将前两个实验结合起来。STM32CubeMX版本的不同,生成的代码可能也不同,请注意不一样的地方,根据实际进行调整。
  usb官网:https://usb.org/

2 USB CDC

  使用usb的通信设备类,即
在这里插入图片描述
此设备类会在设备描述符和接口描述符中体现。
  下面使用STM32CubeMX来生成此实验所需要的代码。

2.1 CDC代码生成

  打开CubeMX,选择控制器芯片STM32F407。
  1、时钟选择
在这里插入图片描述
  2、usb设备选择
在这里插入图片描述
  3、usb通信设备类选择
在这里插入图片描述
  4、时钟配置
在这里插入图片描述
  OK,上述CubeMX配置如此,接下来生成Keil MDK的工程代码即可。

2.2 通信设备(CDC)描述符

2.2.1 设备描述符

  标准设备描述符如下:

/** USB standard device descriptor. */
__ALIGN_BEGIN uint8_t USBD_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,0x02,                       /*bDeviceClass*/0x02,                       /*bDeviceSubClass*/0x00,                       /*bDeviceProtocol*/USB_MAX_EP0_SIZE,           /*bMaxPacketSize*/LOBYTE(USBD_VID),           /*idVendor*/HIBYTE(USBD_VID),           /*idVendor*/LOBYTE(USBD_PID_FS),        /*idProduct*/HIBYTE(USBD_PID_FS),        /*idProduct*/0x00,                       /*bcdDevice rel. 2.00*/0x02,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*/
};

  注意bDeviceClass字段必须使用通信设备类0x2。

2.2.2 配置描述符

  配置描述符组成如下图:
在这里插入图片描述
  对应的配置描述符代码段如下:

/* USB CDC device Configuration Descriptor */
__ALIGN_BEGIN static uint8_t USBD_CDC_CfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =
{/* Configuration Descriptor */0x09,                                       /* bLength: Configuration Descriptor size */USB_DESC_TYPE_CONFIGURATION,                /* bDescriptorType: Configuration */USB_CDC_CONFIG_DESC_SIZ,                    /* wTotalLength */0x00,0x02,                                       /* bNumInterfaces: 2 interfaces */0x01,                                       /* bConfigurationValue: Configuration value */0x00,                                       /* iConfiguration: Index of string descriptordescribing the configuration */
#if (USBD_SELF_POWERED == 1U)0xC0,                                       /* bmAttributes: Bus Powered according to user configuration */
#else0x80,                                       /* bmAttributes: Bus Powered according to user configuration */
#endif /* USBD_SELF_POWERED */USBD_MAX_POWER,                             /* MaxPower (mA) *//*---------------------------------------------------------------------------*//* Interface Descriptor */0x09,                                       /* bLength: Interface Descriptor size */USB_DESC_TYPE_INTERFACE,                    /* bDescriptorType: Interface *//* Interface descriptor type */0x00,                                       /* bInterfaceNumber: Number of Interface */0x00,                                       /* bAlternateSetting: Alternate setting */0x01,                                       /* bNumEndpoints: One endpoint used */0x02,                                       /* bInterfaceClass: Communication Interface Class */0x02,                                       /* bInterfaceSubClass: Abstract Control Model */0x01,                                       /* bInterfaceProtocol: Common AT commands */0x00,                                       /* iInterface *//* Header Functional Descriptor */0x05,                                       /* bLength: Endpoint Descriptor size */0x24,                                       /* bDescriptorType: CS_INTERFACE */0x00,                                       /* bDescriptorSubtype: Header Func Desc */0x10,                                       /* bcdCDC: spec release number */0x01,/* Call Management Functional Descriptor */0x05,                                       /* bFunctionLength */0x24,                                       /* bDescriptorType: CS_INTERFACE */0x01,                                       /* bDescriptorSubtype: Call Management Func Desc */0x00,                                       /* bmCapabilities: D0+D1 */0x01,                                       /* bDataInterface *//* ACM Functional Descriptor */0x04,                                       /* bFunctionLength */0x24,                                       /* bDescriptorType: CS_INTERFACE */0x02,                                       /* bDescriptorSubtype: Abstract Control Management desc */0x02,                                       /* bmCapabilities *//* Union Functional Descriptor */0x05,                                       /* bFunctionLength */0x24,                                       /* bDescriptorType: CS_INTERFACE */0x06,                                       /* bDescriptorSubtype: Union func desc */0x00,                                       /* bMasterInterface: Communication class interface */0x01,                                       /* bSlaveInterface0: Data Class Interface *//* Endpoint 2 Descriptor */0x07,                                       /* bLength: Endpoint Descriptor size */USB_DESC_TYPE_ENDPOINT,                     /* bDescriptorType: Endpoint */CDC_CMD_EP,                                 /* bEndpointAddress */0x03,                                       /* bmAttributes: Interrupt */LOBYTE(CDC_CMD_PACKET_SIZE),                /* wMaxPacketSize */HIBYTE(CDC_CMD_PACKET_SIZE),CDC_FS_BINTERVAL,                           /* bInterval *//*---------------------------------------------------------------------------*//* Data class interface descriptor */0x09,                                       /* bLength: Endpoint Descriptor size */USB_DESC_TYPE_INTERFACE,                    /* bDescriptorType: */0x01,                                       /* bInterfaceNumber: Number of Interface */0x00,                                       /* bAlternateSetting: Alternate setting */0x02,                                       /* bNumEndpoints: Two endpoints used */0x0A,                                       /* bInterfaceClass: CDC */0x00,                                       /* bInterfaceSubClass */0x00,                                       /* bInterfaceProtocol */0x00,                                       /* iInterface *//* Endpoint OUT Descriptor */0x07,                                       /* bLength: Endpoint Descriptor size */USB_DESC_TYPE_ENDPOINT,                     /* bDescriptorType: Endpoint */CDC_OUT_EP,                                 /* bEndpointAddress */0x02,                                       /* bmAttributes: Bulk */LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),        /* wMaxPacketSize */HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),0x00,                                       /* bInterval *//* Endpoint IN Descriptor */0x07,                                       /* bLength: Endpoint Descriptor size */USB_DESC_TYPE_ENDPOINT,                     /* bDescriptorType: Endpoint */CDC_IN_EP,                                  /* bEndpointAddress */0x02,                                       /* bmAttributes: Bulk */LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),        /* wMaxPacketSize */HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),0x00                                        /* bInterval */
};

2.3 调试

  上述代码生成之后,修改一下代码,在main.c的主函数中修改如下:

int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USB_DEVICE_Init();/* USER CODE BEGIN 2 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */uint32_t cur_time, pre_time = 0;uint32_t count = 0;char buf[256];while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */cur_time = HAL_GetTick();if ((cur_time - pre_time) >=1000 ) {pre_time = cur_time;int length = snprintf(buf, sizeof(buf)-1, "你是个第%d个沙雕\r\n", count++);CDC_Transmit_FS((uint8_t*)buf, length);}}/* USER CODE END 3 */
}

  编译,通过Jlink下载到控制器芯片中,将usb口连到pc上,查看对应的设备管理器,发现了增加了一个端口,如下:
在这里插入图片描述
  网上找个免费的串口调试助手,打开对应的端口COM9,则根据上述修改的main.c文件中的内容,控制器会通过usb发送指定的内容出来显示到pc上的串口调试助手上,如下:
在这里插入图片描述
  以上为USB CDC的实验,完成。

3 USB MSC

  使用usb的Mass Storage类,即
在这里插入图片描述
此设备类会在接口描述符中体现。
  下面使用STM32CubeMX来生成此实验所需要的代码。

3.1 MSC代码生成

  打开CubeMX,选择控制器芯片STM32F407。
  1、时钟选择
在这里插入图片描述
  2、usb设备选择
在这里插入图片描述
  3、usb大容量存储类选择
在这里插入图片描述
  4、SDIO接口选择
  选择sdio接口时需注意自己使用的sd卡是支持多少数据线的,本人的是1 bit的,大部分可能是4bit的,请注意一下。在这里插入图片描述
  5、fatfs文件系统选择
在这里插入图片描述
  OK,上述CubeMX配置如此,接下来生成Keil MDK的工程代码即可。

3.2 大容量存储设备(MSC)描述符

3.2.1 设备描述符

  标准设备描述符如下:

/** USB standard device descriptor. */
__ALIGN_BEGIN uint8_t USBD_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,0x00,                       /*bDeviceClass*/0x00,                       /*bDeviceSubClass*/0x00,                       /*bDeviceProtocol*/USB_MAX_EP0_SIZE,           /*bMaxPacketSize*/LOBYTE(USBD_VID),           /*idVendor*/HIBYTE(USBD_VID),           /*idVendor*/LOBYTE(USBD_PID_FS),        /*idProduct*/HIBYTE(USBD_PID_FS),        /*idProduct*/0x00,                       /*bcdDevice rel. 2.00*/0x02,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*/
};

2.2.2 配置描述符

  配置描述符组成如下图:
在这里插入图片描述

  对应的配置描述符代码段如下:

/* USB Mass storage device Configuration Descriptor */
/* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
__ALIGN_BEGIN static uint8_t USBD_MSC_CfgDesc[USB_MSC_CONFIG_DESC_SIZ]  __ALIGN_END =
{0x09,                                            /* bLength: Configuration Descriptor size */USB_DESC_TYPE_CONFIGURATION,                     /* bDescriptorType: Configuration */USB_MSC_CONFIG_DESC_SIZ,0x00,0x01,                                            /* bNumInterfaces: 1 interface */0x01,                                            /* bConfigurationValue */0x04,                                            /* iConfiguration */
#if (USBD_SELF_POWERED == 1U)0xC0,                                            /* bmAttributes: Bus Powered according to user configuration */
#else0x80,                                            /* bmAttributes: Bus Powered according to user configuration */
#endif /* USBD_SELF_POWERED */USBD_MAX_POWER,                                  /* MaxPower (mA) *//********************  Mass Storage interface ********************/0x09,                                            /* bLength: Interface Descriptor size */0x04,                                            /* bDescriptorType: */0x00,                                            /* bInterfaceNumber: Number of Interface */0x00,                                            /* bAlternateSetting: Alternate setting */0x02,                                            /* bNumEndpoints */0x08,                                            /* bInterfaceClass: MSC Class */0x06,                                            /* bInterfaceSubClass : SCSI transparent*/0x50,                                            /* nInterfaceProtocol */0x05,                                            /* iInterface: *//********************  Mass Storage Endpoints ********************/0x07,                                            /* Endpoint descriptor length = 7 */0x05,                                            /* Endpoint descriptor type */MSC_EPIN_ADDR,                                   /* Endpoint address (IN, address 1) */0x02,                                            /* Bulk endpoint type */LOBYTE(MSC_MAX_FS_PACKET),HIBYTE(MSC_MAX_FS_PACKET),0x00,                                            /* Polling interval in milliseconds */0x07,                                            /* Endpoint descriptor length = 7 */0x05,                                            /* Endpoint descriptor type */MSC_EPOUT_ADDR,                                  /* Endpoint address (OUT, address 1) */0x02,                                            /* Bulk endpoint type */LOBYTE(MSC_MAX_FS_PACKET),HIBYTE(MSC_MAX_FS_PACKET),0x00                                             /* Polling interval in milliseconds */
};

3.3 调试

  上述代码生成之后,修改一下代码,在usb与sdio通信的接口文件usbd_storage_if.c中对以后几个函数进行补充,很关键:
在这里插入图片描述
具体补充的内容如下:

int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{/* USER CODE BEGIN 3 */UNUSED(lun);*block_num  = hsd.SdCard.BlockNbr;*block_size = hsd.SdCard.BlockSize;return (USBD_OK);/* USER CODE END 3 */
}
int8_t STORAGE_IsReady_FS(uint8_t lun)
{/* USER CODE BEGIN 4 */uint8_t state = 0;state = HAL_SD_GetState(&hsd) ;if(HAL_SD_STATE_READY != state){return USBD_FAIL ;}return (USBD_OK);/* USER CODE END 4 */
}
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{/* USER CODE BEGIN 6 */if(HAL_OK != HAL_SD_ReadBlocks(&hsd,(uint8_t *)buf, blk_addr , blk_len, 1000)){return USBD_FAIL ;}return (USBD_OK);/* USER CODE END 6 */
}
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{/* USER CODE BEGIN 7 */if(HAL_OK != HAL_SD_WriteBlocks(&hsd, (uint8_t *)buf, blk_addr , blk_len, 1000)){return USBD_FAIL ;}return (USBD_OK);/* USER CODE END 7 */
}

在main.c中增加如下内容,会对底层的sd卡和文件系统进行初始化:

  /* Initialize all configured peripherals */MX_GPIO_Init();MX_DMA_Init();MX_SDIO_SD_Init();MX_USB_DEVICE_Init();MX_FATFS_Init();/* USER CODE BEGIN 2 */retSD=f_mount(&SDFatFS, (TCHAR const*)SDPath, 0);retSD = f_open(&SDFile,(const char*)"1.txt",FA_CREATE_ALWAYS|FA_WRITE);retSD = f_close(&SDFile);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}

  编译,通过Jlink下载到控制器芯片中,将usb口连到pc上,查看pc的资源管理器,发现了增加了一个磁盘并且具有我们新增的文件,如下:
在这里插入图片描述
  以上为USB MSC的实验,完成。

4 USB复合设备(CDC+MSC)

  理论上好像应该不能叫复合设备,应该称为组合设备,只是习惯这个叫法了。
  以最近的这个MSC实验为基础,结合第一个CDC实验来修改代码变成我们的复合设备。

4.1 复合设备描述符

在这里插入图片描述

4.2 代码修改

1、将CDC实验使用的usb cdc类代码拷贝过来,新增复合设备类(composite)代码,这两个文件是从官网的F407例程中抠出来的,可官网搜索下载。
在这里插入图片描述
2、修改复合设备类(composite)代码
  usbd_composite_builder.c文件中增加配置描述符,此配置描述是按照上述给出的图来进行内容填充的。

/* The generic configuration descriptor buffer that will be filled by builderSize of the buffer is the maximum possible configuration descriptor size. */
__ALIGN_BEGIN static uint8_t USBD_CMPSIT_FSCfgDesc[USBD_CMPST_MAX_CONFDESC_SZ]  __ALIGN_END = {/* Configuration Descriptor */0x09,                                       /* bLength: Configuration Descriptor size */USB_DESC_TYPE_CONFIGURATION,                /* bDescriptorType: Configuration */USBD_CMPST_MAX_CONFDESC_SZ,                    /* wTotalLength */0x00,0x03,                                       /* bNumInterfaces: 2 interfaces */0x01,                                       /* bConfigurationValue: Configuration value */0x00,                                       /* iConfiguration: Index of string descriptordescribing the configuration */
#if (USBD_SELF_POWERED == 1U)0xC0,                                       /* bmAttributes: Bus Powered according to user configuration */
#else0x80,                                       /* bmAttributes: Bus Powered according to user configuration */
#endif /* USBD_SELF_POWERED */USBD_MAX_POWER,                             /* MaxPower (mA) *//*---------------------------------------------------------------------------*//* Interface Association Descriptor: CDC device (virtual com port) */0x08,   /* bLength: IAD size */0x0B,   /* bDescriptorType: Interface Association Descriptor */0x00,   /* bFirstInterface */0x02,   /* bInterfaceCount */0x02,   /* bFunctionClass: Communication Interface Class */0x02,   /* bFunctionSubClass: Abstract Control Model */0x01,   /* bFunctionProtocol: Common AT commands */0x00,   /* iFunction *//* Interface Descriptor */0x09,                                       /* bLength: Interface Descriptor size */USB_DESC_TYPE_INTERFACE,                    /* bDescriptorType: Interface *//* Interface descriptor type */0x00,                                       /* bInterfaceNumber: Number of Interface */0x00,                                       /* bAlternateSetting: Alternate setting */0x01,                                       /* bNumEndpoints: One endpoint used */0x02,                                       /* bInterfaceClass: Communication Interface Class */0x02,                                       /* bInterfaceSubClass: Abstract Control Model */0x01,                                       /* bInterfaceProtocol: Common AT commands */0x00,                                       /* iInterface *//* Header Functional Descriptor */0x05,                                       /* bLength: Endpoint Descriptor size */0x24,                                       /* bDescriptorType: CS_INTERFACE */0x00,                                       /* bDescriptorSubtype: Header Func Desc */0x10,                                       /* bcdCDC: spec release number */0x01,/* Call Management Functional Descriptor */0x05,                                       /* bFunctionLength */0x24,                                       /* bDescriptorType: CS_INTERFACE */0x01,                                       /* bDescriptorSubtype: Call Management Func Desc */0x00,                                       /* bmCapabilities: D0+D1 */0x01,                                       /* bDataInterface *//* ACM Functional Descriptor */0x04,                                       /* bFunctionLength */0x24,                                       /* bDescriptorType: CS_INTERFACE */0x02,                                       /* bDescriptorSubtype: Abstract Control Management desc */0x02,                                       /* bmCapabilities *//* Union Functional Descriptor */0x05,                                       /* bFunctionLength */0x24,                                       /* bDescriptorType: CS_INTERFACE */0x06,                                       /* bDescriptorSubtype: Union func desc */0x00,                                       /* bMasterInterface: Communication class interface */0x01,                                       /* bSlaveInterface0: Data Class Interface *//* Endpoint 2 Descriptor */0x07,                                       /* bLength: Endpoint Descriptor size */USB_DESC_TYPE_ENDPOINT,                     /* bDescriptorType: Endpoint */CDC_CMD_EP,                                 /* bEndpointAddress */0x03,                                       /* bmAttributes: Interrupt */LOBYTE(CDC_CMD_PACKET_SIZE),                /* wMaxPacketSize */HIBYTE(CDC_CMD_PACKET_SIZE),CDC_FS_BINTERVAL,                           /* bInterval *//*---------------------------------------------------------------------------*//* Data class interface descriptor */0x09,                                       /* bLength: Endpoint Descriptor size */USB_DESC_TYPE_INTERFACE,                    /* bDescriptorType: */0x01,                                       /* bInterfaceNumber: Number of Interface */0x00,                                       /* bAlternateSetting: Alternate setting */0x02,                                       /* bNumEndpoints: Two endpoints used */0x0A,                                       /* bInterfaceClass: CDC */0x00,                                       /* bInterfaceSubClass */0x00,                                       /* bInterfaceProtocol */0x06,                                       /* iInterface *//* Endpoint OUT Descriptor */0x07,                                       /* bLength: Endpoint Descriptor size */USB_DESC_TYPE_ENDPOINT,                     /* bDescriptorType: Endpoint */CDC_OUT_EP,                                 /* bEndpointAddress */0x02,                                       /* bmAttributes: Bulk */LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),        /* wMaxPacketSize */HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),0x00,                                       /* bInterval *//* Endpoint IN Descriptor */0x07,                                       /* bLength: Endpoint Descriptor size */USB_DESC_TYPE_ENDPOINT,                     /* bDescriptorType: Endpoint */CDC_IN_EP,                                  /* bEndpointAddress */0x02,                                       /* bmAttributes: Bulk */LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),        /* wMaxPacketSize */HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),0x00,                                        /* bInterval *//* Interface Association Descriptor: Mass Storage device */0x08,   /* bLength: IAD size */0x0B,   /* bDescriptorType: Interface Association Descriptor */0x02,   /* bFirstInterface */0x01,   /* bInterfaceCount */0x08,   /* bFunctionClass: */0x06,   /* bFunctionSubClass: */0x50,   /* bFunctionProtocol: */0x00,   /* iFunction *//********************  Mass Storage interface ********************/0x09,                                            /* bLength: Interface Descriptor size */0x04,                                            /* bDescriptorType: */0x02,                                            /* bInterfaceNumber: Number of Interface */0x00,                                            /* bAlternateSetting: Alternate setting */0x02,                                            /* bNumEndpoints */0x08,                                            /* bInterfaceClass: MSC Class */0x06,                                            /* bInterfaceSubClass : SCSI transparent*/0x50,                                            /* nInterfaceProtocol */0x00,                                            /* iInterface: *//********************  Mass Storage Endpoints ********************/0x07,                                            /* Endpoint descriptor length = 7 */0x05,                                            /* Endpoint descriptor type */MSC_EPIN_ADDR,                                   /* Endpoint address (IN, address 1) */0x02,                                            /* Bulk endpoint type */LOBYTE(MSC_MAX_FS_PACKET),HIBYTE(MSC_MAX_FS_PACKET),0x00,                                            /* Polling interval in milliseconds */0x07,                                            /* Endpoint descriptor length = 7 */0x05,                                            /* Endpoint descriptor type */MSC_EPOUT_ADDR,                                  /* Endpoint address (OUT, address 1) */0x02,                                            /* Bulk endpoint type */LOBYTE(MSC_MAX_FS_PACKET),HIBYTE(MSC_MAX_FS_PACKET),0x00                                             /* Polling interval in milliseconds */
};static uint8_t *pCmpstFSConfDesc = USBD_CMPSIT_FSCfgDesc;
/* Variable that dynamically holds the current size of the configuration descriptor */
static __IO uint32_t CurrFSConfDescSz = USBD_CMPST_MAX_CONFDESC_SZ;

  usbd_composite_builder.h修改配置描述符数组长度。

/* This is the maximum supported configuration descriptor sizeUser may define this value in usbd_conf.h in order to optimize footprint */
#ifndef USBD_CMPST_MAX_CONFDESC_SZ
#define USBD_CMPST_MAX_CONFDESC_SZ                         106U
#endif /* USBD_CMPST_MAX_CONFDESC_SZ */

3、修改各端点地址
  CDC类,usbd_cdc.h中修改三个端点的地址。

#ifndef CDC_IN_EP
#define CDC_IN_EP                                   0x81U  /* EP1 for data IN */
#endif /* CDC_IN_EP */
#ifndef CDC_OUT_EP
#define CDC_OUT_EP                                  0x01U  /* EP1 for data OUT */
#endif /* CDC_OUT_EP */
#ifndef CDC_CMD_EP
#define CDC_CMD_EP                                  0x82U  /* EP2 for CDC commands */
#endif /* CDC_CMD_EP  */

  MSC类,usbd_msc.h中修改两个端点的地址。

#ifndef MSC_EPIN_ADDR
#define MSC_EPIN_ADDR                0x83U
#endif /* MSC_EPIN_ADDR */#ifndef MSC_EPOUT_ADDR
#define MSC_EPOUT_ADDR               0x03U
#endif /* MSC_EPOUT_ADDR */

3、启动复合设备编译
  main.h中增加复合设备编译宏USE_USBD_COMPOSITE

/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);/* USER CODE BEGIN EFP *//* USER CODE END EFP *//* Private defines -----------------------------------------------------------*/
#define USE_USBD_COMPOSITE 1

4、修改复合设备类的类添加函数
  usbd_composite_builder.c中修改设备类的添加函数,内部涉及接口、端点、类型等。

/*** @brief  USBD_CMPSIT_AddClass*         Register a class in the class builder* @param  pdev: device instance* @param  pclass: pointer to the class structure to be added* @param  class: type of the class to be added (from USBD_CompositeClassTypeDef)* @param  cfgidx: configuration index* @retval status*/
uint8_t  USBD_CMPSIT_AddClass(USBD_HandleTypeDef *pdev,USBD_ClassTypeDef *pclass,USBD_CompositeClassTypeDef class,uint8_t cfgidx)
{
#if 1switch(class){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 = 2;pdev->tclasslist[pdev->classId].Ifs[0] = 0;pdev->tclasslist[pdev->classId].Ifs[1] = 1;	}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 = 1;pdev->tclasslist[pdev->classId].Ifs[0] = 2;						}break;default:break;}pdev->tclasslist[pdev->classId].CurrPcktSze = 0U;UNUSED(cfgidx);return (uint8_t)USBD_OK;#elseif ((pdev->classId < USBD_MAX_SUPPORTED_CLASS) && (pdev->tclasslist[pdev->classId].Active == 0U)){/* Store the class parameters in the global tab */pdev->pClass[pdev->classId] = pclass;pdev->tclasslist[pdev->classId].ClassId = pdev->classId;pdev->tclasslist[pdev->classId].Active = 1U;pdev->tclasslist[pdev->classId].ClassType = class;/* Call configuration descriptor builder and endpoint configuration builder */if (USBD_CMPSIT_AddToConfDesc(pdev) != (uint8_t)USBD_OK){return (uint8_t)USBD_FAIL;}}UNUSED(cfgidx);return (uint8_t)USBD_OK;
#endif
}

5、修改usb端点发送和接收的fifo设置
  关于三个批量端点的发送fifo相关的设置可参考手册,此处必须修改。
在这里插入图片描述
  usbd_conf.c文件中修改端点发送fifo配置。

#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U)/* Register USB PCD CallBacks */HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SOF_CB_ID, PCD_SOFCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SETUPSTAGE_CB_ID, PCD_SetupStageCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_RESET_CB_ID, PCD_ResetCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SUSPEND_CB_ID, PCD_SuspendCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_RESUME_CB_ID, PCD_ResumeCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_CONNECT_CB_ID, PCD_ConnectCallback);HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_DISCONNECT_CB_ID, PCD_DisconnectCallback);HAL_PCD_RegisterDataOutStageCallback(&hpcd_USB_OTG_FS, PCD_DataOutStageCallback);HAL_PCD_RegisterDataInStageCallback(&hpcd_USB_OTG_FS, PCD_DataInStageCallback);HAL_PCD_RegisterIsoOutIncpltCallback(&hpcd_USB_OTG_FS, PCD_ISOOUTIncompleteCallback);HAL_PCD_RegisterIsoInIncpltCallback(&hpcd_USB_OTG_FS, PCD_ISOINIncompleteCallback);
#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_FS, 0x80);HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 0x40);HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x40);HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 3, 0x40);}return USBD_OK;
}

6、添加cdc接口文件
  将cdc实验的接口文件添加过来。
在这里插入图片描述
7、注册复合设备类
  将cdc和msc的类注册到复合设备类中,注意注册的接口和类顺序,否则会出问题,usb_device.c中增加如下。

void MX_USB_DEVICE_Init(void)
{/* USER CODE BEGIN USB_DEVICE_Init_PreTreatment *//* USER CODE END USB_DEVICE_Init_PreTreatment usbCmpsitFS_Desc*//* Init Device Library, add supported class and start the library. */if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK){Error_Handler();}if(USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS) != USBD_OK){Error_Handler();}if(USBD_RegisterClassComposite(&hUsbDeviceFS, &USBD_CDC,CLASS_TYPE_CDC,0) != USBD_OK){Error_Handler();}if (USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_Interface_fops_FS) != USBD_OK){Error_Handler();}if(USBD_RegisterClassComposite(&hUsbDeviceFS, &USBD_MSC,CLASS_TYPE_MSC,0) != USBD_OK){Error_Handler();}if (USBD_Start(&hUsbDeviceFS) != USBD_OK){Error_Handler();}/* USER CODE BEGIN USB_DEVICE_Init_PostTreatment *//* USER CODE END USB_DEVICE_Init_PostTreatment */
}

8、修改cdc发送函数
  因代码生成的cdc发送函数是单个设备的,若使用复合设备发送,则需要修改,usbd_cdc_if.c增加如下函数:

uint8_t CDC_Transmit(uint8_t* Buf, uint16_t Len)
{uint8_t result = USBD_OK;USBD_HandleTypeDef *pdev = &hUsbDeviceFS;/* Get the class index relative to this endpoint */uint8_t idx = USBD_CoreFindEP(pdev, CDC_IN_EP);if (((uint8_t)idx != 0xFFU) && (idx < USBD_MAX_SUPPORTED_CLASS)){pdev->classId = idx;USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassDataCmsit[pdev->classId];if (hcdc->TxState != 0){return USBD_BUSY;}USBD_CDC_SetTxBuffer(pdev, Buf, Len);result = USBD_CDC_TransmitPacket(pdev);return result;}return USBD_FAIL;
}

9,修改设备描述符
  因现在是复合设备,则需要修改usbd_desc.c中的设备描述符:
在这里插入图片描述

/** USB standard device descriptor. */
__ALIGN_BEGIN uint8_t USBD_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,0xEF,                       /*bDeviceClass*/0x02,                       /*bDeviceSubClass*/0x01,                       /*bDeviceProtocol*/USB_MAX_EP0_SIZE,           /*bMaxPacketSize*/LOBYTE(USBD_VID),           /*idVendor*/HIBYTE(USBD_VID),           /*idVendor*/LOBYTE(USBD_PID_FS),        /*idProduct*/HIBYTE(USBD_PID_FS),        /*idProduct*/0x00,                       /*bcdDevice rel. 2.00*/0x02,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*/
};

10,修改usb配置文件
  因现在支持3个接口,则需要修改usbd_conf.h中的支持最大接口数,同时修改此文件中的malloc和free函数:

/*---------- -----------*/
#define USBD_MAX_NUM_INTERFACES     3U/* Memory management macros make sure to use static memory allocation */
/** Alias for memory allocation. */#define USBD_malloc         malloc//(void *)USBD_static_malloc/** Alias for memory release. */
#define USBD_free           free//USBD_static_free

11、修改内存堆大小
  因10中使用了动态内存管理,而usb比较耗内存,则需要修改内存的堆大小,如下:
在这里插入图片描述

4.3 调试

  编译代码,下载,可以同时出现了cdc设备,msc设备:
在这里插入图片描述

5 后记

  都已经这么详细了,你那里出现这样的设备了吗?如果还没出现,欢迎随时来打我。


                            2023.08.09-01:12于shenzhen



http://www.ppmy.cn/news/1028202.html

相关文章

深入浅出PHP封装根据商品ID获取淘宝商品详情数据方法

要通过淘宝的API获取商品详情&#xff0c;您可以使用淘宝开放平台提供的接口来实现。以下是一种使用PHP编程语言实现的示例&#xff0c;展示如何通过淘宝开放平台API获取商品详情&#xff1a; 首先&#xff0c;确保您已注册成为淘宝开放平台的开发者&#xff0c;并创建一个应用…

Hyper-V 扩展磁盘后,如何扩容到 root

问题 从前空间不够了&#xff0c;都是在虚拟机再加一块硬盘&#xff0c;挂载上来 VirtualBox 虚拟 CentOS 7 磁盘扩容 这次虚拟机是 Hyper-V &#xff0c;他可以直接扩展容量&#xff0c;但是&#xff0c;扩展出来并没有直接就加给 root 或者 home&#xff0c; centos-root 占…

FreeRTOS(计数信号量)

资料来源于硬件家园&#xff1a;资料汇总 - FreeRTOS实时操作系统课程(多任务管理) 目录 一、计数信号量的定义与应用 1、计数信号量的定义 2、计数信号量的应用 二、计数信号量的运作机制 1、任务间计数信号量的实现 三、计数信号量常用的API函数 1、计数信号量典型流程…

分布式应用:Zabbix监控Tomcat

目录 一、理论 1.Zabbix监控Tomcat 二、实验 1.Zabbix监控Tomcat 三、问题 1.获取软件包失败 2.tomcat 配置 JMX remote monitor不生效 3.Zabbix客户端日志报错 一、理论 1.Zabbix监控Tomcat &#xff08;1&#xff09;环境 zabbix服务端&#xff1a;192.168.204.214 …

1015 Reversible Primes

个人学习记录&#xff0c;代码难免不尽人意。 reversible prime in any number system is a prime whose “reverse” in that number system is also a prime. For example in the decimal system 73 is a reversible prime because its reverse 37 is also a prime. Now giv…

什么是http协议

HTTP&#xff08;Hypertext Transfer Protocol&#xff09;即超文本传输协议&#xff0c;是一种用于在网络上传输超文本数据的协议。它是客户端&#xff08;如浏览器&#xff09;和服务器之间进行通信的基础&#xff0c;用于请求和响应在Web上的资源。 HTTP 协议的工作原理如下…

SSL证书DV和OV的区别?

SSL证书是在互联网通信中保护数据传输安全的一种加密工具。它能够确保客户端和服务器之间的通信得以加密&#xff0c;防止第三方窃听或篡改信息。在选择SSL证书时&#xff0c;常见的有DV证书和OV证书&#xff0c;它们在验证标准和信任级别上有所不同。那么SSL证书DV和OV的有哪些…

MySQL~事务的四大特性和隔离级别

事务的四大特性 1.原子性&#xff1a;一个事务&#xff08;transaction&#xff09;中的所有操作&#xff0c;要么全部完成&#xff0c;要么全部不完成。事务在执行过程中发生错误&#xff0c;会被回滚&#xff08;Rollback&#xff09;到事务开始前的状态&#xff0c;就像这个…