STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口

前言

在上一篇文章实现USB虚拟U盘之后,项目需要用同一个USB口同时实现MSC和CDC功能,既能进行串口通信又能读取片外FLASH虚拟U盘。对于USB通用串行总线如果要真正搞明白这个协议还是比较困难的,需要用不少时间来了解驱动原代码,但是如果仅仅会用USB串行通信或者大容量存储这些功能还是相对容易。

一、硬件

STM32F407ZGTX,板子上包含一片片外FLASH(w25q128)芯片
另外,USB_CDC 工程文件,USB_MSC 工程文件准备好。

二、实现功能

对于USB复合设备,同一个USB串口实现MSC+CDC功能
在实现此功能之前,确保对于这两个功能MSC和CDC均已单独完成并测试可行。USB复合设备实现,即对这两个工程的相关文件进行整合,最关键的是对于描述符的修改。配置描述符,接口描述符,端点描述符等。一个设备仅有一个设备描述符,可以有一个或多个配置描述符

三、Cube MX配置

前面文章详细介绍了USBMSC虚拟U盘实现,不懂的可以翻看上一篇文章。
对于CDC虚拟串口的配置,也比较简单,在Cube MX选择Communication Device Class(Virtual Port Com)其他默认即可,这里不详细介绍。
然后分别对这两个工程进行实验测试均可行,下面开始介绍MSC+CDC双设备实现过程。

四、文件移植

1.打开USB_MSC和USB_CDC这两个工程文件夹
注意:由于CDC工程文件相对较少,我们就把需要的CDC工程的相关文件复制到MSC工程中去。

左边为CDC工程文件,右边为MSC工程文件
STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口_第1张图片
2.拷贝CDC工程所需的C文件和H文件
usb_cdc.if.c
usb_cdc.if.h
注意,找到这几个文件所在路径,若发现文件中已经包含以上C文件和H文件,便不需要拷贝到MSC工程中。

STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口_第2张图片

将CDC整个文件拷贝到MSC工程对应路径下,注意看图中文件所在位置
STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口_第3张图片
3.创建usbd_msccdc.c文件和usbd_msccdc.h文件
STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口_第4张图片
注意文件所在位置,USB_DEVICE 文件夹—>App文件夹中
3.打开Keil MSC工程,添加文件
C文件

STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口_第5张图片
STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口_第6张图片
添加H文件路径
STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口_第7张图片
添加完之后工程文件结构如图
STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口_第8张图片
编译无错误然后进行下一步

五、代码修改

1.usbd_msccdc.c

/**
 * @file        usbd_msccdc.c
 * @author      Pzkkkkkk
 * @version     V0.1
 * @date        2021.12.5
 * @brief       MSC + CDC
 * @note
 * @attention   COYPRIGHT Pzkkkkkk
 */

#include "usbd_msccdc.h"
#include "usbd_def.h"
#include "usbd_msc.h"
#include "usbd_cdc.h"
#include "usbd_storage_if.h"
#include "usbd_cdc_if.h"

/** @defgroup MC_CORE_Private_FunctionPrototypes
  * @{
  */

USBD_CDC_HandleTypeDef *pCDCData;
USBD_MSC_BOT_HandleTypeDef *pMSCData;

uint8_t USBD_MC_Init(USBD_HandleTypeDef *pdev,
                     uint8_t cfgidx);

uint8_t USBD_MC_DeInit(USBD_HandleTypeDef *pdev,
                       uint8_t cfgidx);

uint8_t USBD_MC_Setup(USBD_HandleTypeDef *pdev,
                      USBD_SetupReqTypedef *req);

uint8_t USBD_MC_DataIn(USBD_HandleTypeDef *pdev,
                       uint8_t epnum);

uint8_t USBD_MC_DataOut(USBD_HandleTypeDef *pdev,
                        uint8_t epnum);

uint8_t *USBD_MC_GetHSCfgDesc(uint16_t *length);

uint8_t *USBD_MC_GetFSCfgDesc(uint16_t *length);

uint8_t *USBD_MC_GetOtherSpeedCfgDesc(uint16_t *length);

uint8_t *USBD_MC_GetDeviceQualifierDescriptor(uint16_t *length);

static uint8_t USBD_MC_RxReady(USBD_HandleTypeDef *pdev);
static void MC_Switch_MSC(USBD_HandleTypeDef *pdev);
static void MC_Switch_CDC(USBD_HandleTypeDef *pdev);

