目的

  • 完成一个CDC + MSC的复合USB设备
  • 可以方便在CDC,MSC,复合设备三者间切换
  • 可移植性强

预备知识

cube中USB只有两个入口。

  • main函数中的MX_USB_DEVICE_Init函数。
/* init function */
void MX_USB_DEVICE_Init(void)
{
/* Init Device Library,Add Supported Class and Start the library*/
USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
USBD_RegisterClass(&hUsbDeviceFS, &USBD_COMPOSITE_CLASS);
USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_FS);
USBD_Start(&hUsbDeviceFS);
}
  • USB中断。USB的所有动作都是主机发起的,设备只是做响应。所以在cube中,所有的USB动作入口都是一个中断。
void USB_LP_CAN1_RX0_IRQHandler(void)
{
/* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 0 */ /* USER CODE END USB_LP_CAN1_RX0_IRQn 0 */
HAL_PCD_IRQHandler(&hpcd_USB_FS);
/* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 1 */ /* USER CODE END USB_LP_CAN1_RX0_IRQn 1 */
}

看一下中断响应函数的内容

/**
* @brief This function handles PCD interrupt request.
* @param hpcd: PCD handle
* @retval HAL status
*/
void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
{
uint32_t wInterrupt_Mask = 0; if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_CTR))
{
/* servicing of the endpoint correct transfer interrupt */
/* clear of the CTR flag into the sub */
PCD_EP_ISR_Handler(hpcd);
} if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_RESET))
{
__HAL_PCD_CLEAR_FLAG(hpcd, USB_ISTR_RESET);
HAL_PCD_ResetCallback(hpcd);
HAL_PCD_SetAddress(hpcd, 0);
} if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_PMAOVR))
{
__HAL_PCD_CLEAR_FLAG(hpcd, USB_ISTR_PMAOVR);
}
if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_ERR))
{
__HAL_PCD_CLEAR_FLAG(hpcd, USB_ISTR_ERR);
} if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_WKUP))
{
hpcd->Instance->CNTR &= ~(USB_CNTR_LP_MODE); /*set wInterrupt_Mask global variable*/
wInterrupt_Mask = USB_CNTR_CTRM | USB_CNTR_WKUPM | USB_CNTR_SUSPM | USB_CNTR_ERRM \
| USB_CNTR_ESOFM | USB_CNTR_RESETM; /*Set interrupt mask*/
hpcd->Instance->CNTR = wInterrupt_Mask; HAL_PCD_ResumeCallback(hpcd); __HAL_PCD_CLEAR_FLAG(hpcd, USB_ISTR_WKUP);
} if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_SUSP))
{
/* clear of the ISTR bit must be done after setting of CNTR_FSUSP */
__HAL_PCD_CLEAR_FLAG(hpcd, USB_ISTR_SUSP); /* Force low-power mode in the macrocell */
hpcd->Instance->CNTR |= USB_CNTR_FSUSP;
hpcd->Instance->CNTR |= USB_CNTR_LP_MODE;
if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_WKUP) == 0)
{
HAL_PCD_SuspendCallback(hpcd);
}
} if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_SOF))
{
__HAL_PCD_CLEAR_FLAG(hpcd, USB_ISTR_SOF);
HAL_PCD_SOFCallback(hpcd);
} if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_ESOF))
{
/* clear ESOF flag in ISTR */
__HAL_PCD_CLEAR_FLAG(hpcd, USB_ISTR_ESOF);
}
}

可以看出来,中断响应函数根据各个不同的中断源做出不同的操作。这点在参考手册也有提到

在USB插入之后,主机开始进行枚举,复位USB,触发复位中断,会进入下面一个if语句。

if (__HAL_PCD_GET_FLAG (hpcd, USB_ISTR_RESET))
{
__HAL_PCD_CLEAR_FLAG(hpcd, USB_ISTR_RESET);
HAL_PCD_ResetCallback(hpcd);
HAL_PCD_SetAddress(hpcd, 0);
}

这两个函数主要是设置总线速度,复位USB,以及设置USB地址。分析了这两个函数,基本了解了程序的流程,主要是两个关键的结构体

