目的

  • 完成一个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. MySQL内外联结

    一.内联结(INNER JOIN) MySQL内联结使用INNER JOIN将多个数据表t1,t2隔开,结果是t1里的每一个数据行将与t2里的每一个数据行组合. 逗号连接符.CROSS JOIN和JO ...

  2. Centos7 安装 MySQL8以及远程访问的配置

    Centos7 安装MySQL8 1. 添加MySQL8的本地源 执行以下命令获取安装MySQL源 [root@virde ~]# wget https://repo.mysql.com//mysql ...

  3. 深入Linux内核架构——进程虚拟内存

    逆向映射(reverse mapping)技术有助于从虚拟内存页跟踪到对应的物理内存页: 缺页处理(page fault handling)允许从块设备按需读取数据填充虚拟地址空间. 一.简介 用户虚 ...

  4. Android布局之线性布局——LinearLayout

    本文将详细介绍线性布局的各种xml属性. xml属性 <?xml version="1.0" encoding="utf-8"?> <Line ...

  5. Vijos 1308 埃及分数(迭代加深搜索)

    题意: 输入a.b, 求a/b 可以由多少个埃及分数组成. 埃及分数是形如1/a , a是自然数的分数. 如2/3 = 1/2 + 1/6, 但埃及分数中不允许有相同的 ,如不可以2/3 = 1/3 ...

  6. python005 Python3 注释

    Python3 注释确保对模块, 函数, 方法和行内注释使用正确的风格Python中的注释有单行注释和多行注释:Python中单行注释以 # 开头,例如:: # 这是一个注释 print(" ...

  7. SSH常见问题集锦

    /WEB-INF/web.xml Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则. /WEB-INF/classes/包含了站点所有用的 class 文件,包括 ser ...

  8. 【ITOO 2】.NET 动态建库建表:使用SQL字符串拼接方式

    导读:在最近接手的项目(高效云平台)中,有一个需求是要当企业用户注册时,给其动态的新建一个库和表.刚开始接手的时候,是一点头绪都没有,然后查了一些资料,也问了问上一版本的师哥师姐,终于有了点头绪.目前 ...

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

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

  10. poj3468区间延迟更新模板题

    #include<stdio.h> #include<string.h> #define N 100000 struct st{  int x,y;  __int64 yanc ...