/**
  * @}
  */
extern USBD_HandleTypeDef hUsbDeviceFS;

/** @defgroup MC_CORE_Private_Variables
  * @{
  */
USBD_ClassTypeDef USBD_COMPOSITE =
    {
        USBD_MC_Init,
        USBD_MC_DeInit,
        USBD_MC_Setup,
        NULL,            /*EP0_TxSent*/
        USBD_MC_RxReady, /*EP0_RxReady*/
        USBD_MC_DataIn,
        USBD_MC_DataOut,
        NULL, /*SOF */
        NULL,
        NULL,
        USBD_MC_GetHSCfgDesc,
        USBD_MC_GetFSCfgDesc,
        USBD_MC_GetOtherSpeedCfgDesc,
        USBD_MC_GetDeviceQualifierDescriptor,
};

/* USB Mass storage device Configuration Descriptor */
/*   All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
uint8_t USBD_MC_CfgDesc[USB_MC_CONFIG_DESC_SIZ] =
    {
        /*Configuration Descriptor*/
        0x09,                        /* bLength: Configuration Descriptor size */
        USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
        USB_MC_CONFIG_DESC_SIZ,      /* wTotalLength:no of returned bytes */
        0x00,
        0x03, /* bNumInterfaces: 3 interface */
        0x01, /* bConfigurationValue: Configuration value */
        0x00, /* iConfiguration: Index of string descriptor describing the configuration */
        0xC0, /* bmAttributes: self powered */
        0x32, /* MaxPower 100 mA */

        /****************************MSC************************************/
        /* Interface Association Descriptor */
        0x08, // bLength
        0x0B, // bDescriptorType
        0x00, // bFirstInterface
        0x02, // bInterfaceCount
        0x02, // bFunctionClass: CDC Class
        0x02, // bFunctionSubClass
        0x01, // bFunctionProtocol
        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 endpoints 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: 1 */

        /*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 */
        MC_CDC_CMD_EP,               /* bEndpointAddress */
        0x03,                        /* bmAttributes: Interrupt */
        LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
        HIBYTE(CDC_CMD_PACKET_SIZE),
        0x10, /* 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 */
        MC_CDC_OUT_EP,            /* bEndpointAddress */
        0x02,                     /* bmAttributes: Bulk */
        LOBYTE(MC_MAX_FS_PACKET), /* wMaxPacketSize: */
        HIBYTE(MC_MAX_FS_PACKET),
        0x00, /* bInterval: ignore for Bulk transfer */

        /*Endpoint IN Descriptor*/
        0x07,                     /* bLength: Endpoint Descriptor size */
        USB_DESC_TYPE_ENDPOINT,   /* bDescriptorType: Endpoint */
        MC_CDC_IN_EP,             /* bEndpointAddress */
        0x02,                     /* bmAttributes: Bulk */
        LOBYTE(MC_MAX_FS_PACKET), /* wMaxPacketSize: */
        HIBYTE(MC_MAX_FS_PACKET),
        0x00, /* bInterval: ignore for Bulk transfer */

        /****************************MSC**********************************/
        /* Interface Association Descriptor */
        0x08, // bLength
        0x0B, // bDescriptorType
        0x02, // bFirstInterface
        0x01, // bInterfaceCount
        0x08, // bFunctionClass: MASS STORAGE Class
        0x06, // bFunctionSubClass
        0x50, // bFunctionProtocol
        0x01, // 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 */
        0x05, /* iInterface: */
        /********************  Mass Storage Endpoints ********************/
        0x07,             /*Endpoint descriptor length = 7*/
        0x05,             /*Endpoint descriptor type */
        MC_MSC_EPIN_ADDR, /*Endpoint address (IN, address 1) */
        0x02,             /*Bulk endpoint type */
        LOBYTE(MC_MAX_FS_PACKET),
        HIBYTE(MC_MAX_FS_PACKET),
        0x00, /*Polling interval in milliseconds */

        0x07,              /*Endpoint descriptor length = 7 */
        0x05,              /*Endpoint descriptor type */
        MC_MSC_EPOUT_ADDR, /*Endpoint address (OUT, address 1) */
        0x02,              /*Bulk endpoint type */
        LOBYTE(MC_MAX_FS_PACKET),
        HIBYTE(MC_MAX_FS_PACKET),
        0x00 /*Polling interval in milliseconds*/
};