/**
* @brief PCD Handle Structure definition
*/
typedef struct
{
PCD_TypeDef *Instance; /*!< Register base address */
PCD_InitTypeDef Init; /*!< PCD required parameters */
__IO uint8_t USB_Address; /*!< USB Address: not used by USB OTG FS */
PCD_EPTypeDef IN_ep[15]; /*!< IN endpoint parameters */
PCD_EPTypeDef OUT_ep[15]; /*!< OUT endpoint parameters */
HAL_LockTypeDef Lock; /*!< PCD peripheral status */
__IO PCD_StateTypeDef State; /*!< PCD communication state */
uint32_t Setup[12]; /*!< Setup packet buffer */
void *pData; /*!< Pointer to upper stack Handler */
} PCD_HandleTypeDef; /* USB Device handle structure */
typedef struct _USBD_HandleTypeDef
{
uint8_t id;
uint32_t dev_config;
uint32_t dev_default_config;
uint32_t dev_config_status;
USBD_SpeedTypeDef dev_speed;
USBD_EndpointTypeDef ep_in[15];
USBD_EndpointTypeDef ep_out[15];
uint32_t ep0_state;
uint32_t ep0_data_len;
uint8_t dev_state;
uint8_t dev_old_state;
uint8_t dev_address;
uint8_t dev_connection_status;
uint8_t dev_test_mode;
uint32_t dev_remote_wakeup; USBD_SetupReqTypedef request;
USBD_DescriptorsTypeDef *pDesc;
USBD_ClassTypeDef *pClass;
void *pClassData;
void *pUserData;
void *pData;
} USBD_HandleTypeDef;

这两个结构体很关键,看下复位操作的函数

/**
* @brief Reset callback.
* @param hpcd: PCD handle
* @retval None
*/
void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd)
{
USBD_SpeedTypeDef speed = USBD_SPEED_FULL; /*Set USB Current Speed*/
switch (hpcd->Init.speed)
{
case PCD_SPEED_FULL:
speed = USBD_SPEED_FULL;
break; default:
speed = USBD_SPEED_FULL;
break;
}
USBD_LL_SetSpeed((USBD_HandleTypeDef*)hpcd->pData, speed); /*Reset Device*/
USBD_LL_Reset((USBD_HandleTypeDef*)hpcd->pData);
}

这里有一个强制类型转换,将一个void指针强制转换为USBD_HandleTypeDef,那么这个指针必然一早就指向了一个实体的USBD_HandleTypeDef,向上追朔调用关系

main->MX_USB_DEVICE_Init->USBD_Init->USBD_LL_Init->pdev->hpcd_USB_FS.pData = pdev

然后,hpcd_USB_FS作为中断处理函数的参数被传进来。

/**
* @brief USBD_LL_Reset
* Handle Reset event
* @param pdev: device instance
* @retval status
*/ USBD_StatusTypeDef USBD_LL_Reset(USBD_HandleTypeDef *pdev)
{
/* Open EP0 OUT */
USBD_LL_OpenEP(pdev,
0x00,
USBD_EP_TYPE_CTRL,
USB_MAX_EP0_SIZE); pdev->ep_out[0].maxpacket = USB_MAX_EP0_SIZE; /* Open EP0 IN */
USBD_LL_OpenEP(pdev,
0x80,
USBD_EP_TYPE_CTRL,
USB_MAX_EP0_SIZE); pdev->ep_in[0].maxpacket = USB_MAX_EP0_SIZE;
/* Upon Reset call user call back */
pdev->dev_state = USBD_STATE_DEFAULT; if (pdev->pClassData)
pdev->pClass->DeInit(pdev, pdev->dev_config); return USBD_OK;
} /**
* @brief USBD_LL_Reset
* Handle Reset event
* @param pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_SetSpeed(USBD_HandleTypeDef *pdev, USBD_SpeedTypeDef speed)
{
pdev->dev_speed = speed;
return USBD_OK;
}

SetSpeed比较简单,只是将hUsbDeviceFs中的dev_speed设置一下。

Reset函数则是将端点0的IN OUT端点打开,然后有一句

pdev->pClass->DeInit(pdev,pdev->dev_config)

这里又是一个USBD_ClassTypeDef类型的指针

typedef struct _Device_cb
{
uint8_t (*Init) (struct _USBD_HandleTypeDef *pdev , uint8_t cfgidx);
uint8_t (*DeInit) (struct _USBD_HandleTypeDef *pdev , uint8_t cfgidx);
/* Control Endpoints*/
uint8_t (*Setup) (struct _USBD_HandleTypeDef *pdev , USBD_SetupReqTypedef *req);
uint8_t (*EP0_TxSent) (struct _USBD_HandleTypeDef *pdev );
uint8_t (*EP0_RxReady) (struct _USBD_HandleTypeDef *pdev );
/* Class Specific Endpoints*/
uint8_t (*DataIn) (struct _USBD_HandleTypeDef *pdev , uint8_t epnum);
uint8_t (*DataOut) (struct _USBD_HandleTypeDef *pdev , uint8_t epnum);
uint8_t (*SOF) (struct _USBD_HandleTypeDef *pdev);
uint8_t (*IsoINIncomplete) (struct _USBD_HandleTypeDef *pdev , uint8_t epnum);
uint8_t (*IsoOUTIncomplete) (struct _USBD_HandleTypeDef *pdev , uint8_t epnum); uint8_t *(*GetHSConfigDescriptor)(uint16_t *length);
uint8_t *(*GetFSConfigDescriptor)(uint16_t *length);
uint8_t *(*GetOtherSpeedConfigDescriptor)(uint16_t *length);
uint8_t *(*GetDeviceQualifierDescriptor)(uint16_t *length);
#if (USBD_SUPPORT_USER_STRING == 1)
uint8_t *(*GetUsrStrDescriptor)(struct _USBD_HandleTypeDef *pdev ,uint8_t index, uint16_t *length);
#endif } USBD_ClassTypeDef;

