目的

  • 完成一个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 复合设备编写的更多相关文章

  1. STM32 USB复合设备编写

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

  2. STM32程序编写或调试犯过的错误

    1.宏定义后加了分号: eg: define NOKEY_PRES 0;      (❌) define NOKEY_PRES 0      (✔) 2.

  3. 转载:关于STM32硬件I2C读写EEPROM代码实现原理的理解与总结

    http://home.eeworld.com.cn/my/space-uid-716241-blogid-655190.html 一.I2C协议简介 I2C是两线式串行总线,用于连接微控制器及其外围 ...

  4. 单片机modebus RTU通信实现,採用C语言,可适用于单片机,VC,安卓等

    当前使用的是STM32+ucos_ii编写的,能够移植到安卓以及VC .NET等方便移植使用,採用modebus poll測试过. 仅仅须要改动响应的通信接口就可以,方便多串口使用 //modebus ...

  5. CAN通信工作原理个人心得

    CAN总线结构示意图: 说明: 1:CAN收发器(示意图中的单元)根据两总线CAN_H和CAN_L的电位差来判断总线电平: 2:实际中CAN_H与CAN_L由双绞线组成: 3:数据传递终端的电阻器,是 ...

  6. STM32实现HID和u盘复合设备

      USB设备可以定义一个复合设备,复合设备分两种,一种是一个设备多个配置,还有一种是一个配置多个接口,在本例中采用一个配置多个接口的方式 首先修改设备描述符,标准设备描述符和报告描述符都不需要修改, ...

  7. flash stm32的flash编写

    定义一个全局变量数组:const u8 TEXT_Buffer[]={"STM32F103 FLASH TEST"};    //u8和char* 写入到内存里会有什么区别???? ...

  8. 编写一个stm32 svc关中断函数

    做到了让stm32触发svc中断并传递进去参数然后切换到handler模式并修改特殊寄存器的值,从而达到关中断,但是其实这个程序直接就是特权级,故不进入handler模式也可以修改特殊寄存器..... ...

  9. STM32之输入捕获以及小小应用(库)

    五一之际,先祝大家五一快乐.其实快乐很简单,工作的人有假放,学习的人也有假放,像我,有假放才有更多的时间学自己想学的东西.51假期学51,可惜没有32假期呀.好了..言归正传,大家听过吸星大法吧..在 ...

随机推荐

  1. MySQL高可用方案

    高可用架构对于互联网服务基本是标配,无论是应用服务还是数据库服务都需要做到高可用.虽然互联网服务号称7*24小时不间断服务,但多多少少有一些时候服务不可用,比如某些时候网页打不开,百度不能搜索或者无法 ...

  2. Yii 开发微信 '您提交的数据无法被验证'

    使用Yii开发微信时,出现 [error][yii\web\HttpException:] exception 'yii\web\BadRequestHttpException' with messa ...

  3. Oracle 数据库导入导出 dmp文件

    转自: http://hi.baidu.com/ooofcu/blog/item/ec5d1f9580d41f007af48077.html 首先询问对方数据库的表空间名称和大小,然后在你的oracl ...

  4. Apache主配置文件httpd.conf 详解

    Apache的主配置文件:/etc/httpd/conf/httpd.conf 默认站点主目录:/var/www/html/ Apache服务器的配置信息全部存储在主配置文件/etc/httpd/co ...

  5. python注释、脚本参数、字节码

    python注释.脚本参数.字节码 --道心 python安装 1.下载安装包 https://www.python.org/downloads/ 2.安装 默认安装路径:C:\python27 3. ...

  6. RHEL6.3系统安装

      进入安装界面   这里选择跳过   点击下一步   选择安装语言     选择键盘   选择系统储存方式   选择是否格式化储存设备     给安装的系统一个计算机名     选择时区   给ro ...

  7. 04.ubuntu下kvm 命令行安装64位ubuntu报"Couldn't find hvm kernel for Ubuntu tree."的问题

    1.安装ubuntu时使用的virt-install的配置: virt-install \ --name test4 \ --ram 1024 \ --disk path=/data/01_ubunt ...

  8. 【Linux命令】文件和目录操作命令

    本文主要用于常用命令的备忘,具体用法可用man查看,或查询其他资料. cd:改变工作目录 ls:列出目录的内容 mkdir:创建一个目录 cat:连接并显示指定的一个和多个文件的有关信息 cp:将给出 ...

  9. Bluemix中国版体验(一)

    很高兴终于拿到了中国版Bluemix的账号!中国版的Bluemix是由世纪互联运营的,这也是世纪互联继Microsoft Azure,Office 365之后运营的又一个国际一线大品牌的云服务. 中国 ...

  10. Java泛型及实践

    代码及说明: package com.zsm.crazyjava; import java.util.ArrayList; import java.util.Collection; import ja ...