uint8_t USBD_MC_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] =
    {
        USB_LEN_DEV_QUALIFIER_DESC,
        USB_DESC_TYPE_DEVICE_QUALIFIER,
        0x00,
        0x02,
        0x00,
        0x00,
        0x00,
        MC_MAX_FS_PACKET,
        0x01,
        0x00,
};

/**
  * @brief  USBD_MC_Init
  *         Initialize  the mass storage configuration
  * @param  pdev: device instance
  * @param  cfgidx: configuration index
  * @retval status
  */
uint8_t USBD_MC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
  uint8_t ret = 0U;

  USBD_CDC_HandleTypeDef *hcdc;

  MC_Switch_CDC(pdev);

  USBD_LL_OpenEP(pdev,
                 MC_CDC_IN_EP,
                 USBD_EP_TYPE_BULK,
                 MC_MAX_FS_PACKET);

  USBD_LL_OpenEP(pdev,
                 MC_CDC_OUT_EP,
                 USBD_EP_TYPE_BULK,
                 MC_MAX_FS_PACKET);

  USBD_LL_OpenEP(pdev,
                 MC_CDC_CMD_EP,
                 USBD_EP_TYPE_INTR,
                 CDC_CMD_PACKET_SIZE);

  hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData;

  ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init();

  hcdc->TxState = 0;
  hcdc->RxState = 0;

  USBD_LL_PrepareReceive(pdev,
                         MC_CDC_OUT_EP,
                         hcdc->RxBuffer,
                         MC_MAX_FS_PACKET);

  pCDCData = pdev->pClassData;

  MC_Switch_MSC(pdev);

  USBD_LL_OpenEP(pdev,
                 MC_MSC_EPOUT_ADDR,
                 USBD_EP_TYPE_BULK,
                 MC_MAX_FS_PACKET);

  USBD_LL_OpenEP(pdev,
                 MC_MSC_EPIN_ADDR,
                 USBD_EP_TYPE_BULK,
                 MC_MAX_FS_PACKET);

  MSC_BOT_Init(pdev);

  pMSCData = pdev->pClassData;

  if (pdev->pClassData == NULL)
  {
    ret = USBD_FAIL;
  }

  return ret;
}

/**
  * @brief  USBD_MC_DeInit
  *         DeInitilaize  the mass storage configuration
  * @param  pdev: device instance
  * @param  cfgidx: configuration index
  * @retval status
  */
