在上一篇文章实现USB虚拟U盘之后,项目需要用同一个USB口同时实现MSC和CDC功能,既能进行串口通信又能读取片外FLASH虚拟U盘。对于USB通用串行总线如果要真正搞明白这个协议还是比较困难的,需要用不少时间来了解驱动原代码,但是如果仅仅会用USB串行通信或者大容量存储这些功能还是相对容易。
STM32F407ZGTX,板子上包含一片片外FLASH(w25q128)芯片
另外,USB_CDC 工程文件,USB_MSC 工程文件准备好。
对于USB复合设备,同一个USB串口实现MSC+CDC功能
在实现此功能之前,确保对于这两个功能MSC和CDC均已单独完成并测试可行。USB复合设备实现,即对这两个工程的相关文件进行整合,最关键的是对于描述符的修改。配置描述符,接口描述符,端点描述符等。一个设备仅有一个设备描述符,可以有一个或多个配置描述符
前面文章详细介绍了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复合设备测试完毕