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

这篇具有很好参考价值的文章主要介绍了手把手教你使用USB的CDC+MSC复合设备(基于stm32f407)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


  最近对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的通信设备类,即
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
此设备类会在设备描述符和接口描述符中体现。
  下面使用STM32CubeMX来生成此实验所需要的代码。

2.1 CDC代码生成

  打开CubeMX,选择控制器芯片STM32F407。
  1、时钟选择
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
  2、usb设备选择
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
  3、usb通信设备类选择
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
  4、时钟配置
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
  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.01
                                             in order to support LPM L1 suspend
                                             resume test of USBCV3.0*/
#else
  0x00,                       /*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+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
  对应的配置描述符代码段如下:

/* 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 descriptor
                                                 describing the configuration */
#if (USBD_SELF_POWERED == 1U)
  0xC0,                                       /* bmAttributes: Bus Powered according to user configuration */
#else
  0x80,                                       /* 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上,查看对应的设备管理器,发现了增加了一个端口,如下:
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
  网上找个免费的串口调试助手,打开对应的端口COM9,则根据上述修改的main.c文件中的内容,控制器会通过usb发送指定的内容出来显示到pc上的串口调试助手上,如下:
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
  以上为USB CDC的实验,完成。

3 USB MSC

  使用usb的Mass Storage类,即
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
此设备类会在接口描述符中体现。
  下面使用STM32CubeMX来生成此实验所需要的代码。

3.1 MSC代码生成

  打开CubeMX,选择控制器芯片STM32F407。
  1、时钟选择
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
  2、usb设备选择
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
  3、usb大容量存储类选择
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
  4、SDIO接口选择
  选择sdio接口时需注意自己使用的sd卡是支持多少数据线的,本人的是1 bit的,大部分可能是4bit的,请注意一下。手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
  5、fatfs文件系统选择
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
  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.01
                                             in order to support LPM L1 suspend
                                             resume test of USBCV3.0*/
#else
  0x00,                       /*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的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备

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

/* 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 */
#else
  0x80,                                            /* 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中对以后几个函数进行补充,很关键:
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
具体补充的内容如下:

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的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
  以上为USB MSC的实验,完成。

4 USB复合设备(CDC+MSC)

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

4.1 复合设备描述符

手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备

4.2 代码修改

1、将CDC实验使用的usb cdc类代码拷贝过来,新增复合设备类(composite)代码,这两个文件是从官网的F407例程中抠出来的,可官网搜索下载。
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
2、修改复合设备类(composite)代码
  usbd_composite_builder.c文件中增加配置描述符,此配置描述是按照上述给出的图来进行内容填充的。

/* The generic configuration descriptor buffer that will be filled by builder
   Size 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 descriptor
                                                 describing the configuration */
#if (USBD_SELF_POWERED == 1U)
  0xC0,                                       /* bmAttributes: Bus Powered according to user configuration */
#else
  0x80,                                       /* 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 size
   User 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 1

	switch(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;

#else
  if ((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相关的设置可参考手册,此处必须修改。
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
  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实验的接口文件添加过来。
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备
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的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备

/** 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.01
                                             in order to support LPM L1 suspend
                                             resume test of USBCV3.0*/