可以看到这个类定义都是函数指针,在调用之前必定实例化(借用面向对象来词汇)过。

main -> MX_USB_DEVICE_Init->USBD_RegisterClass->pdev->pClass=pclass

如果是CDC类,那么最终指向的是在usbd_cdc.c中的USBD_CDC类,如果是复合类,那么就需要我们自己定义。

在调用DeInit过程中,又会调用两个void 指针:pclassData,pUserData

其中pClassData是初始化是在初始化各个类的时候,比如

  • MSC类

USBD_MSC_Init -> pdev->pClassData = USBD_malloc(sizeof (USBD_MSC_BOT_HandleTypeDef));

  • CDC类

USBD_CDC_Init -> pdev->pClassData = USBD_malloc(sizeof (USBD_CDC_HandleTypeDef));

不同的类,指向不同的函数指针。

pUserData是通过一个函数初始化,比如CDC类

main -> USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS);

USBD_Interface_fops_FS定义如下:

typedef struct _USBD_CDC_Itf
{
int8_t (* Init) (void);
int8_t (* DeInit) (void);
int8_t (* Control) (uint8_t, uint8_t * , uint16_t);
int8_t (* Receive) (uint8_t *, uint32_t *); }USBD_CDC_ItfTypeDef;

pClassData和pUserData在USBD_HandleTypeDef中是指针形式,所以在调用不同类的时候,改变指针的指向,即可完成不同类的功能。我们复合设备类的设计思想既是如此。

可以总结一下 USBD_HandleTypeDef中几个复杂的指针

 USBD_DescriptorsTypeDef *pDesc;
USBD_ClassTypeDef *pClass;
void *pClassData;
void *pUserData;
void *pData;
  • pDesc指向描述符的数组,在枚举阶段启用
  • pClass指向设备类,比如CDC类的USBD_ClassTypeDef,负责类的底层Init,Setup,DataIn/Out等
  • pClassData指向设备类句柄,负责记录数据,比如RxBuffer,TxBuffer,RxLength等
  • pUserData指向设备类的接口操作函数,比如CDC的Init,Receive,Transmit,比如MSC的Read,Write等
  • pData指向hpcd_USB_FS

注意点:

  • pClassData要在堆上动态生成,不能调用静态分配函数,否则两次分配会被分配到同一段内存中
#define USBD_malloc               malloc
#define USBD_free free
  • 在pClass->Init之后,pClassData分配完成,此时需要一个全局指针将这个pClassData记录下来
  • 在pClass->DeInit时,pClassData根据不同的类读回之前的全局指针,然后free掉堆上的内存
  • 由于使用了两次malloc所以heap_size要分配足够大,同时也要注意修改stack_size。
Stack_Size EQU 0x1000
Heap_Size EQU 0x800

准备工作

  • 利用cube生成一个CDC工程和一个MSC工程,将CDC工程中的usbd_cdc以及usbd_cdc_if文件拷贝出来。
  • 修改都在MSC工程下面完成。在工程下建立一个USBConfig文件夹,将之前拷贝的文件以及其他USB配置相关的文件放到这个文件夹下面。
  • 新建一个usbd_composite.c,usbd_composite.h,添加到工程。整体文件结构如下:





  • 编译(注意添加头文件路径)

