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

2023-11-11

前言

在上一篇文章实现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工程文件
请添加图片描述
2.拷贝CDC工程所需的C文件和H文件
usb_cdc.if.c
usb_cdc.if.h
注意,找到这几个文件所在路径,若发现文件中已经包含以上C文件和H文件,便不需要拷贝到MSC工程中。

请添加图片描述

将CDC整个文件拷贝到MSC工程对应路径下,注意看图中文件所在位置
请添加图片描述
3.创建usbd_msccdc.c文件和usbd_msccdc.h文件
请添加图片描述
注意文件所在位置,USB_DEVICE 文件夹—>App文件夹中
3.打开Keil MSC工程,添加文件
C文件

请添加图片描述
请添加图片描述
添加H文件路径
请添加图片描述
添加完之后工程文件结构如图
请添加图片描述
编译无错误然后进行下一步

五、代码修改

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盘)。
请添加图片描述
同时对U盘中文件读写功能正常
请添加图片描述
2.CDC虚拟串口

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口 的相关文章

  • Android 上原生的自修改代码

    我正在尝试在 Android 上制作一些自修改本机代码并在模拟器中运行它 我的示例基于 android ndk 中的 Hello JNI 示例 它看起来像这样 define NOPE LENGTH 4 typedef void FUNC v
  • 理解这部分手臂的汇编代码

    syntax unified thumb cpu cortex m4 arch armv7e m fpu fpv4 sp d16 Changes from unprivileged to privileged mode thumb func
  • 使用 GCC 编译器为代码的特定部分保留寄存器

    是否可以为 C 代码的特定部分保留寄存器 ffixed reg 选项或声明全局寄存器变量不是我正在寻找的答案 我想保留特定范围 比如说特定函数 的寄存器值 使用局部寄存器变量是不可能的 因为它不能保证在整个范围内保留寄存器的值 我正在寻找类
  • 使用 ARM NEON 内在函数添加 alpha 和排列

    我正在开发一个 iOS 应用程序 需要相当快地将图像从 RGB gt BGRA 转换 如果可能的话 我想使用 NEON 内在函数 有没有比简单分配组件更快的方法 void neonPermuteRGBtoBGRA unsigned char
  • 小型 ARM 微控制器的 RTOS 内核之间的可量化差异 [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 有许多不同的 RTOS 可用于微控制器 我专门寻找支持 ARM Cortex M 处理器的 RTOS 另外 我对闭源解决方案不感兴趣 试图从网站
  • 用于 RHEL 的 gdb-multiarch

    我正在尝试寻找方法来运行gdb 多架构RHEL 中的命令 我已经安装了用于 ARM 处理的 QEMU 模拟器 我想安装GDB进行调试 我能够安装GDB 多体系结构在 Ubuntu 中运行命令成功 sudo apt get GDB multi
  • 在嵌入式设备上使用new或malloc引起的段错误[关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 我正在尝试
  • ARM 中只有两个操作数的 ADD 或 SUB

    我正在学习ARM汇编语言 我读过 ADD 应该有 3 个操作数 然而 我见过很多案例 现实中只有两种 例如 STR R1 SP 0x20 var 1C LDR R1 a lua 0x1DE4E6 MOVS R0 R4 haystack AD
  • Linux malloc() 在 ARM 和 x86 上的行为是否不同?

    这个网站上有很多关于内存分配的问题 但是我 找不到专门解决我的问题的人 这 问题 https stackoverflow com questions 19148296 linux memory overcommit details似乎最接近
  • M1 MacBook Pro 上的 Android Studio 无法使用 ABI armeabi-v7a 模拟系统映像

    我的 M1 Macbook Pro 上的 Android Studio 可以很好地模拟 ABI arm64 v8a 的所有系统映像 API 24 29 30 31 但是 它无法使用 ABI armeabi v7a 运行所有映像 例如 API
  • arm-thumb指令集的blx指令如何支持4MB范围

    读自https www keil com support man docs armasm armasm dom1361289866046 htm https www keil com support man docs armasm arma
  • 如何使用 Neon SIMD 将无符号字符转换为有符号整数

    如何转换变量的数据类型uint8 t to int32 t使用霓虹灯 我找不到执行此操作的任何内在因素 假设您想要将 16 x 8 位整数的向量转换为 4 个 4 x 32 位整数的向量 您可以通过首先解压缩为 16 位 然后再次解压缩为
  • 产生并处理软件中断

    有人可以告诉我如何在Linux下生成软件中断然后用request irq处理它吗 或者也许这是不可能的 您可以使用软中断来代替 您可以通过编辑 include linux interrupt h 来定义您的 sofirq 然后使用函数 ra
  • 使用 Android NDK 使用 -fsigned-char 进行构建安全吗?

    为了与其他平台保持一致 我需要使用signed char在我正在处理的一些本机代码中 但默认情况下在Android NDK上char类型是unsigned 我尝试明确使用signed char类型 但它生成太多警告differ in sig
  • 在linux x86平台上学习ARM所需的工具[关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我有一个 x86 linux 机器 在阅读一些关于 ARM 的各种信息时 我很好奇 现在我想花一些时间学
  • 在 Intel 机器上构建 Apple Silicon 二进制文件

    如何在 macOS 11 Intel 上编译 C 项目以在 Silicon 上运行 我当前的构建脚本很简单 configure make sudo make install 我尝试过使用 host and target标志与aarch64
  • gdb 不会从外部架构读取核心文件

    我正在尝试在 Linux 桌面上读取 ARM 核心文件 但似乎无法找出我的核心文件 有什么方法可以指示 gdb 我的核心文件是什么类型吗 file daemon daemon ELF 32 bit LSB executable ARM ve
  • arm-linux-gnueabi 编译器选项

    我在用 ARM Linux gnueabi gcc在 Linux 中为 ARM 处理器编译 C 程序 但是 我不确定它编译的默认 ARM 模式是什么 例如 对于 C 代码 test c unsigned int main return 0x
  • 什么是遗留中断?

    我正在开发一个项目 试图弄清楚 ARM 架构的全局中断控制器中如何处理中断 我正在使用 pl390 中断控制器 我看到有一条线被称为传统中断 它绕过了分配器逻辑 假设有 2 个中断可以被编程为传统中断 任何人都可以帮助解释一下什么是遗留中断
  • 架构armv7的重复符号

    尝试在我现有的应用程序中使用 Layar SDK 时出现以下错误 我该如何解决这个问题 Ld Users pnawale Library Developer Xcode DerivedData hub afxxzaqisdfliwbzxbi

随机推荐

  • 简单的用Python采集招聘数据内容,并做可视化分析!

    哈喽大家好 现在刚毕业 很多小伙伴因为找不到工作或者找了很多也不喜欢 再有懒一点的 太热了根本不想出门到处找 所以今天给大家分享使用Python批量采集招聘数据 进行可视化分析 轻松找到心仪工作 话不多说 我们直接开始 准备工作 软件工具
  • linux 卸载 rtx,在Ubuntu 18.04系统中安装RTX 2080Ti显卡驱动的方法

    在Ubuntu 18 04操作系统中使用GeForce RTX 2080Ti显卡 但是系统内置的驱动与PPA安装都不行 需要安装NVidia官方的驱动才能运行起来 下面是实现的方法 1 先禁用Ubuntu 18 04系统默认显卡驱动 打开系
  • ECM麦克风电路元器件计算

    ECM麦克风电路元器件设计 一 计算Micbias 偏置电阻R R 偏置电压 micbias电压 静态电流 1 偏置电阻计算案例 供电2V ECM麦克风电流500uA 偏置电阻取值 设置合适的偏置电阻 麦克风实现最大输出 需麦克风两端电压是
  • Multisim14基本介绍(上)

    Multisim14是一种专门用于电路仿真和设计的软件之一 是NI公司下属的ElectroNIcs Workbench Group推出的以Windows为基础的仿真工具 是目前最为流行的EDA软件之一 该软件基于PC平台 采用图形操作界面虚
  • ValueError: Input contains NaN, infinity or a value too large for dtype('float64').

    笔者在使用LogisticRegression模型进行预测时 报错 Traceback most recent call last File D 软件 学习 Python MachineLearing taitannike train py
  • Linux Docker容器 镜像的详解与创建

    一 什么是docker 镜像 就是把业务代码 可运行环境进行整体的打包 二 如何创建docker镜像 现在docker官方共有仓库里面有大量的镜像 所以最基础的镜像 我们可以在公有仓库直接拉取 因为这些镜像都是原厂维护 可以得到即使的更新和
  • 响应式原理

    vue响应式原理 vue2 vue2中使用了ES5里面的Object defineProperty方法 给对应data中的数据的每个值添加了set和get方法 当值被修改时 就会触发对应的set方法 set方法里会通知独影的watcher
  • Python循环结构——for

    for循环是循环结构中的另外一种 基本使用方法 for 变量名 in 目标对象 用于循环的子代码 在for循环中 必定存在一个变量作为游标 且变量会在每次循环开始时自动发生变化 如果没有特别设定变化的值 则默认为 1 当目标对象为列表 字典
  • redis学习:redisKey的基本命令

    Redis是一个开源 BSD许可 内存存储的数据结构服务器 可用作数据库 高速缓存和消息队列代理 它支持字符串 哈希表 列表 集合 有序集合 位图 hyperloglogs等数据类型 内置复制 Lua脚本 LRU收回 事务以及不同级别磁盘持
  • 激光雕刻机:废旧光驱的涅槃之路

    激光雕刻机 废旧光驱的涅槃之路 从 FeedzShare 1天最热 有超过 100 人喜欢此条目 来自 www guokr com FeedzShare 发布时间 2011年04月20日 已有 5 人推荐 DIYer zieak 制作时间
  • ROS 安装详细教程 —— Ubuntu20.04 LTS 安装

    ROS 安装详细教程 Ubuntu20 04 LTS 安装 ROS 简介 官方文档对 ROS 的介绍如下 The Robot Operating System ROS is a set of software libraries and t
  • 网站优化搜索引擎与关键词

    网站优化搜索引擎与关键词 人们不应该高估搜索引擎的智商 这不利于seo的研究 事实上 搜索引擎是非常愚蠢的 让我们举一个非常简单的例子 你在搜索引擎中输入 教师 这个词 搜索引擎就会给出一个准确的搜索列表 我们不会给出 教师 一词的检索信息
  • 使用Docker部署前后端分离项目

    目录 引言 部署需要用到的镜像汇总 1 Redis部署 1 搜索Redis镜像 2 拉取Redis镜像 3 创建Redis容器 2 MySQL部署 1 拉取MySQL镜像 2 查看镜像 3 启动MySQL容器 4 使用本地Navicat测试
  • 报错(内存溢出):Exception in thread "Thread-8" java.lang.OutOfMemoryError: PermGen space

    Exception in thread Thread 8 java lang OutOfMemoryError PermGen space 解决办法 能正常使用 但是偶尔会报下面这个错误 从偶尔这个说法来看 是你热部署次数太多了 导致JVM
  • http协议访问网址的流程

    http协议 http协议可以说是由三个部分组成的 超文本 URL Http 超文本 网页中的信息 如文字 图片 视频 URL 统一资源定位符 由三个部分组成 协议 主机端口 文件名及路径 使用http协议的访问流程 例如我们想访问百度 则
  • C# => Lambda表达式理解

    本文参考网上的博客和找到的资料加上个人理解编写的 主要的代码借鉴 http www cnblogs com knowledgesea p 3163725 html 百度百科 希望能够帮助理解lambda表达式 定义 Lambda表达式 是一
  • 阿里测开的性能测试技术笔记:如何快速上手压测工作

    新年第一个工作日 继续整理之前的技术笔记 前面通过三篇的内容 将自动化测试相关的技术笔记做了整理汇总 这篇内容 主要是我刚开始做性能测试时的一些记录 对新手或者刚进入一个新项目的同学 应该有所帮助 一般我们在刚介入一个项目时 我认为可以从如
  • 基于视觉重定位的室内AR导航APP的大创项目思路(3)手机相机内参数据获取和相机标定

    文章目录 相机内参 为什么要获取相机的内参数据 获取相机内存数据的方法 棋盘格标定 自动相机标定 前情提要 是第一次做项目的小白 文章内的资料介绍如有错误 请多包含 相机内参 相机内参是本身的物理数据 包括焦距f和缩放c 一般以矩阵K的形式
  • Lattice Diamond 3.12下载与安装(免费获取license.dat)

    Lattice Diamond 3 12下载 安装与激活 免费获取license dat Lattice Diamond是LATTICE半导体公司推出的一款免费的FPGA开发软件 其实这个软件具体的下载与安装过程在其配套文档里有比较详细的说
  • STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口

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