#else
  0x00,                       /*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比较耗内存,则需要修改内存的堆大小,如下:
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备

4.3 调试

  编译代码,下载,可以同时出现了cdc设备,msc设备:
手把手教你使用USB的CDC+MSC复合设备(基于stm32f407),嵌入式学习,stm32,stm32,usb复合设备

5 后记

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


                            2023.08.09-01:12于shenzhen文章来源地址https://www.toymoban.com/news/detail-645024.html


到了这里,关于手把手教你使用USB的CDC+MSC复合设备(基于stm32f407)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 手把手教你 iconfont 导入使用及相关配置

    iconfont是阿里旗下的一套图标库,UI设计师设计号图标后,会将图标上传到iconfont的项目库中。前端开发人员需要下载项目图标,并在项目中使用。 iconfont相对于传统的直接导入图标进入页面,有以下几点优势: 体积更小,页面加载速度更快 解决图片像素点会随页面变化而模

    2024年02月07日
    浏览(62)
  • 手把手教你使用gdb调试器

    所谓调试,指的是对编好的程序用各种手段进进行查错和排非错的过程。进行这种查错处理时,下面将讲解如何使用gdb进行程序的调试。  gdb 简介 gdb是一个功能强大的调试工具,可以用来调试C程序或C++程序。在使用这个工具进行程序调试时,主要涉及下面四个方面的操作。

    2024年02月16日
    浏览(49)
  • 单元测试利器——手把手教你使用Mockito

    作者:京东零售 秦浩然 从你成为开发人员的那一天起,写单元测试终究是你逃不开的宿命!那开发人员为什么不喜欢写单元测试呢?究其原因,无外乎是依赖。依赖其他的服务、依赖运行的环境、等等,各种依赖都成为了我们写单元测试的绊脚石。那现在有个单元测试利器

    2024年02月08日
    浏览(66)
  • 怎么用AI绘画?手把手教你使用

    与传统的绘画方式不同,AI绘画软件采用了人工智能算法和计算机视觉技术,使艺术作品的创作变得更加智能化和自动化。这样,即使一个看不懂颜料,也毫无绘画经验的业余者也能创作出可圈可点的艺术品了。AI绘画软件因此被越来越多的创作者和爱好者所使用。那你们知道

    2024年02月15日
    浏览(70)
  • 手把手教你使用Markdown:从入门到精通

    本篇文章由卷不动的小白撰写,为读者提供了一份详尽的Markdown语法指南。

    2024年02月03日
    浏览(71)
  • 手把手教你使用Segformer训练自己的数据

    使用Transformer进行语义分割的简单高效设计。 将 Transformer 与轻量级多层感知 (MLP) 解码器相结合,表现SOTA!性能优于SETR、Auto-Deeplab和OCRNet等网络 相比于ViT,Swin Transfomer计算复杂度大幅度降低,具有输入图像大小线性计算复杂度。Swin Transformer随着深度加深,逐渐合并图像块来

    2024年01月20日
    浏览(75)
  • 手把手教你如何使用Fiddler抓包工具

    什么是 Fiddler? Fiddler 是一个 HTTP 协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的 HTTP 通讯。Fiddler 提供了电脑端、移动端的抓包、包括 http 协议和 https 协议都可以捕获到报文并进行分析;可以设置断点调试、截取报文进行请求替换和数据篡改,也可以进行

    2024年02月07日
    浏览(65)
  • 【Linux】-vim的介绍,教你手把手使用vim

    💖作者:小树苗渴望变成参天大树 ❤️‍🩹作者宣言:认真写好每一篇博客 💨作者gitee:gitee 💞作者专栏:C语言,数据结构初阶,Linux,C++ 如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧! 今天我们来具体介绍一下vim这个工具的使用,这个工具可以更好帮助我们编

    2024年02月08日
    浏览(60)
  • 手把手教你成为荣耀开发者:数据报表使用指引

    荣耀开发者服务平台是荣耀面向开发者的统一生态入口,通过聚合周边内外部系统,分全球多站点部署,为全球开发者提供业务全生命周期的商业支撑服务,拥有应用分发、智慧服务、开放能力、HONOR Connect等众多业务等您来合作。 “数据报表”为荣耀开发者提供产品详细的

    2024年02月09日
    浏览(51)
  • 手把手教你使用anaconda安装pytorch环境(适合新手)

    如果你右键电脑有(nvidia控制面板)则不要以下操作 没有的话需要进行以下操作 (右键此电脑,找到管理然后打开) 找到设备管理器 找到显示适配器(这里会有你的显卡型号) 根据以上的信息以后我们就可以对应我们的显卡去英伟达官网上去找相对应的显卡驱动更新或者

    2024年01月17日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包