修改

  • 分配好端点号
#define CDC_IN_EP                                   0x81  /* EP1 for data IN */
#define CDC_OUT_EP 0x01 /* EP1 for data OUT */
#define CDC_CMD_EP 0x83 /* EP3 for CDC commands */
#define MSC_EPIN_ADDR                0x82
#define MSC_EPOUT_ADDR 0x02
  • 将如下内容添加到usbd_composite.c以及usbdcomposite.h
/**
* @file usbd_composite.c
* @author Weyne
* @version V01
* @date 2016.10.28
* @brief MSC + CDC 复合设备
* @note
* @attention COYPRIGHT WEYNE
*/ #include "usbd_composite.h"
#include "usbd_cdc.h"
#include "usbd_msc.h" static USBD_CDC_HandleTypeDef *pCDCData;
static USBD_MSC_BOT_HandleTypeDef *pMSCData; static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev,
uint8_t cfgidx); static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev,
uint8_t cfgidx); static uint8_t USBD_Composite_EP0_RxReady(USBD_HandleTypeDef *pdev); static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev,
USBD_SetupReqTypedef *req); static uint8_t USBD_Composite_DataIn (USBD_HandleTypeDef *pdev,
uint8_t epnum); static uint8_t USBD_Composite_DataOut (USBD_HandleTypeDef *pdev,
uint8_t epnum); static uint8_t *USBD_Composite_GetFSCfgDesc (uint16_t *length); static uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length); USBD_ClassTypeDef USBD_COMPOSITE =
{
USBD_Composite_Init,
USBD_Composite_DeInit,
USBD_Composite_Setup,
NULL, /*EP0_TxSent*/
USBD_Composite_EP0_RxReady,
USBD_Composite_DataIn,
USBD_Composite_DataOut,
NULL,
NULL,
NULL,
NULL,
USBD_Composite_GetFSCfgDesc,
NULL,
USBD_Composite_GetDeviceQualifierDescriptor,
}; /* USB composite device Configuration Descriptor */
/* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
__ALIGN_BEGIN uint8_t USBD_Composite_CfgFSDesc[USBD_COMPOSITE_DESC_SIZE] __ALIGN_END =
{
0x09, /* bLength: Configuation Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
WBVAL(USBD_COMPOSITE_DESC_SIZE),
USBD_MAX_NUM_INTERFACES , /* bNumInterfaces: */
0x01, /* bConfigurationValue: */
0x04, /* iConfiguration: */
0xC0, /* bmAttributes: */
0x96, /* MaxPower 300 mA */ /****************************CDC************************************/
/* Interface Association Descriptor */
USBD_IAD_DESC_SIZE, // bLength
USBD_IAD_DESCRIPTOR_TYPE, // bDescriptorType
USBD_CDC_FIRST_INTERFACE, // bFirstInterface
USBD_CDC_INTERFACE_NUM, // bInterfaceCount
0x02, // bFunctionClass
0x02, // bFunctionSubClass
0x01, // bInterfaceProtocol
0x04, // iFunction /*Interface Descriptor */
0x09, /* bLength: Interface Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
/* Interface descriptor type */
USBD_CDC_CMD_INTERFACE, /* 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 */
0x01, /* 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 */
USBD_CDC_CMD_INTERFACE, /* bMasterInterface: Communication class interface */
USBD_CDC_DATA_INTERFACE, /* bSlaveInterface0: Data Class Interface */ /*Endpoint 2 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_CMD_EP, /* bEndpointAddress */
0x03, /* bmAttributes: Interrupt */
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_CMD_PACKET_SIZE),
0x01, /* bInterval: */ /*Data class interface descriptor*/
0x09, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
USBD_CDC_DATA_INTERFACE, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints: Two endpoints used */
0x0A, /* bInterfaceClass: CDC */
0x02, /* bInterfaceSubClass: */
0x00, /* bInterfaceProtocol: */
0x01, /* iInterface: */ /*Endpoint OUT Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_OUT_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
0x01, /* bInterval: ignore for Bulk transfer */ /*Endpoint IN Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_IN_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
0x01, /* bInterval: ignore for Bulk transfer */ /****************************MSC************************************/
/* Interface Association Descriptor */
USBD_IAD_DESC_SIZE, // bLength
USBD_IAD_DESCRIPTOR_TYPE, // bDescriptorType
USBD_MSC_FIRST_INTERFACE, // bFirstInterface
USBD_MSC_INTERFACE_NUM, // bInterfaceCount
0x08, // bFunctionClass
0x06, // bFunctionSubClass
0x50, // bInterfaceProtocol
0x05, /******************** Mass Storage interface ********************/
0x09, /* bLength: Interface Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
USBD_MSC_INTERFACE, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints*/
0x08, /* bInterfaceClass: MSC Class */
0x06, /* bInterfaceSubClass : SCSI transparent*/
0x50, /* nInterfaceProtocol */
0x05, /* iInterface: */ /******************** Mass Storage Endpoints ********************/
0x07, /*Endpoint descriptor length = 7*/
0x05, /*Endpoint descriptor type */
MSC_EPIN_ADDR, /*Endpoint address (IN, address 1) */
0x02, /*Bulk endpoint type */
LOBYTE(MSC_MAX_FS_PACKET),
HIBYTE(MSC_MAX_FS_PACKET),
0x01, /*Polling interval in milliseconds */ 0x07, /*Endpoint descriptor length = 7 */
0x05, /*Endpoint descriptor type */
MSC_EPOUT_ADDR, /*Endpoint address (OUT, address 1) */
0x02, /*Bulk endpoint type */
LOBYTE(MSC_MAX_FS_PACKET),
HIBYTE(MSC_MAX_FS_PACKET),
0x01, /*Polling interval in milliseconds*/ }; /* USB Standard Device Descriptor */
__ALIGN_BEGIN uint8_t USBD_Composite_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] __ALIGN_END =
{
USB_LEN_DEV_QUALIFIER_DESC,
USB_DESC_TYPE_DEVICE_QUALIFIER,
0x00,
0x02,
0x00,
0x00,
0x00,
0x40,
0x01,
0x00,
}; /**
* @brief USBD_Composite_Init
* Initialize the Composite interface
* @param pdev: device instance
* @param cfgidx: Configuration index
* @retval status
*/
static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev,
uint8_t cfgidx)
{
uint8_t res = 0; pdev->pUserData = &USBD_CDC_Interface_fops_FS;
res += USBD_CDC.Init(pdev,cfgidx);
pCDCData = pdev->pClassData;
pdev->pUserData = &USBD_Storage_Interface_fops_FS;
res += USBD_MSC.Init(pdev,cfgidx);
pMSCData = pdev->pClassData;
return res;
} /**
* @brief USBD_Composite_DeInit
* DeInitilaize the Composite configuration
* @param pdev: device instance
* @param cfgidx: configuration index
* @retval status
*/
static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev,
uint8_t cfgidx)
{
uint8_t res = 0;
pdev->pClassData = pCDCData;
pdev->pUserData = &USBD_CDC_Interface_fops_FS;
res += USBD_CDC.DeInit(pdev,cfgidx); pdev->pClassData = pMSCData;
pdev->pUserData = &USBD_Storage_Interface_fops_FS;
res += USBD_MSC.DeInit(pdev,cfgidx); return res;
} static uint8_t USBD_Composite_EP0_RxReady(USBD_HandleTypeDef *pdev)
{
return USBD_CDC.EP0_RxReady(pdev);
} /**
* @brief USBD_Composite_Setup
* Handle the Composite requests
* @param pdev: device instance
* @param req: USB request
* @retval status
*/
static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
switch (req->bmRequest & USB_REQ_RECIPIENT_MASK)
{
case USB_REQ_RECIPIENT_INTERFACE:
switch(req->wIndex)
{
case USBD_CDC_DATA_INTERFACE:
case USBD_CDC_CMD_INTERFACE:
pdev->pClassData = pCDCData;
pdev->pUserData = &USBD_CDC_Interface_fops_FS;
return(USBD_CDC.Setup(pdev, req)); case USBD_MSC_INTERFACE:
pdev->pClassData = pMSCData;
pdev->pUserData = &USBD_Storage_Interface_fops_FS;
return(USBD_MSC.Setup (pdev, req)); default:
break;
}
break; case USB_REQ_RECIPIENT_ENDPOINT:
switch(req->wIndex)
{ case CDC_IN_EP:
case CDC_OUT_EP:
case CDC_CMD_EP:
pdev->pClassData = pCDCData;
pdev->pUserData = &USBD_CDC_Interface_fops_FS;
return(USBD_CDC.Setup(pdev, req)); case MSC_EPIN_ADDR:
case MSC_EPOUT_ADDR:
pdev->pClassData = pMSCData;
pdev->pUserData = &USBD_Storage_Interface_fops_FS;
return(USBD_MSC.Setup (pdev, req)); default:
break;
}
break;
}
return USBD_OK;
} /**
* @brief USBD_Composite_DataIn
* handle data IN Stage
* @param pdev: device instance
* @param epnum: endpoint index
* @retval status
*/
uint8_t USBD_Composite_DataIn (USBD_HandleTypeDef *pdev,
uint8_t epnum)
{
switch(epnum)
{
case CDC_INDATA_NUM:
pdev->pClassData = pCDCData;
pdev->pUserData = &USBD_CDC_Interface_fops_FS;
return(USBD_CDC.DataIn(pdev,epnum)); case MSC_INDATA_NUM:
pdev->pClassData = pMSCData;
pdev->pUserData = &USBD_Storage_Interface_fops_FS;
return(USBD_MSC.DataIn(pdev,epnum)); default:
break; }
return USBD_FAIL;
} /**
* @brief USBD_Composite_DataOut
* handle data OUT Stage
* @param pdev: device instance
* @param epnum: endpoint index
* @retval status
*/
uint8_t USBD_Composite_DataOut (USBD_HandleTypeDef *pdev,
uint8_t epnum)
{
switch(epnum)
{
case CDC_OUTDATA_NUM:
case CDC_OUTCMD_NUM:
pdev->pClassData = pCDCData;
pdev->pUserData = &USBD_CDC_Interface_fops_FS;
return(USBD_CDC.DataOut(pdev,epnum)); case MSC_OUTDATA_NUM:
pdev->pClassData = pMSCData;
pdev->pUserData = &USBD_Storage_Interface_fops_FS;
return(USBD_MSC.DataOut(pdev,epnum)); default:
break; }
return USBD_FAIL;
} /**
* @brief USBD_Composite_GetHSCfgDesc
* return configuration descriptor
* @param length : pointer data length
* @retval pointer to descriptor buffer
*/
uint8_t *USBD_Composite_GetFSCfgDesc (uint16_t *length)
{
*length = sizeof (USBD_Composite_CfgFSDesc);
return USBD_Composite_CfgFSDesc;
} /**
* @brief DeviceQualifierDescriptor
* return Device Qualifier descriptor
* @param length : pointer data length
* @retval pointer to descriptor buffer
*/
uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length)
{
*length = sizeof (USBD_Composite_DeviceQualifierDesc);
return USBD_Composite_DeviceQualifierDesc;
} /**
* @}
*/ /**
* @}
*/ /**
* @}
*/ /************************ (C) COPYRIGHT WEYNE *****END OF FILE****/

