STM32 复合设备编写
目的
- 完成一个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 复合设备编写的更多相关文章
- STM32 USB复合设备编写
目的 完成一个CDC + MSC的复合USB设备 可以方便在CDC,MSC,复合设备三者间切换 可移植性强 预备知识 cube中USB只有两个入口. main函数中的MX_USB_DEVICE_Ini ...
- STM32程序编写或调试犯过的错误
1.宏定义后加了分号: eg: define NOKEY_PRES 0; (❌) define NOKEY_PRES 0 (✔) 2.
- 转载:关于STM32硬件I2C读写EEPROM代码实现原理的理解与总结
http://home.eeworld.com.cn/my/space-uid-716241-blogid-655190.html 一.I2C协议简介 I2C是两线式串行总线,用于连接微控制器及其外围 ...
- 单片机modebus RTU通信实现,採用C语言,可适用于单片机,VC,安卓等
当前使用的是STM32+ucos_ii编写的,能够移植到安卓以及VC .NET等方便移植使用,採用modebus poll測试过. 仅仅须要改动响应的通信接口就可以,方便多串口使用 //modebus ...
- CAN通信工作原理个人心得
CAN总线结构示意图: 说明: 1:CAN收发器(示意图中的单元)根据两总线CAN_H和CAN_L的电位差来判断总线电平: 2:实际中CAN_H与CAN_L由双绞线组成: 3:数据传递终端的电阻器,是 ...
- STM32实现HID和u盘复合设备
USB设备可以定义一个复合设备,复合设备分两种,一种是一个设备多个配置,还有一种是一个配置多个接口,在本例中采用一个配置多个接口的方式 首先修改设备描述符,标准设备描述符和报告描述符都不需要修改, ...
- flash stm32的flash编写
定义一个全局变量数组:const u8 TEXT_Buffer[]={"STM32F103 FLASH TEST"}; //u8和char* 写入到内存里会有什么区别???? ...
- 编写一个stm32 svc关中断函数
做到了让stm32触发svc中断并传递进去参数然后切换到handler模式并修改特殊寄存器的值,从而达到关中断,但是其实这个程序直接就是特权级,故不进入handler模式也可以修改特殊寄存器..... ...
- STM32之输入捕获以及小小应用(库)
五一之际,先祝大家五一快乐.其实快乐很简单,工作的人有假放,学习的人也有假放,像我,有假放才有更多的时间学自己想学的东西.51假期学51,可惜没有32假期呀.好了..言归正传,大家听过吸星大法吧..在 ...
随机推荐
- MySQL高可用方案
高可用架构对于互联网服务基本是标配,无论是应用服务还是数据库服务都需要做到高可用.虽然互联网服务号称7*24小时不间断服务,但多多少少有一些时候服务不可用,比如某些时候网页打不开,百度不能搜索或者无法 ...
- Yii 开发微信 '您提交的数据无法被验证'
使用Yii开发微信时,出现 [error][yii\web\HttpException:] exception 'yii\web\BadRequestHttpException' with messa ...
- Oracle 数据库导入导出 dmp文件
转自: http://hi.baidu.com/ooofcu/blog/item/ec5d1f9580d41f007af48077.html 首先询问对方数据库的表空间名称和大小,然后在你的oracl ...
- Apache主配置文件httpd.conf 详解
Apache的主配置文件:/etc/httpd/conf/httpd.conf 默认站点主目录:/var/www/html/ Apache服务器的配置信息全部存储在主配置文件/etc/httpd/co ...
- python注释、脚本参数、字节码
python注释.脚本参数.字节码 --道心 python安装 1.下载安装包 https://www.python.org/downloads/ 2.安装 默认安装路径:C:\python27 3. ...
- RHEL6.3系统安装
进入安装界面 这里选择跳过 点击下一步 选择安装语言 选择键盘 选择系统储存方式 选择是否格式化储存设备 给安装的系统一个计算机名 选择时区 给ro ...
- 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 ...
- 【Linux命令】文件和目录操作命令
本文主要用于常用命令的备忘,具体用法可用man查看,或查询其他资料. cd:改变工作目录 ls:列出目录的内容 mkdir:创建一个目录 cat:连接并显示指定的一个和多个文件的有关信息 cp:将给出 ...
- Bluemix中国版体验(一)
很高兴终于拿到了中国版Bluemix的账号!中国版的Bluemix是由世纪互联运营的,这也是世纪互联继Microsoft Azure,Office 365之后运营的又一个国际一线大品牌的云服务. 中国 ...
- Java泛型及实践
代码及说明: package com.zsm.crazyjava; import java.util.ArrayList; import java.util.Collection; import ja ...