uint8_t USBD_MC_DeInit(USBD_HandleTypeDef *pdev,
                       uint8_t cfgidx)
{
  return USBD_OK;
}
/**
* @brief  USBD_MC_Setup
*         Handle the MC specific requests
* @param  pdev: device instance
* @param  req: USB request
* @retval status
*/
uint8_t USBD_MC_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
  if (req->wIndex == 0x0002)
  {
    MC_Switch_MSC(pdev);
    USBD_MSC_BOT_HandleTypeDef *hmsc = (USBD_MSC_BOT_HandleTypeDef *)pdev->pClassData;

    switch (req->bmRequest & USB_REQ_TYPE_MASK)
    {

    /* Class request */
    case USB_REQ_TYPE_CLASS:
      switch (req->bRequest)
      {
      case BOT_GET_MAX_LUN:

        if ((req->wValue == 0) &&
            (req->wLength == 1) &&
            ((req->bmRequest & 0x80) == 0x80))
        {
          hmsc->max_lun = ((USBD_StorageTypeDef *)pdev->pUserData)->GetMaxLun();
          USBD_CtlSendData(pdev,
                           (uint8_t *)&hmsc->max_lun,
                           1);
        }
        else
        {
          USBD_CtlError(pdev, req);
          return USBD_FAIL;
        }
        break;

      case BOT_RESET:
        if ((req->wValue == 0) &&
            (req->wLength == 0) &&
            ((req->bmRequest & 0x80) != 0x80))
        {
          MSC_BOT_Reset(pdev);
        }
        else
        {
          USBD_CtlError(pdev, req);
          return USBD_FAIL;
        }
        break;

      default:
        USBD_CtlError(pdev, req);
        return USBD_FAIL;
      }
      break;
    /* Interface & Endpoint request */
    case USB_REQ_TYPE_STANDARD:
      switch (req->bRequest)
      {
      case USB_REQ_GET_INTERFACE:
        USBD_CtlSendData(pdev,
                         (uint8_t *)&hmsc->interface,
                         1);
        break;

      case USB_REQ_SET_INTERFACE:
        hmsc->interface = (uint8_t)(req->wValue);
        break;

      case USB_REQ_CLEAR_FEATURE:

        /* Flush the FIFO and Clear the stall status */
        USBD_LL_FlushEP(pdev, (uint8_t)req->wIndex);

        /* Reactivate the EP */
        USBD_LL_CloseEP(pdev, (uint8_t)req->wIndex);
        if ((((uint8_t)req->wIndex) & 0x80) == 0x80)
        {
          if (pdev->dev_speed == USBD_SPEED_HIGH)
          {
            /* Open EP IN */
            USBD_LL_OpenEP(pdev,
                           MC_MSC_EPIN_ADDR,
                           USBD_EP_TYPE_BULK,
                           MSC_MAX_HS_PACKET);
          }
          else
          {
            /* Open EP IN */
            USBD_LL_OpenEP(pdev,
                           MC_MSC_EPIN_ADDR,
                           USBD_EP_TYPE_BULK,
                           MSC_MAX_FS_PACKET);
          }
        }
        else
        {
          if (pdev->dev_speed == USBD_SPEED_HIGH)
          {
            /* Open EP IN */
            USBD_LL_OpenEP(pdev,
                           MC_MSC_EPOUT_ADDR,
                           USBD_EP_TYPE_BULK,
                           MSC_MAX_HS_PACKET);
          }
          else
          {
            /* Open EP IN */
            USBD_LL_OpenEP(pdev,
                           MC_MSC_EPOUT_ADDR,
                           USBD_EP_TYPE_BULK,
                           MSC_MAX_FS_PACKET);
          }
        }

        /* Handle BOT error */
        MSC_BOT_CplClrFeature(pdev, (uint8_t)req->wIndex);
        break;
      }
      break;

    default:
      break;
    }
  }
  else
  {
    MC_Switch_CDC(pdev);
    static uint8_t ifalt = 0;
    USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData;

    switch (req->bmRequest & USB_REQ_TYPE_MASK)
    {
    case USB_REQ_TYPE_CLASS:
      if (req->wLength)
      {
        if (req->bmRequest & 0x80)
        {
          ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(req->bRequest, (uint8_t *)hcdc->data, req->wLength);
          USBD_CtlSendData(pdev,
                           (uint8_t *)hcdc->data,
                           req->wLength);
        }
        else
        {
          hcdc->CmdOpCode = req->bRequest;
          hcdc->CmdLength = req->wLength;

          USBD_CtlPrepareRx(pdev,
                            (uint8_t *)hcdc->data,
                            req->wLength);
        }
      }
      else
      {
        ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(req->bRequest, (uint8_t *)req, 0);
      }
      break;

    case USB_REQ_TYPE_STANDARD:
      switch (req->bRequest)
      {
      case USB_REQ_GET_INTERFACE:
        USBD_CtlSendData(pdev,
                         &ifalt,
                         1);
        break;

      case USB_REQ_SET_INTERFACE:
        break;
      }

    default:
      break;
    }
  }

  return USBD_OK;
}

/**
* @brief  USBD_MC_DataIn
*         handle data IN Stage
* @param  pdev: device instance
* @param  epnum: endpoint index
* @retval status
*/
uint8_t USBD_MC_DataIn(USBD_HandleTypeDef *pdev,
                       uint8_t epnum)
{
  if (epnum == (MC_MSC_EPIN_ADDR & 0x7f))
  {
    MC_Switch_MSC(pdev);
    MSC_BOT_DataIn(pdev, epnum);
  }
  else if (epnum == (MC_CDC_IN_EP & 0x7f))
  {
    USBD_CDC_HandleTypeDef *hcdc;

    MC_Switch_CDC(pdev);
    hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData;
    hcdc->TxState = 0;
  }

  return USBD_OK;
}

/**
* @brief  USBD_MC_DataOut
*         handle data OUT Stage
* @param  pdev: device instance
* @param  epnum: endpoint index
* @retval status
*/
uint8_t USBD_MC_DataOut(USBD_HandleTypeDef *pdev,
                        uint8_t epnum)
{
  if (epnum == MC_MSC_EPOUT_ADDR)
  {
    MC_Switch_MSC(pdev);
    MSC_BOT_DataOut(pdev, epnum);
  }
  else if (epnum == MC_CDC_OUT_EP)
  {
    USBD_CDC_HandleTypeDef *hcdc;

    MC_Switch_CDC(pdev);
    hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData;

    hcdc->RxLength = USBD_LL_GetRxDataSize(pdev, epnum);
    ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Receive(hcdc->RxBuffer, &hcdc->RxLength);
  }

  return USBD_OK;
}