/**
* @file usbd_composite.h
* @author Weyne
* @version V01
* @date 2016.10.28
* @brief MSC + CDC 复合设备
* @note
* @attention COYPRIGHT WEYNE
*/ /* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USBD_COMPOSITE_H
#define __USBD_COMPOSITE_H #ifdef __cplusplus
extern "C" {
#endif /* Includes ------------------------------------------------------------------*/
#include "usbd_msc.h"
#include "usbd_cdc.h"
#include "usbd_storage_if.h"
#include "usbd_cdc_if.h" #define WBVAL(x) (x & 0xFF),((x >> 8) & 0xFF)
#define DBVAL(x) (x & 0xFF),((x >> 8) & 0xFF),((x >> 16) & 0xFF),((x >> 24) & 0xFF) #define USBD_IAD_DESC_SIZE 0x08
#define USBD_IAD_DESCRIPTOR_TYPE 0x0B #define USBD_CDC_FIRST_INTERFACE 0 /* CDC FirstInterface */
#define USBD_CDC_INTERFACE_NUM 2 /* CDC Interface NUM */ #define USBD_CDC_CMD_INTERFACE 0
#define USBD_CDC_DATA_INTERFACE 1 #define USBD_MSC_FIRST_INTERFACE 2 /* MSC FirstInterface */
#define USBD_MSC_INTERFACE_NUM 1 /* MSC Interface NUM */ #define USBD_MSC_INTERFACE 2 #define MSC_INDATA_NUM (MSC_EPIN_ADDR & 0x0F)
#define MSC_OUTDATA_NUM (MSC_EPOUT_ADDR & 0x0F) #define CDC_INDATA_NUM (CDC_IN_EP & 0x0F)
#define CDC_OUTDATA_NUM (CDC_OUT_EP & 0x0F)
#define CDC_OUTCMD_NUM (CDC_CMD_EP & 0x0F) #define USBD_COMPOSITE_DESC_SIZE (9 + 58 + 8 + 32 + 8) extern USBD_ClassTypeDef USBD_COMPOSITE; /**
* @}
*/ /**
* @}
*/ #ifdef __cplusplus
}
#endif #endif /* __USBD_MSC_H */
/**
* @}
*/ /************************ (C) COPYRIGHT WEYNE *****END OF FILE****/

