本文只对HID和CDC组合设备生成做讲解,关于USB设备描述符等请大家参考本人之前的博客
HID工程配置完成之后,修改主函数即可将数据发送出来
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint8_t send_buf[]={0x00,0x00,0x00,0x00};
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USB_DEVICE_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
USBD_HID_SendReport(&hUsbDeviceFS,send_buf,sizeof(send_buf));
HAL_Delay(100);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
主要是调用了USBD_HID_SendReport(&hUsbDeviceFS,send_buf,sizeof(send_buf));
函数发送
需要注意的是此HID只配置了输入端点(也就是只能发数据),并没有配置输出端点(因此并不能通过HID接收数据)
CDC基础工程生成之后,修改主函数,
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint8_t buf[]="abcd";
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USB_DEVICE_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
CDC_Transmit_FS(buf,sizeof(buf));
HAL_Delay(100);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
主要是调用CDC_Transmit_FS(buf,sizeof(buf));
函数发送数据出来,然后电脑端打开串口调试助手,打开串口,即可看到数据
至此HID和CDC的基础工程已经配置完成了,接下来就是使用这两个基础工程来搭建HID+CDC复合设备了!
其实复合设备的编写大家还可以参考阅读此篇博客STM32 USB复合设备编写,然后需要大家认真去理解一下usb的程序了,需要大家自己去看下代码,对整个框架有个熟悉,大家也可以借助debug调试,看代码如何运行的。
/**
* Init USB device Library, add supported class and start the library
* @retval None
*/
void MX_USB_DEVICE_Init(void)
{
/* USER CODE BEGIN USB_DEVICE_Init_PreTreatment */
/* USER CODE END USB_DEVICE_Init_PreTreatment */
/* Init Device Library, add supported class and start the library. */
if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK)
{
Error_Handler();
}
if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_COMPOSITE) != USBD_OK)
{
Error_Handler();
}
// if (USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_CDC_Interface_fops_FS) != USBD_OK)
// {
// Error_Handler();
// }
if (USBD_Start(&hUsbDeviceFS) != USBD_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USB_DEVICE_Init_PostTreatment */
/* USER CODE END USB_DEVICE_Init_PostTreatment */
}
MX_USB_DEVICE_Init
用来初始化USB,主要是注册相关函数USBD_RegisterClass
用来注册类,将指针指向对应指针函数
USBD_COMPOSITE
具体函数为USBD_ClassTypeDef USBD_COMPOSITE =
{
USBD_Composite_Init,
USBD_Composite_DeInit,
USBD_Composite_Setup,
NULL, /*EP0_TxSent*/
USBD_Composite_EP0_RxReady, //add
USBD_Composite_DataIn,
USBD_Composite_DataOut,
NULL,
NULL,
NULL,
NULL,
USBD_Composite_GetFSCfgDesc,
NULL,
USBD_Composite_GetDeviceQualifierDescriptor,
};
主要用来实现usb复合设备的初始化,setup,数据输入输出处理等待
USBD_ClassTypeDef
是一个函数指针结构体
```c
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_DESC == 1U)
uint8_t *(*GetUsrStrDescriptor)(struct _USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length);
#endif
} USBD_ClassTypeDef;
```
USBD_CDC_RegisterInterface
用来注册接口函数的,比如串口的发送接收函数等等
USBD_CDC_ItfTypeDef USBD_CDC_Interface_fops_FS =
{
CDC_Init_FS,
CDC_DeInit_FS,
CDC_Control_FS,
CDC_Receive_FS
};
这里注释是因为我们在后面的函数中将指针指过去了,也就是这一步我们在后面的步骤中已经实现了,所以这里不再需要
我们配置了USB之后,CubeMX就会自动配置中断,所有的USB通讯都通过中断完成(接收肯定是这样,发送是不是还没研究)
中断函数如下:
/**
* @brief This function handles USB OTG FS global interrupt.
*/
void OTG_FS_IRQHandler(void)
{
/* USER CODE BEGIN OTG_FS_IRQn 0 */
/* USER CODE END OTG_FS_IRQn 0 */
HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS);
/* USER CODE BEGIN OTG_FS_IRQn 1 */
/* USER CODE END OTG_FS_IRQn 1 */
}
进入HAL_PCD_IRQHandler
函数
我使用debug发现,主要处理在以下部分
//截取其中一部分
while (ep_intr != 0U)
{
if ((ep_intr & 0x1U) != 0U)
{
epint = USB_ReadDevOutEPInterrupt(hpcd->Instance, (uint8_t)epnum);
if ((epint & USB_OTG_DOEPINT_XFRC) == USB_OTG_DOEPINT_XFRC)
{
CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_XFRC);
(void)PCD_EP_OutXfrComplete_int(hpcd, epnum);
}
if ((epint & USB_OTG_DOEPINT_STUP) == USB_OTG_DOEPINT_STUP)
{
CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_STUP);
/* Class B setup phase done for previous decoded setup */
(void)PCD_EP_OutSetupPacket_int(hpcd, epnum);
}
if ((epint & USB_OTG_DOEPINT_OTEPDIS) == USB_OTG_DOEPINT_OTEPDIS)
{
CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_OTEPDIS);
}
/* Clear Status Phase Received interrupt */
if ((epint & USB_OTG_DOEPINT_OTEPSPR) == USB_OTG_DOEPINT_OTEPSPR)
{
CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_OTEPSPR);
}
/* Clear OUT NAK interrupt */
if ((epint & USB_OTG_DOEPINT_NAK) == USB_OTG_DOEPINT_NAK)
{
CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_NAK);
}
}
epnum++;
ep_intr >>= 1U;
}
}
主要的处理在(void)PCD_EP_OutXfrComplete_int(hpcd, epnum);
和 (void)PCD_EP_OutSetupPacket_int(hpcd, epnum);
内。
(void)PCD_EP_OutSetupPacket_int(hpcd, epnum);
函数分析HAL_PCD_SetupStageCallback(hpcd);
USBD_LL_SetupStage((USBD_HandleTypeDef*)hpcd->pData, (uint8_t *)hpcd->Setup);
,核心部分在这里面 /**
* @brief USBD_SetupStage
* Handle the setup stage
* @param pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_SetupStage(USBD_HandleTypeDef *pdev, uint8_t *psetup)
{
USBD_ParseSetupRequest(&pdev->request, psetup);
pdev->ep0_state = USBD_EP0_SETUP;
pdev->ep0_data_len = pdev->request.wLength;
switch (pdev->request.bmRequest & 0x1FU)
{
case USB_REQ_RECIPIENT_DEVICE:
USBD_StdDevReq(pdev, &pdev->request);
break;
case USB_REQ_RECIPIENT_INTERFACE:
USBD_StdItfReq(pdev, &pdev->request);
break;
case USB_REQ_RECIPIENT_ENDPOINT:
USBD_StdEPReq(pdev, &pdev->request);
break;
default:
USBD_LL_StallEP(pdev, (pdev->request.bmRequest & 0x80U));
break;
}
return USBD_OK;
}
主要在switch内调用不同的函数,实现设备枚举等内容,大家点进去之后就可以发现,他的调用过程都是使用指针指向对应的函数,而指向的内容在usb初始化的时候已经设置好。
(void)PCD_EP_OutXfrComplete_int(hpcd, epnum);
函数分析HAL_PCD_DataOutStageCallback(hpcd, (uint8_t)epnum);
,USBD_LL_DataOutStage((USBD_HandleTypeDef*)hpcd->pData, epnum, hpcd->OUT_ep[epnum].xfer_buff);
,核心在这里面
/**
* @brief USBD_DataOutStage
* Handle data OUT stage
* @param pdev: device instance
* @param epnum: endpoint index
* @retval status
*/
USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev,
uint8_t epnum, uint8_t *pdata)
{
USBD_EndpointTypeDef *pep;
if (epnum == 0U)
{
pep = &pdev->ep_out[0];
if (pdev->ep0_state == USBD_EP0_DATA_OUT)
{
if (pep->rem_length > pep->maxpacket)
{
pep->rem_length -= pep->maxpacket;
USBD_CtlContinueRx(pdev, pdata,
(uint16_t)MIN(pep->rem_length, pep->maxpacket));
}
else
{
if ((pdev->pClass->EP0_RxReady != NULL) &&
(pdev->dev_state == USBD_STATE_CONFIGURED))
{
pdev->pClass->EP0_RxReady(pdev);
}
USBD_CtlSendStatus(pdev);
}
}
else
{
if (pdev->ep0_state == USBD_EP0_STATUS_OUT)
{
/*
* STATUS PHASE completed, update ep0_state to idle
*/
pdev->ep0_state = USBD_EP0_IDLE;
USBD_LL_StallEP(pdev, 0U);
}
}
}
else if ((pdev->pClass->DataOut != NULL) &&
(pdev->dev_state == USBD_STATE_CONFIGURED))
{
pdev->pClass->DataOut(pdev, epnum);
}
else
{
/* should never be in this condition */
return USBD_FAIL;
}
return USBD_OK;
}
在此函数内完成数据输出DataOut,EP0_RxReady,端点设置等等
usb全局结构体`USBD_HandleTypeDef hUsbDeviceFS;`
/* 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[16];
USBD_EndpointTypeDef ep_out[16];
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;
这个结构体特别重要,我们可以注意到在初始化usb的时候其实都是配置的这个结构体,对于此结构体我们重点理解后面几个指针参数,最重要!
USBD_DescriptorsTypeDef *pDesc;
USBD_ClassTypeDef *pClass;
void *pClassData;
void *pUserData;
void *pData;
USBD_DescriptorsTypeDef *pDesc;
指向的是设备描述符, /* USB Device descriptors structure */
typedef struct
{
uint8_t *(*GetDeviceDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t *(*GetLangIDStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t *(*GetManufacturerStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t *(*GetProductStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t *(*GetSerialStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t *(*GetConfigurationStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t *(*GetInterfaceStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
#if (USBD_LPM_ENABLED == 1U)
uint8_t *(*GetBOSDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length);
#endif
} USBD_DescriptorsTypeDef;
在void MX_USB_DEVICE_Init(void)
内调用
if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK)
{
Error_Handler();
}
FS_Desc为定义的结构体
/** @defgroup USBD_DESC_Private_Variables USBD_DESC_Private_Variables
* @brief Private variables.
* @{
*/
USBD_DescriptorsTypeDef FS_Desc =
{
USBD_FS_DeviceDescriptor
, USBD_FS_LangIDStrDescriptor
, USBD_FS_ManufacturerStrDescriptor
, USBD_FS_ProductStrDescriptor
, USBD_FS_SerialStrDescriptor
, USBD_FS_ConfigStrDescriptor
, USBD_FS_InterfaceStrDescriptor
};
USBD_ClassTypeDef *pClass
指向一个类 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_DESC == 1U)
uint8_t *(*GetUsrStrDescriptor)(struct _USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length);
#endif
} USBD_ClassTypeDef;
对应的定义,下面是我们根据HID和CDC的修改的组合设备的类
USBD_ClassTypeDef USBD_COMPOSITE =
{
USBD_Composite_Init,
USBD_Composite_DeInit,
USBD_Composite_Setup,
NULL, /*EP0_TxSent*/
USBD_Composite_EP0_RxReady, //add
USBD_Composite_DataIn,
USBD_Composite_DataOut,
NULL,
NULL,
NULL,
NULL,
USBD_Composite_GetFSCfgDesc,
NULL,
USBD_Composite_GetDeviceQualifierDescriptor,
};
void *pClassData;
特别重要,存放了CDC或者HID的相关句柄,搭建组合设备的时候需要重点区分此指针 当是CDC的时候pClassData所指向的结构体
typedef struct
{
uint32_t data[CDC_DATA_HS_MAX_PACKET_SIZE / 4U]; /* Force 32bits alignment */
uint8_t CmdOpCode;
uint8_t CmdLength;
uint8_t *RxBuffer;
uint8_t *TxBuffer;
uint32_t RxLength;
uint32_t TxLength;
__IO uint32_t TxState;
__IO uint32_t RxState;
}
USBD_CDC_HandleTypeDef;
当是HID的时候指向的结构体
typedef struct
{
uint32_t Protocol;
uint32_t IdleState;
uint32_t AltSetting;
HID_StateTypeDef state;
}
USBD_HID_HandleTypeDef;
void *pUserData;
指向接口函数我们看下uint8_t USBD_CDC_RegisterInterface(USBD_HandleTypeDef *pdev, USBD_CDC_ItfTypeDef *fops)
函数内部就可以知道
/**
* @brief USBD_CDC_RegisterInterface
* @param pdev: device instance
* @param fops: CD Interface callback
* @retval status
*/
uint8_t USBD_CDC_RegisterInterface(USBD_HandleTypeDef *pdev,
USBD_CDC_ItfTypeDef *fops)
{
uint8_t ret = USBD_FAIL;
if (fops != NULL)
{
pdev->pUserData = fops;
ret = USBD_OK;
}
return ret;
}
void *pData;
指向hpcd_USB_OTG_FS
,hpcd_USB_OTG_FS
在中断函数的时候会作为参数传入USBD_LL_Init
函数内pdev->pData = &hpcd_USB_OTG_FS;
/**
* @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. */
if (pdev->id == DEVICE_FS) {
/* Link the driver to the stack. */
hpcd_USB_OTG_FS.pData = pdev;
pdev->pData = &hpcd_USB_OTG_FS;
//...略
}
pClassData和pUserData在USBD_HandleTypeDef中是指针形式,所以在调用不同类的时候,改变指针的指向,即可完成不同类的功能,此外复合设备配置修改之后修改pDesc和pClass指向即可。我们复合设备类的设计思想既是如此。
cubemx配置的工程默认优化等级都是2级优化,优化等级高了很容易出现离奇bug的,别问为什么,改低点就对了!
usbd_composite.c
和usbd_composite.h
文件usbd_composite.c
文件前面我们说过组合设备的核心,pClassData和pUserData在USBD_HandleTypeDef中是指针形式,所以在调用不同类的时候,改变指针的指向,即可完成不同类的功能,此外复合设备配置修改之后修改pDesc和pClass指向即可
根据上述思想,编写对应的usbd_composite.c
文件
#include "usbd_composite.h"
USBD_CDC_HandleTypeDef *pCDCData;
USBD_HID_HandleTypeDef *pHIDData;
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_Setup (USBD_HandleTypeDef *pdev,
USBD_SetupReqTypedef *req);
static uint8_t USBD_Composite_EP0_RxReady(USBD_HandleTypeDef *pdev);
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, //add
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 */
USBD_COMPOSITE_DESC_SIZE,
0x00,
USBD_MAX_NUM_INTERFACES , /* bNumInterfaces: */
0x01, /* bConfigurationValue: 0 配置的值 */
0x00, /* iConfiguration: 00 字符串索引 */
0x80, /* bmAttributes:no-bus powered and Dissupport Remote Wake-up*/
0x32, /* MaxPower 100 mA */
/****************************HID************************************/
/* Interface Association Descriptor */
USBD_IAD_DESC_SIZE, // bLength IAD描述符大小
USBD_IAD_DESCRIPTOR_TYPE, // bDescriptorType IAD描述符类型
0x00, // bFirstInterface 接口描述符是在总的配置描述符中的第几个从0开始数
0x01, // bInterfaceCount 接口描述符数量
0x03, // bFunctionClass 设备符中的bDeviceClass
0x00, // bFunctionSubClass 设备符中的bDeviceSubClass
0x00, // bInterfaceProtocol 设备符中的bDeviceProtocol
0x00,
/******************** HID interface ********************/
/************** Descriptor of Custom HID interface ****************/
/* 09 */
0x09, /*bLength: Interface Descriptor size*/
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
USBD_HID_INTERFACE, /*bInterfaceNumber: Number of Interface 接口编号 0 */
0x00, /*bAlternateSetting: Alternate setting 备用接口 */
0x01, /*bNumEndpoints 使用的端点数 1 */
0x03, /*bInterfaceClass: HID*/
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
0, /*iInterface: Index of string descriptor*/
/******************** Descriptor of Custom HID ********************/
/* 18 */
0x09, /*bLength: HID Descriptor size*/
HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
0x00, /*bcdHID: HID Class Spec release number*/
0x01,
0x00, /*bCountryCode: Hardware target country*/
0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/
0x22, /*bDescriptorType*/
HID_MOUSE_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
0x00,
/******************** Descriptor of TouchScreen endpoint ********************/
/* 27 */
0x07, /*bLength: Endpoint Descriptor size*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
HID_EPIN_SIZE, /*wMaxPacketSize: 16 Byte max */
0x00,
HID_FS_BINTERVAL, /*bInterval: Polling Interval */
/* 34 */
/****************************CDC************************************/
/* IAD描述符 */
/* Interface Association Descriptor */
USBD_IAD_DESC_SIZE, // bLength
USBD_IAD_DESCRIPTOR_TYPE, // bDescriptorType
0x01, // bFirstInterface 接口描述符是在总的配置描述符中的第几个从0开始数 1
0x02, // bInterfaceCount 接口描述符数量 2
0x02, // bFunctionClass CDC Control
0x02, // bFunctionSubClass Abstract Control Model
0x01, // bInterfaceProtocol AT Commands: V.250 etc
0x00, // iFunction
/* CDC命令接口描述符 */
/*Interface Descriptor */
0x09, /* bLength: Interface Descriptor size 长度 */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface 接口编号0x04 */
/* Interface descriptor type */
USBD_CDC_CMD_INTERFACE, /* bInterfaceNumber: Number of Interface 接口编号,第一个接口编号为1 */
0x00, /* bAlternateSetting: Alternate setting 接口备用编号 0 */
0x01, /* bNumEndpoints: One endpoints used 非0端点的数目 1 cdc接口只使用了一个中断输入端点 */
0x02, /* bInterfaceClass: Communication Interface Class 接口所使用的类0x02 */
0x02, /* bInterfaceSubClass: Abstract Control Model 接口所使用的子类0x02 */
0x01, /* bInterfaceProtocol: Common AT commands 使用AT命令协议 */
0x00, /* iInterface: 接口字符串索引值 0表示没有 */
/* 类特殊接口描述符--功能描述符 用来描述接口的功能 */
/*Header Functional Descriptor*/
0x05, /* bLength: Endpoint Descriptor size 描述符长度为5字节 */
0x24, /* bDescriptorType: CS_INTERFACE 描述符类型为类特殊接口CS_INTERFACE*/
0x00, /* bDescriptorSubtype: Header Func Desc 子类为 Header Func Desc,编号0x00 */
0x10, /* bcdCDC: spec release number CDC版本 */
0x01,
/*Call Management Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE 描述符类型为类特殊接口CS_INTERFACE*/
0x01, /* bDescriptorSubtype: Call Management Func Desc 子类为Call Management Func Desc 编号0x01*/
0x00, /* bmCapabilities: D0+D1 设备自己不管理call management */
0x01, /* bDataInterface: 1 有一个数据类接口用作call management */
/*ACM Functional Descriptor*/
0x04, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE 描述符类型为类特殊接口CS_INTERFACE*/
0x02, /* bDescriptorSubtype: Abstract Control Management desc 子类为Abstract Control Management desc编号0x02*/
0x02, /* bmCapabilities 支持Set_Control_Line_State、Get_Line_Coding请求和Serial_State通知*/
/*Union Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE 描述符类型为类特殊接口CS_INTERFACE */
0x06, /* bDescriptorSubtype: Union func desc 子类为Union func desc 编号0x06*/
USBD_CDC_CMD_INTERFACE, /* bMasterInterface: Communication class interface 编号为1的CDC接口 */
USBD_CDC_DATA_INTERFACE, /* bSlaveInterface0: Data Class Interface 编号为2的数据类接口 */
/*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),
CDC_FS_BINTERVAL, /* bInterval: */
/*---------------------------------------------------------------------------*/
/* 数据类接口的接口描述符 */
/*Data class interface descriptor*/
0x09, /* bLength: Endpoint Descriptor size 接口描述符长度9字节*/
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: 接口描述符的编号0x04*/
USBD_CDC_DATA_INTERFACE, /* bInterfaceNumber: Number of Interface 接口的编号为2*/
0x00, /* bAlternateSetting: Alternate setting 该接口的备用编号为0 */
0x02, /* bNumEndpoints: Two endpoints used 非0端点的数据 设备需要使用一对批量端点,设置为2*/
0x0A, /* bInterfaceClass: CDC 该接口所使用的类 数据类接口代码为0x0A */
0x00, /* bInterfaceSubClass: 接口所使用的子类为0*/
0x00, /* bInterfaceProtocol: 接口所使用的协议为0*/
0x00, /* iInterface: 接口的字符串索引值,0表示没有*/
/* 输出端点的端点描述符 */
/*Endpoint OUT Descriptor*/
0x07, /* bLength: Endpoint Descriptor size 端点描述符长度7字节 */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint 端点描述符编号为0x05 */
CDC_OUT_EP, /* bEndpointAddress 端点的地址0x02 D7为方向*/
0x02, /* bmAttributes: Bulk 批量传输*/
LOBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), /* wMaxPacketSize: 端点的最大包长 512字节*/
HIBYTE(CDC_DATA_HS_MAX_PACKET_SIZE),
0x00, /* bInterval: ignore for Bulk transfer 端点查询时间,对批量端点无效 */
/* 输入端点的端点描述符 */
/*Endpoint IN Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint 端点描述符编号为0x05*/
CDC_IN_EP, /* bEndpointAddress 端点的地址0x82 D7为方向*/
0x02, /* bmAttributes: Bulk 批量传输*/
LOBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), /* wMaxPacketSize: 端点的最大包长 512字节*/
HIBYTE(CDC_DATA_HS_MAX_PACKET_SIZE),
0x00 /* bInterval: ignore for Bulk transfer 端点查询时间,对批量端点无效*/
};
/* USB 设备限定符描述符 */
/* 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,
};
static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev,
uint8_t cfgidx)
{
uint8_t res = 0;
pdev->pUserData = (void*)&USBD_CDC_Interface_fops_FS;
res += USBD_CDC.Init(pdev,cfgidx);
pCDCData = pdev->pClassData;
/* TODO */
pdev->pUserData = NULL;
res += USBD_HID.Init(pdev,cfgidx);
pHIDData = pdev->pClassData;
return res;
}
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 = pHIDData;
/* TODO */
pdev->pUserData = NULL;
res += USBD_HID.DeInit(pdev,cfgidx);
return res;
}
static uint8_t USBD_Composite_EP0_RxReady(USBD_HandleTypeDef *pdev)
{
pdev->pClassData = pCDCData;
pdev->pUserData = &USBD_CDC_Interface_fops_FS;
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_HID_INTERFACE:
pdev->pClassData = pHIDData;
/* TODO */
pdev->pUserData = NULL;
return(USBD_HID.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 HID_EPIN_ADDR:
// case HID_EPOUT_ADDR:
pdev->pClassData = pHIDData;
/* TODO */
pdev->pUserData = NULL;
return(USBD_HID.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
*/
static uint8_t USBD_Composite_DataIn (USBD_HandleTypeDef *pdev,
uint8_t epnum)
{
switch(epnum)
{
case CDC_INDATA_NUM:
pdev->pUserData = &USBD_CDC_Interface_fops_FS;
pdev->pClassData = pCDCData;
return(USBD_CDC.DataIn(pdev,epnum));
case HID_INDATA_NUM:
/* TODO */
pdev->pUserData = NULL;
pdev->pClassData = pHIDData;
return(USBD_HID.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));
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;
}
编写对应的usbd_composite.h
文件
#ifndef __USBD_COMPOSITE_H_
#define __USBD_COMPOSITE_H_
#include "usbd_hid.h"
#include "usbd_cdc.h"
#include "usbd_cdc_if.h"
#define USBD_COMPOSITE_DESC_SIZE (108)
#define USBD_IAD_DESC_SIZE 0x08
#define USBD_IAD_DESCRIPTOR_TYPE 0x0B
#define USBD_HID_INTERFACE 0 //HID接口索引值
#define USBD_CDC_CMD_INTERFACE 1 //CDC CMD接口索引值
#define USBD_CDC_DATA_INTERFACE 2 //CDC Data接口索引值
#define HID_INDATA_NUM (HID_EPIN_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)
extern USBD_CDC_HandleTypeDef *pCDCData;
extern USBD_HID_HandleTypeDef *pHIDData;
extern USBD_ClassTypeDef USBD_COMPOSITE;
#endif
/** USB standard device descriptor. */
__ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
{
#if 1
0x12, /*bLength */
USB_DESC_TYPE_DEVICE, /*bDescriptorType 描述符编号0x01 */
0x00, /*bcdUSB 版本2.0 */
0x02,
0xEF, /*bDeviceClass 综合设备 */
0x02, /*bDeviceSubClass*/
0x01, /*bDeviceProtocol*/
USB_MAX_EP0_SIZE, /*bMaxPacketSize 端点0大小为64字节 */
LOBYTE(USBD_VID), /*idVendor 厂家ID */
HIBYTE(USBD_VID), /*idVendor*/
LOBYTE(USBD_PID_FS), /*idProduct 产品ID */
HIBYTE(USBD_PID_FS), /*idProduct*/
0x00, /*bcdDevice rel. 2.00版本*/
0x02,
USBD_IDX_MFC_STR, /*Index of manufacturer string 厂商字符串索引值 0x01 */
USBD_IDX_PRODUCT_STR, /*Index of product string 产品字符串索引值 0x02 */
USBD_IDX_SERIAL_STR, /*Index of serial number string 设备序列号字符串索引值 0x03 */
USBD_MAX_NUM_CONFIGURATION /*bNumConfigurations 该设备所具有的配置数 0x01 */
#elif 1
0x12, /*bLength */
USB_DESC_TYPE_DEVICE, /*bDescriptorType*/
0x00, /*bcdUSB */
0x02,
0x02, /*bDeviceClass*/
0x02, /*bDeviceSubClass*/
0x00, /*bDeviceProtocol*/
USB_MAX_EP0_SIZE, /*bMaxPacketSize*/
LOBYTE(USBD_VID), /*idVendor*/
HIBYTE(USBD_VID), /*idVendor*/
LOBYTE(USBD_PID_FS), /*idProduct*/
HIBYTE(USBD_PID_FS), /*idProduct*/
0x00, /*bcdDevice rel. 2.00*/
0x02,
USBD_IDX_MFC_STR, /*Index of manufacturer string*/
USBD_IDX_PRODUCT_STR, /*Index of product string*/
USBD_IDX_SERIAL_STR, /*Index of serial number string*/
USBD_MAX_NUM_CONFIGURATION /*bNumConfigurations*/
#endif
};
/**
* Init USB device Library, add supported class and start the library
* @retval None
*/
void MX_USB_DEVICE_Init(void)
{
/* USER CODE BEGIN USB_DEVICE_Init_PreTreatment */
/* USER CODE END USB_DEVICE_Init_PreTreatment */
/* Init Device Library, add supported class and start the library. */
if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK)
{
Error_Handler();
}
if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_COMPOSITE) != USBD_OK)
{
Error_Handler();
}
// if (USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_CDC_Interface_fops_FS) != USBD_OK)
// {
// Error_Handler();
// }
if (USBD_Start(&hUsbDeviceFS) != USBD_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USB_DEVICE_Init_PostTreatment */
/* USER CODE END USB_DEVICE_Init_PostTreatment */
}
修改端点的时候注意一定不要冲突,其次,端点的配置也在设备描述符类体现,需要大家注意
usbd_cdc.h
文件内
#define CDC_IN_EP 0x81U /* EP1 for data IN */
#define CDC_OUT_EP 0x01U /* EP1 for data OUT */
#define CDC_CMD_EP 0x82U /* EP2 for CDC commands */
usbd_hid
文件内
#define HID_EPIN_ADDR 0x83U
#define HID_EPIN_SIZE 0x04U
修改usbd_conf.h
文件内配置
/*---------- -----------*/
#define USBD_MAX_NUM_INTERFACES 3
/*---------- -----------*/
#define USBD_MAX_NUM_CONFIGURATION 1
/*---------- -----------*/
#define USBD_MAX_STR_DESC_SIZ 512
/*---------- -----------*/
#define USBD_DEBUG_LEVEL 0
/*---------- -----------*/
#define USBD_SELF_POWERED 1
/*---------- -----------*/
#define MAX_STATIC_ALLOC_SIZE 512
/****************************************/
/* #define for FS and HS identification */
#define DEVICE_FS 0
在usbd_composite.c文件内的USBD_Composite_CfgFSDesc[]
描述符中
pdev->pClassData
函数在初始化的时候都是需要申请内存的,函数内部调用的USBD_malloc
申请,但是这里有个很大的坑,默认函数内部是定义了一个静态数组,然后将数组指针返回,但是在定义数组的时候特别坑,默认是下面这样子
void *USBD_static_malloc(uint32_t size)
{
static uint32_t mem[(sizeof(USBD_CDC_HandleTypeDef)/4)+1];/* On 32-bit boundary */
return mem;
}
如果加入HID的,那申请的内存大小也会是(sizeof(USBD_CDC_HandleTypeDef)/4)+1大小!!!
因此进行修改,分别定义
/**
* @brief Static single allocation.
* @param size: Size of allocated memory
* @retval None
*/
void *USBD_static_CDC_malloc(uint32_t size)
{
static uint32_t mem[(sizeof(USBD_CDC_HandleTypeDef)/4)+1];/* On 32-bit boundary */
return mem;
}
/**
* @brief Static single allocation.
* @param size: Size of allocated memory
* @retval None
*/
void *USBD_static_HID_malloc(uint32_t size)
{
static uint32_t mem[(sizeof(USBD_HID_HandleTypeDef)/4)+1];/* On 32-bit boundary */
return mem;
}
或者修改宏,使用malloc动态申请内存
之前的单独的CDC或者HID调用的时候直接使用pdev->pClassData;就可以了,但是组合设备不一样,因为pdev->pClassData是在不断切换的,你不知道此时pdev->pClassData指向的是CDC还是HID,所以在调用对应的发送函数的时候必须修改。
CDC_Transmit_FS()
/**
* @brief CDC_Transmit_FS
* Data to send over USB IN endpoint are sent over CDC interface
* through this function.
* @note
*
*
* @param Buf: Buffer of data to be sent
* @param Len: Number of data to be sent (in bytes)
* @retval USBD_OK if all operations are OK else USBD_FAIL or USBD_BUSY
*/
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
{
uint8_t result = USBD_OK;
/* USER CODE BEGIN 7 */
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)pCDCData;//(USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;
if (hcdc->TxState != 0){
return USBD_BUSY;
}
hUsbDeviceFS.pClassData=pCDCData;
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len);
result = USBD_CDC_TransmitPacket(&hUsbDeviceFS);
/* USER CODE END 7 */
return result;
}
这里内部调用的USBD_CDC_SetTxBuffer()
和USBD_CDC_SetRxBuffer()
指向不能随便修改,在补充说明里面详细解释为什么没有改,USBD_CDC_TransmitPacket()
内部hcdc
指向也没有修改,因为USBD_CDC_TransmitPacket()
只有CDC_Transmit_FS()
调用过,而我们修改了CDC_Transmit_FS()
内部
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)pCDCData;//(USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;
因此pdev->pClassData
指向已经修改过来了,所以没必要改了
uint8_t USBD_CDC_SetTxBuffer(USBD_HandleTypeDef *pdev,
uint8_t *pbuff,
uint16_t length)
{
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *) pdev->pClassData;
hcdc->TxBuffer = pbuff;
hcdc->TxLength = length;
return USBD_OK;
}
uint8_t USBD_CDC_SetRxBuffer(USBD_HandleTypeDef *pdev,
uint8_t *pbuff)
{
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *) pdev->pClassData;
hcdc->RxBuffer = pbuff;
return USBD_OK;
}
uint8_t USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev)
{
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *) pdev->pClassData;
if (pdev->pClassData != NULL)
{
if (hcdc->TxState == 0U)
{
/* Tx Transfer in progress */
hcdc->TxState = 1U;
/* Update the packet total length */
pdev->ep_in[CDC_IN_EP & 0xFU].total_length = hcdc->TxLength;
/* Transmit next packet */
USBD_LL_Transmit(pdev, CDC_IN_EP, hcdc->TxBuffer,
(uint16_t)hcdc->TxLength);
return USBD_OK;
}
else
{
return USBD_BUSY;
}
}
else
{
return USBD_FAIL;
}
}
注意修改的时候一定不要只改了上层,要看下函数内部调用的函数
USBD_HID_SendReport
函数uint8_t USBD_HID_SendReport(USBD_HandleTypeDef *pdev,
uint8_t *report,
uint16_t len)
{
USBD_HID_HandleTypeDef *hhid = (USBD_HID_HandleTypeDef*)pHIDData;//(USBD_HID_HandleTypeDef *)pdev->pClassData;
if (pdev->dev_state == USBD_STATE_CONFIGURED)
{
if (hhid->state == HID_IDLE)
{
hhid->state = HID_BUSY;
USBD_LL_Transmit(pdev,
HID_EPIN_ADDR,
report,
len);
}
}
return USBD_OK;
}
USBD_StatusTypeDef USBD_LL_Transmit(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint16_t size)
{
HAL_StatusTypeDef hal_status = HAL_OK;
USBD_StatusTypeDef usb_status = USBD_OK;
hal_status = HAL_PCD_EP_Transmit(pdev->pData, ep_addr, pbuf, size);
usb_status = USBD_Get_USB_Status(hal_status);
return usb_status;
}
增加端点肯定需要硬件的支持,修改USBD_LL_Init
函数
首先是hpcd_USB_OTG_FS.Init.dev_endpoints = 8;
然后是HAL_PCDEx_SetTxFiFo()
配置端点
但是HAL_PCDEx_SetTxFiFo()
配置的时候很坑,配置的大小要求特别严格,此函数第二个参数表示的是端点,第三个参数表示的是大小,默认生成的是这样子(设置这个最好清楚USB的FIFO构造,在14点有描述)
HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_FS, 0x80);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 0x40);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x80);
那么我需要增加端点设置的话就必须要减少之前的大小,否则usblyzer抓包会看到内部复位错误
HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_FS, 0x80);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 0x40);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x20);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 2, 0x20);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 3, 0x40);
USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
{
/* Init USB Ip. */
if (pdev->id == DEVICE_FS) {
/* Link the driver to the stack. */
hpcd_USB_OTG_FS.pData = pdev;
pdev->pData = &hpcd_USB_OTG_FS;
hpcd_USB_OTG_FS.Instance = USB_OTG_FS;
hpcd_USB_OTG_FS.Init.dev_endpoints = 8;
hpcd_USB_OTG_FS.Init.speed = PCD_SPEED_FULL;
hpcd_USB_OTG_FS.Init.Sof_enable = DISABLE;
hpcd_USB_OTG_FS.Init.low_power_enable = DISABLE;
hpcd_USB_OTG_FS.Init.vbus_sensing_enable = DISABLE;
if (HAL_PCD_Init(&hpcd_USB_OTG_FS) != HAL_OK)
{
Error_Handler( );
}
#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U)
/* Register USB PCD CallBacks */
HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SOF_CB_ID, PCD_SOFCallback);
HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SETUPSTAGE_CB_ID, PCD_SetupStageCallback);
HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_RESET_CB_ID, PCD_ResetCallback);
HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SUSPEND_CB_ID, PCD_SuspendCallback);
HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_RESUME_CB_ID, PCD_ResumeCallback);
HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_CONNECT_CB_ID, PCD_ConnectCallback);
HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_DISCONNECT_CB_ID, PCD_DisconnectCallback);
HAL_PCD_RegisterDataOutStageCallback(&hpcd_USB_OTG_FS, PCD_DataOutStageCallback);
HAL_PCD_RegisterDataInStageCallback(&hpcd_USB_OTG_FS, PCD_DataInStageCallback);
HAL_PCD_RegisterIsoOutIncpltCallback(&hpcd_USB_OTG_FS, PCD_ISOOUTIncompleteCallback);
HAL_PCD_RegisterIsoInIncpltCallback(&hpcd_USB_OTG_FS, PCD_ISOINIncompleteCallback);
#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */
HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_FS, 0x80);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 0x40);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x20);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 2, 0x20);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 3, 0x40);
// HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 4, 0x20);
// HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 5, 0x20);
}
return USBD_OK;
}
互联型产品USB为USB OTG,其实这部分可以详细看下手册这章的关于FIFO的说明,手册上讲的很清楚,下面是我对FIFO的总结
从上图我们可以知道FIFO是如何设置大小的,但是需要特别注意的是设置FIFO深度的单位,是32位的字
然后我们回到上一点(13.修改硬件层配置)所描述的修改
HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_FS, 0x80);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 0x40);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x20);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 2, 0x20);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 3, 0x40);
现在来解释下为什么设置fifo的时候我们改大了就会报错
我们来计算一下现在的FIFO大小
0x80+0x40+0x20+0x20+0x40=0x140=320
又由于单位是32位的字,所以设置的整个FIFO大小位320*4=1280字节
1280/1024=1.25k刚好是支持的最大FIFO大小
这就是我们之前设置FIFO的时候不能随便乱加的原因了!
USBD_CDC_SetTxBuffer()
和USBD_CDC_SetRxBuffer()
函数内部的USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *) pdev->pClassData;
USBD_CDC_HandleTypeDef *hcdc = pCDCData;
USBD_Composite_Init()
函数中,static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev,
uint8_t cfgidx)
{
uint8_t res = 0;
pdev->pUserData = (void*)&USBD_CDC_Interface_fops_FS;
res += USBD_CDC.Init(pdev,cfgidx);
pCDCData = pdev->pClassData;
/* TODO */
pdev->pUserData = NULL;
res += USBD_HID.Init(pdev,cfgidx);
pHIDData = pdev->pClassData;
return res;
}
该函数调用 res += USBD_CDC.Init(pdev,cfgidx);
进行cdc的初始化,此时pCDCData
还是一个空指针,
只有在res += USBD_CDC.Init(pdev,cfgidx);
执行完成之后,才会执行pCDCData = pdev->pClassData;
将pCDCData
指针赋值。
USBD_CDC.Init(pdev,cfgidx)
也就是USBD_CDC_Init()
函数,初始化CDC,并在这里面给pdev->pClassData申请内存,
pdev->pClassData=USBD_CDC_malloc(sizeof(USBD_CDC_HandleTypeDef));
之后进入usbd_cdc_if.c文件内的
/* Init physical Interface components */
((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init();
也就是
static int8_t CDC_Init_FS(void)
{
/* USER CODE BEGIN 3 */
/* Set Application Buffers */
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, 0);
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS);
return (USBD_OK);
/* USER CODE END 3 */
}
这里面调用USBD_CDC_SetTxBuffer()
和USBD_CDC_SetRxBuffer()
,
uint8_t USBD_CDC_SetTxBuffer(USBD_HandleTypeDef *pdev,
uint8_t *pbuff,
uint16_t length)
{
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *) pdev->pClassData;
hcdc->TxBuffer = pbuff;
hcdc->TxLength = length;
return USBD_OK;
}
uint8_t USBD_CDC_SetRxBuffer(USBD_HandleTypeDef *pdev,
uint8_t *pbuff)
{
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *) pdev->pClassData;
hcdc->RxBuffer = pbuff;
return USBD_OK;
}
如果我们之前简单的修改*hcdc = (USBD_CDC_HandleTypeDef *) pCDCData;
那么此时pCDCData
还是一个NULL的空指针,对空指针操作会直接进入硬件错误中断,因此我们不能这么修改!!!
至此,USB组合设备已经配置完成了,搞了个把月了,终于算是看到希望了,但是还有很多需要完善的地方,关于usb,总体框架还是很重要,大家一定要沉下去,仔细去理解代码,借助debug看看程序怎么运行的,然后结合我给大家的分享去拿下他,一定要有信心,征服它!
完整工程附上USB HID+CDC组合设备,觉得文章可以的话,各位大佬可以打赏支持下哦!