/**
* @brief  USBD_MC_GetHSCfgDesc 
*         return configuration descriptor
* @param  length : pointer data length
* @retval pointer to descriptor buffer
*/
uint8_t *USBD_MC_GetHSCfgDesc(uint16_t *length)
{
  *length = sizeof(USBD_MC_CfgDesc);
  return USBD_MC_CfgDesc;
}

/**
* @brief  USBD_MC_GetFSCfgDesc 
*         return configuration descriptor
* @param  length : pointer data length
* @retval pointer to descriptor buffer
*/
uint8_t *USBD_MC_GetFSCfgDesc(uint16_t *length)
{
  *length = sizeof(USBD_MC_CfgDesc);
  return USBD_MC_CfgDesc;
}

/**
* @brief  USBD_MC_GetOtherSpeedCfgDesc 
*         return other speed configuration descriptor
* @param  length : pointer data length
* @retval pointer to descriptor buffer
*/
uint8_t *USBD_MC_GetOtherSpeedCfgDesc(uint16_t *length)
{
  *length = sizeof(USBD_MC_CfgDesc);
  return USBD_MC_CfgDesc;
}
/**
* @brief  DeviceQualifierDescriptor 
*         return Device Qualifier descriptor
* @param  length : pointer data length
* @retval pointer to descriptor buffer
*/
uint8_t *USBD_MC_GetDeviceQualifierDescriptor(uint16_t *length)
{
  *length = sizeof(USBD_MC_DeviceQualifierDesc);
  return USBD_MC_DeviceQualifierDesc;
}

/**
* @brief  USBD_MC_RegisterStorage
* @param  fops: storage callback
* @retval status
*/

static uint8_t USBD_MC_RxReady(USBD_HandleTypeDef *pdev)
{
  USBD_CDC_HandleTypeDef *hcdc;

  MC_Switch_CDC(pdev);
  hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData;

  if ((pdev->pUserData != NULL) && (hcdc->CmdOpCode != 0xFF))
  {
    ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(hcdc->CmdOpCode, (uint8_t *)hcdc->data, hcdc->CmdLength);
    hcdc->CmdOpCode = 0xFF;
  }

  return USBD_OK;
}

static void MC_Switch_MSC(USBD_HandleTypeDef *pdev)
{
  static USBD_MSC_BOT_HandleTypeDef msc_handle;

  USBD_MSC_RegisterStorage(pdev, &USBD_Storage_Interface_fops_FS);
  pdev->pClassData = &msc_handle;
}

static void MC_Switch_CDC(USBD_HandleTypeDef *pdev)
{
  static USBD_CDC_HandleTypeDef cdc_handle;

  USBD_CDC_RegisterInterface(pdev, &USBD_CDC_Interface_fops_FS);
  pdev->pClassData = &cdc_handle;
}

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT PZKKKKKK *****END OF FILE****/

注意:我在usbd_msccdc.c文件中将原来自动生成的结构体变量名USBD_Interface_fops_FS更改为USBD_CDC_Interface_fops_FS ,不修改问题应该也不大,记得在usbd_cdc_if.c和usbd_cdc_if.h定义中修改。主要为了将两个变量区分开,防止出现编译报错。
2.usbd_msccdc.h

/**
 * @file        usbd_msccdc.h
 * @author      Pzkkkkkk
 * @version     V0.1
 * @date        2021.12.5
 * @brief       MSC + CDC 
 * @note
 * @attention   COYPRIGHT PZkkkkkk
 */
 
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USBD_MSCCDC_H
#define __USBD_MSCCDC_H
 
#ifdef __cplusplus
 extern "C" {
#endif
 
/* Includes ------------------------------------------------------------------*/
#include  "usbd_ioreq.h"
#include 	"usbd_cdc.h"
#include 	"usbd_msc.h"

#define MC_MAX_FS_PACKET            0x40

#define USB_MC_CONFIG_DESC_SIZ      106 

#define MC_MSC_EPIN_ADDR                MSC_EPIN_ADDR 
#define MC_MSC_EPOUT_ADDR               MSC_EPOUT_ADDR 

#define MC_CDC_IN_EP                   CDC_IN_EP 
#define MC_CDC_OUT_EP                  CDC_OUT_EP  
#define MC_CDC_CMD_EP                  CDC_CMD_EP 

extern USBD_ClassTypeDef  USBD_COMPOSITE;
 
/**
  * @}
  */
 
/**
  * @}
  */
 
#ifdef __cplusplus
}
#endif
 