注意我这里修改了cube原来在usbd_cdc_if中的fops变量名(原来的命名不合理),直接编译会报错,需要修改下。

  • 修改 usbd_device.h
/* init function */
void MX_USB_DEVICE_Init(void)
{
/* Init Device Library,Add Supported Class and Start the library*/
USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS); USBD_RegisterClass(&hUsbDeviceFS, &USBD_COMPOSITE); // USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_Interface_fops_FS); USBD_Start(&hUsbDeviceFS); }
  • 修改 usbd_conf.h
#define USBD_MAX_NUM_INTERFACES     3

#define USBD_malloc               malloc
#define USBD_free free
  • 修改usbd_conf.c
/**
* @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 */
/* Link The driver to the stack */
hpcd_USB_FS.pData = pdev;
pdev->pData = &hpcd_USB_FS; hpcd_USB_FS.Instance = USB;
hpcd_USB_FS.Init.dev_endpoints = 8;
hpcd_USB_FS.Init.speed = PCD_SPEED_FULL;
hpcd_USB_FS.Init.ep0_mps = DEP0CTL_MPS_8;
hpcd_USB_FS.Init.low_power_enable = DISABLE;
hpcd_USB_FS.Init.lpm_enable = DISABLE;
hpcd_USB_FS.Init.battery_charging_enable = DISABLE;
if (HAL_PCD_Init(&hpcd_USB_FS) != HAL_OK)
{
Error_Handler();
} HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, 0x18);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x58);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_IN_EP , PCD_SNG_BUF, 0x98);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_OUT_EP , PCD_SNG_BUF, 0xD8);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , MSC_EPIN_ADDR , PCD_SNG_BUF, 0x118);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , MSC_EPOUT_ADDR , PCD_SNG_BUF, 0x158);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_CMD_EP , PCD_SNG_BUF, 0x198); return USBD_OK;
}

