前言

在上一篇文章实现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, // bLength0x0B, // bDescriptorType0x00, // bFirstInterface0x02, // bInterfaceCount0x02, // bFunctionClass: CDC Class0x02, // bFunctionSubClass0x01, // bFunctionProtocol0x00, // 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, // bLength0x0B, // bDescriptorType0x02, // bFirstInterface0x01, // bInterfaceCount0x08, // bFunctionClass: MASS STORAGE Class0x06, // bFunctionSubClass0x50, // bFunctionProtocol0x01, // 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 __cplusplusextern "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_EPextern 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复合设备测试完毕

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

  1. STM32Cube MX USB虚拟U盘+FATFS+W25Q128

    第一次写CSDN,把这两天做的一个小实验记个笔记.写的不好请见谅,有错误欢迎指正,欢迎讨论.在做之前也参考其他博主的一些文章Carry_王的博客 USB基本概念不做介绍,不懂的可以先去了解,主要说明实 ...

  2. linux USB大容量设备驱动入门之读取U盘容量

    主要参考了以下资料: usbmassbulk_10.pdf , usbmass-ufi10.pdf, SCSI Commands Reference Manual,spc-3.pdf 以下驱动模块通过 ...

  3. 把Hyper-V Server 2008R2装进USB存储设备

    把Hyper-V Server 2008R2装进USB存储设备 之前在发布了windows8的时候,有一个比较吸引人的feature就是windows to go,借助该feature可以把Windo ...

  4. 4.5.1、CUBEMX USB之MSC+CDC

    前言 本意是做一个CDC+MSC的复合设备,就是类似于ST-LINK2的那种设备, 在平时使用串口功能通讯,偶尔可以通过拖拉文件进行IAP升级,但是在网上 找了好多资料和教程后,却发现基本都是同一个教 ...

  5. STM32CubeMX | 基于STM32使用HAL库实现USB组合设备之多路CDC

    STM32CubeMX | 基于STM32使用HAL库实现USB组合设备之多路CDC 本博客完整代码下载地址:https://download.csdn.net/download/qq15347150 ...

  6. 【STM32利用CuBe MX生成HID设备】1-熟悉软件以及生成一个8键的游戏控制器

    文章目录 前言 一.软件下载 二.软件安装 (一)\ STM32CubeMX软件安装步骤 (二)\ Dt HID生成软件安装步骤 三.软件界面介绍 (一)\ STM32CubeMX软件界面 (二)\ ...

  7. 开始计算机USB存储功能,USB存储设备禁用怎么设置

    因为有些情况下我们必须得禁止USB存储设备,特别是涉及到当文件被泄漏或者复制时,那如何设置禁用USB存储设备呢?下面是学习啦小编为大家介绍禁用USB存储设备的设置方法,欢迎大家阅读. 禁用USB存储设 ...

  8. usb大容量存储设备驱动_usb无法识别怎么办 如何解决usb识别故障【详细步骤】...

    usb无法识别怎么办? 随着计算机硬件飞速发展,外围设备日益增多,键盘.鼠标等早已为人所共知,数码相机.MP3随身听接踵而至,这么多的设备,如何接入个人计算机?USB就是基于这个目的产生的.USB是一 ...

  9. usb大容量存储设备驱动程序_20年历史了!为什么USB接口还存在?网友:原来如此...

    USB接口作为计算机领域应用最广泛的数据接口,已有20多年的历史. 和它的名字一样,USB(Universal Serial Bus)最初也是为统一的数据接口而设计的.你几乎可以用它来代替计算机的各种 ...

最新文章

  1. 什么人适合学习web前端?怎样学好web前端开发?
  2. 同一事务多次加for_Synchronized锁在Spring事务管理下,为啥还线程不安全?
  3. 第二阶段_第四家小节_C#基础2
  4. USB接口直接焊线的顺序记录
  5. 中台到底是什么?传统企业怎样建设中台?
  6. ndroid中View.inflate()
  7. 无法向会话状态服务器发出回话状态请求
  8. 几个常用JAVA开源项目的地址荟萃
  9. UnityShader22:序列帧动画
  10. Javascript 笔记与总结(1-3)arguments
  11. mysql 向量写法_mysql – 你如何在Ruby中处理一个非常大的向量?
  12. 哪些思维方式是你刻意训练过的?
  13. Proe调入ansys中单位的协调
  14. android 9.0user版本如何开启root,打开su
  15. arcgis10.2以上版本转换测量队节点所在TXT为所需格式(arctoolbox-samples-features-creat featrure from text file所需要的面格式)
  16. 索尼 a7 IV 和佳能 EOS R6 哪个好
  17. 中兴通讯刀片服务器,中兴通讯发布行业首个OLT内置刀片技术白皮书
  18. ORB-SLAM3 单目惯导ros-system-track
  19. python怎么画地球_第12天|16天搞定Python数据分析,在地球上画个圈
  20. Java - 什么时候用断言(assert)?

热门文章

  1. 什么是大数据和大数据平台?
  2. 关于pytorch中各种矩阵乘法运算的区别
  3. Python的简单代码:两天肝出画函数图像(散点图)的程序(不用matplotlib)(含白菜也能看懂的超超超详细讲解和源代码哦)
  4. DEDECMS站点内容怎么自动更新到新浪微博
  5. 新浪微博热搜榜“背后的男人”讲述热搜背后的秘密
  6. Bootstrap系列之模态框(Modal)
  7. 字幕基础:字幕介绍、字幕种类及常见格式(转)
  8. 用CMD命令查看局域网电脑IP地址,电脑名称及MAC地址
  9. HTML+CSS错题整理
  10. 【论文笔记】Learning Enriched Features for Real Image Restoration and Enhancement