#endif  /* __USBD_MSCCDC_H */
/**
  * @}
  */
 
/************************ (C) COPYRIGHT PZKKKKKK *****END OF FILE****/

3.usbd_cdc.h
重新分配端点号,否则会冲突

/** @defgroup usbd_cdc_Exported_Defines
  * @{
  */
#define CDC_IN_EP                                   0x83U  /* EP1 for data IN */
#define CDC_OUT_EP                                  0x02U  /* EP1 for data OUT */
#define CDC_CMD_EP                                  0x82U  /* EP2 for CDC commands */

4.usbd_msc.h

#define MSC_EPIN_ADDR                0x81U
#define MSC_EPOUT_ADDR               0x01U

5.usbd_conf.h
把USBD_MAX_NUM_INTERFACES的值1修改为3

/*---------- -----------*/
#define USBD_MAX_NUM_INTERFACES     3U  //1U
/*---------- -----------*/

6.usbd_conf.c
函数USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)中进行修改

/*******************************************************************************
                       LL Driver Interface (USB Device Library --> PCD)
*******************************************************************************/

/**
  * @brief  Initializes the low level portion of the device driver.
  * @param  pdev: Device handle
  * @retval USBD status
  */
USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
{
  /* Init USB Ip. */
  if (pdev->id == DEVICE_FS) {
  /* Link the driver to the stack. */
  hpcd_USB_OTG_FS.pData = pdev;
  pdev->pData = &hpcd_USB_OTG_FS;

  hpcd_USB_OTG_FS.Instance = USB_OTG_FS;
  hpcd_USB_OTG_FS.Init.dev_endpoints = 4;
  hpcd_USB_OTG_FS.Init.speed = PCD_SPEED_FULL;
  hpcd_USB_OTG_FS.Init.dma_enable = DISABLE;
  hpcd_USB_OTG_FS.Init.phy_itface = PCD_PHY_EMBEDDED;
  hpcd_USB_OTG_FS.Init.Sof_enable = DISABLE;
  hpcd_USB_OTG_FS.Init.low_power_enable = DISABLE;
  hpcd_USB_OTG_FS.Init.lpm_enable = DISABLE;
  hpcd_USB_OTG_FS.Init.vbus_sensing_enable = DISABLE;
  hpcd_USB_OTG_FS.Init.use_dedicated_ep1 = DISABLE;
  if (HAL_PCD_Init(&hpcd_USB_OTG_FS) != HAL_OK)
  {
    Error_Handler( );
  }

#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, 0x80);
  //添加下面两行程序
  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x40);
  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 2, 0x40);
  }
  return USBD_OK;
}

7.usb_device.c

/**
  * Init USB device Library, add supported class and start the library
  * @retval None
  */
void MX_USB_DEVICE_Init(void)
{
  /* USER CODE BEGIN USB_DEVICE_Init_PreTreatment */

  /* USER CODE END USB_DEVICE_Init_PreTreatment */

  /* Init Device Library, add supported class and start the library. */
  if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK)
  {
    Error_Handler();
  }
  if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_COMPOSITE) != USBD_OK)
  {
    Error_Handler();
  }
// if (USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_Interface_fops_FS) != 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.至此,USB双设备MSC+CDC代码修改完毕,最后添加USB串口收发测试代码进行功能测试。

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
	
	CDC_Transmit_FS(UserRxBufferFS, *Len);      //将接受到的数据返回
	
  return (USBD_OK);
  /* USER CODE END 6 */
}

六、功能测试

编译无错误,使用USB数据线连接STM32F4开发板USB_SLAVE端口与PC端,下载程序到板子。

1.MSC虚拟U盘功能

打开Debug运行程序,立即弹出U盘(若第一次运行可能弹出U盘较慢因为需要建立文件系统格式化U盘)。
STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口_第9张图片
同时对U盘中文件读写功能正常
STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口_第10张图片
2.CDC虚拟串口

打开串口调试助手,选择串口号(使用USB串口通信不需要设置波特率等其他选项),打开串口,实时返回发送的数据。
STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口_第11张图片
3.打开PC设备管理器
STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口_第12张图片
电脑端同时显示USB串行设备与磁盘驱动STM Product USB Device
USBMSC+CDC复合设备测试完毕

你可能感兴趣的:(STM32Cube,MX,stm32,单片机,arm)