修改完成

安装驱动

  • 安装ST的虚拟串口驱动。插上复合设备后,如果提示找不到驱动,可以采用下面的办法
  • 在设备管理器中找到 STMicroelectronics -> 右键 -> 更新驱动程序软件 -> 浏览计算机以查找驱动

    程序软件 -> 从计算机的设备驱动程序列表中选择 -> 选择“端口(COM和LPT)”-> 从磁盘安装 ->

    找到stmcdc.inf的目录并选择stmcdc.inf(一般在C:\Program Files (x86)\STMicroelectronics\Win7)

    -> 确定执行安装。

至此,设备可以枚举成功了,电脑中出现了串口号和U盘。

测试

这里只测试虚拟串口

int main(void)
{ /* USER CODE BEGIN 1 */
char test[]="test\n";
/* USER CODE END 1 */ /* MCU Configuration----------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init(); /* Configure the system clock */
SystemClock_Config(); /* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USB_DEVICE_Init(); /* USER CODE BEGIN 2 */
HAL_Delay(10000);
/* USER CODE END 2 */ /* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
CDC_Transmit_FS((uint8_t*)test,sizeof(test));
HAL_Delay(500);
/* USER CODE BEGIN 3 */ }
/* USER CODE END 3 */ }

测试串口可以正常收到数据

转载请注明出处!

