STM32 USB复合设备编写

阅读: 评论:0

STM32 USB复合设备编写

STM32 USB复合设备编写

本实验是基于野火霸道开发板STM32F103ZET6

目的

  • 完成一个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,               // bLengthUSBD_IAD_DESCRIPTOR_TYPE,         // bDescriptorTypeUSBD_CDC_FIRST_INTERFACE,         // bFirstInterfaceUSBD_CDC_INTERFACE_NUM,           // bInterfaceCount0x02,                             // bFunctionClass0x02,                             // bFunctionSubClass0x01,                             // bInterfaceProtocol0x04,                             // 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,                        // bLengthUSBD_IAD_DESCRIPTOR_TYPE,                  // bDescriptorTypeUSBD_MSC_FIRST_INTERFACE,                  // bFirstInterfaceUSBD_MSC_INTERFACE_NUM,                    // bInterfaceCount0x08,                                      // bFunctionClass0x06,                                      // bFunctionSubClass0x50,                                      // bInterfaceProtocol0x05,/********************  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 __cplusplusextern "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)STMicroelectronicsWin7)
    -> 确定执行安装。

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

测试

这里只测试虚拟串口

int main(void)
{/* USER CODE BEGIN 1 */char test[]="testn";/* 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 */}

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

 

 

本文发布于:2024-02-03 08:53:41,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170692161949968.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:设备   USB
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23