STM32 USB复合设备编写的更多相关文章

  1. STM32 USB 问题汇总(转)

    源:STM32 USB 问题汇总 汇总1:STM32的USB例程修改步骤,来自http://blog.csdn.net/cy757/archive/2010/01/01/5117610.aspx 以下 ...

  2. STM32 USB Virtual COM

    STM32 USB Virtual COM USB转串口的功能实现   这次讲的是如何实现USB转串口功能的实现.首先看看工程的布局吧: 我们主要要介绍的文件的在USB_User这个组文件.从上面的截 ...

  3. STM32 USB转串口驱动 Virtual COM Port Driver(V1.3.1)

    将stm32的USB口接到PC端后,PC端会要求安装一个STM32 USB Virtual COM Port Driver,然后就可以用串口调试助手对其操作了

  4. STM32 USB虚拟串口(转)

    源:STM32 USB虚拟串口 串口调试在项目中被使用越来越多,串口资源的紧缺也变的尤为突出.很多本本人群,更是深有体会,不准备一个USB转串口工具就没办法进行开发.本章节来简单概述STM32低端芯片 ...

  5. USB Compound Device,USB复合设备 ; USB Composite Device,USB组合设备【转】

    本文转载自:https://blog.csdn.net/autumn20080101/article/details/52776863 科普下USB复合设备和USB组合设备的区别. 关键字 Commu ...

  6. STM32 USB虚拟串口

    串口调试在项目中被使用越来越多,串口资源的紧缺也变的尤为突出.很多本本人群,更是深有体会,不准备一个USB转串口工具就没办法进行开发.本章节来简单概述STM32低端芯片上的USB虚拟串口的移植.在官方 ...

  7. STM32 复合设备编写

    目的 完成一个CDC + MSC的复合USB设备 可以方便在CDC,MSC,复合设备三者间切换 可移植性强 预备知识 cube中USB只有两个入口. main函数中的MX_USB_DEVICE_Ini ...

  8. stm32 usb数据接收与数据发送程序流程分析

    http://blog.csdn.net/u011318735/article/details/17424349 既然学习了USB,那就必须的搞懂USB设备与USB主机数据是怎么通讯的.这里主要讲设备 ...

  9. STM32 USB FS Core and USB OTG Core

    STM32 USB-FS-Device development kit Compatible with the STM32F102xx and STM32F103xx series, STM32 L1 ...

随机推荐

  1. Global Round 2

    A - Ilya and a Colorful Walk CodeForces - 1119A Ilya lives in a beautiful city of Chordalsk. There a ...

  2. java用XSSFWorkbook实现读写Excel

    /** * 读取Excel文件的内容 * @param inputStream excel文件,以InputStream的形式传入 * @param sheetName sheet名字 * @retu ...

  3. fork 和 exec

    https://blog.csdn.net/disadministrator/article/details/39347333 进程创建方法:fork.exec.clone,父进程等待子进程结束是用w ...

  4. Linux test命令

    test命令 长格式的例子: test "$A" == "$B" && echo "Strings are equal" t ...

  5. 在Ubuntu 16.04 LTS上用g++和gcc编译C/C++代码错误提示“.../x86_64-linux-gnu/crt1.o: ELF section name out of range”

    (有一些图片我是直接从个人的CSDN博客上复制来的) 最近一个多月来,我曾经多次尝试在Ubuntu 16.04 LTS上使用g++和gcc(这俩好像合起来叫MinGW?)来编译C/C++代码,但是在解 ...

  6. 数组和String几种方法的需要注意的地方

    array的方法总结 会更改原来的的数组 push.unshift方法,返回length.增加值得就返回length,其他返回该元素 pop,shift返回该元素 reverse返回该元素 splic ...

  7. POJ-2773 Happy 2006,暴力2700ms+水过!

                                                         Happy 2006 这个题很可能会超时的,但我几乎暴力的方法2700ms+过了,可能是后台水 ...

  8. centos配置mutt跟msmtp发送邮件

    一.安装mutt yum install mutt 二.配置mutt vi /etc/Muttrc 在里面找到下面几行,并将内容修改为你自己的内容(下面几行分布在不同位置,请耐心查找,记得去掉它行首的 ...

  9. SpringMVC(8) - 处理器映射

    在以前的Spring版本中,用户需要在Web应用程序上下文中定义一个或多个HandlerMapping bean,以将传入的Web请求映射到适当的处理器.通过引入带注解的控制器,就不需要像之前那样定义 ...

  10. gridview读取Excel文件中的数据,并将其导入数据库

    原文发布时间为:2008-10-16 -- 来源于本人的百度文章 [由搬家工具导入] //将需要导入的文件上传到服务器           string filePath = "" ...