复合设备是啥,通俗讲就是一个USB物理设备可以实现多个功能,在主机端可以看到多个设备。通常实现HID复合设备有两种方式。第一种是使用同一个接口,修改报告描述符,增加一个功能集合,同时需要使用报告ID来区分哪一个设备,这样主机端和设备端需要增加报告ID处理,但只需要两个端点来实现功能,对于端口资源较少的MCU可以使用;第二种是使用两个接口,每个接口对应不同的报告描述符,不需要特意使用报告ID来区分,但不同接口使用独立的端点。
本文章使用不同接口来实现,基于stm32 cube例程开发。
开发准备
使用官方STM32Cube_FW_F1_V1.6.0,使用STM32F103XF,因此使用
STM32Cube_FW_F1_V1.6.0\Projects\STM3210E_EVAL\Applications\USB_Device\CustomHID_Standalone
这个工程
移植代码
2.1 修改控制控制USBDP上拉电阻引脚,(内部无法直接控制上下拉,因此增加此电路,否则主机不会枚举设备)
2.2 增加串口1输出调试信息,main函数里添加:
/* uart configuration */
MX_USART1_UART_Init();
添加如下代码实现printf功能
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
2.3 修改usb注册函数,main函数里修改如下代码
USBD_Init(&USBD_Device, &HID_Desc, 0);
/* Add Supported Class /
USBD_RegisterClass(&USBD_Device, &USBD_COMPOSITE);
/ Start Device Process */
USBD_Start(&USBD_Device);
2.4 修改desc
在usbd_desc.c,定义自己的desc
#define USBD_VID 0x0483
#define USBD_PID 0x5750
#define USBD_LANGID_STRING 0x409
#define USBD_MANUFACTURER_STRING “man”
#define USBD_PRODUCT_FS_STRING “HID in FS Mode”
#define USBD_CONFIGURATION_FS_STRING “HID Config”
#define USBD_INTERFACE_FS_STRING “HID Interface”
2.5 添加USBD_COMPOSITE
增加如下文件
usbd_composite.c,usbd_composite.h,usbd_customhid_if.c
usbd_customhid_if.h,usbd_keyboard.c,usbd_keyboard.h,usbd_keyboard_if.c,usbd_keyboard_if.h
usbd_composite.c具体内容如下,关键修改为USBD_Composite_CfgFSDesc。
/**
* @file usbd_composite.c
* @author
* @version
* @date
* @brief KB + HID 复合设备
* @note
* @attention
*/
#include "usbd_composite.h"
#include "usbd_customhid.h"
#include "usbd_customhid_if.h"
#include "usbd_keyboard.h"
#include "usbd_keyboard_hid_if.h"
static USBD_CUSTOM_HID_HandleTypeDef *pHIDData;
static USBD_CUSTOM_HID_HandleTypeDef *pKBData;
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: Configuration Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
USBD_COMPOSITE_DESC_SIZE,
/* wTotalLength: Bytes returned */
0x00,
0x02, /*bNumInterfaces: 2 interface*/
0x01, /*bConfigurationValue: Configuration value*/
0x00, /*iConfiguration: Index of string descriptor describing
the configuration*/
0xC0, /*bmAttributes: bus powered */
0x32, /*MaxPower 100 mA: this current is used for detecting Vbus*/
/************** Descriptor of CUSTOM HID interface ****************/
/* 09 */
0x09, /*bLength: Interface Descriptor size*/
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
0x00, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x02, /*bNumEndpoints*/
0x03, /*bInterfaceClass: CUSTOM_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: CUSTOM_HID Descriptor size*/
CUSTOM_HID_DESCRIPTOR_TYPE, /*bDescriptorType: CUSTOM_HID*/
0x11, /*bCUSTOM_HIDUSTOM_HID: CUSTOM_HID Class Spec release number*/
0x01,
0x00, /*bCountryCode: Hardware target country*/
0x01, /*bNumDescriptors: Number of CUSTOM_HID class descriptors to follow*/
0x22, /*bDescriptorType*/
USBD_CUSTOM_HID_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
0x00,
/******************** Descriptor of Custom HID endpoints ********************/
/* 27 */
0x07, /*bLength: Endpoint Descriptor size*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
CUSTOM_HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
CUSTOM_HID_EPIN_SIZE, /*wMaxPacketSize: 64 Byte max */
0x00,
0x10, /*bInterval: Polling Interval (20 ms)*/
/* 34 */
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: */
CUSTOM_HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (OUT)*/
0x03, /* bmAttributes: Interrupt endpoint */
CUSTOM_HID_EPOUT_SIZE, /* wMaxPacketSize: 64 Bytes max */
0x00,
0x10, /* bInterval: Polling Interval (20 ms) */
/************** Descriptor of Keyboard interface ****************/
/* 41 */
0x09, /*bLength: Interface Descriptor size*/
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
0x01, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x02, /*bNumEndpoints*/
0x03, /*bInterfaceClass: Keyboard*/
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
0, /*iInterface: Index of string descriptor*/
/******************** Descriptor of Keyboard *************************/
/* 50 */
0x09, /*bLength: Keyboard Descriptor size*/
CUSTOM_HID_DESCRIPTOR_TYPE, /*bDescriptorType: Keyboard*/
0x11, /*bCUSTOM_HIDUSTOM_HID: Keyboard Class Spec release number*/
0x01,
0x00, /*bCountryCode: Hardware target country*/
0x01, /*bNumDescriptors: Number of CUSTOM_HID class descriptors to follow*/
0x22, /*bDescriptorType*/
USBD_KEYBOARD_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
0x00,
/******************** Descriptor of Keyboard endpoints ********************/
/* 59 */
0x07, /*bLength: Endpoint Descriptor size*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
KEYBOARD_HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
KEYBOARD_HID_EPIN_SIZE, /*wMaxPacketSize: 2 Byte max */
0x00,
0x10, /*bInterval: Polling Interval (20 ms)*/
/* 66 */
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: */
KEYBOARD_HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (OUT)*/
0x03, /* bmAttributes: Interrupt endpoint */
CUSTOM_HID_EPOUT_SIZE, /* wMaxPacketSize: 64 Bytes max */
0x00,
0x10, /* bInterval: Polling Interval (20 ms) */
/* 73 */
};
/* 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_CustomHID_fops;
res += USBD_CUSTOM_HID.Init(pdev,cfgidx);
pHIDData = pdev->pClassData;
pdev->pUserData = &USBD_KEYBOARD_fops_FS;
res += USBD_KEYBOARD.Init(pdev,cfgidx);
pKBData = 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 = pHIDData;
pdev->pUserData = &USBD_CustomHID_fops;
res += USBD_CUSTOM_HID.DeInit(pdev,cfgidx);
pdev->pClassData = pKBData;
pdev->pUserData = &USBD_KEYBOARD_fops_FS;
res += USBD_KEYBOARD.DeInit(pdev,cfgidx);
return res;
}
static uint8_t USBD_Composite_EP0_RxReady(USBD_HandleTypeDef *pdev)
{
return USBD_CUSTOM_HID.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_HID_DATA_INTERFACE:
pdev->pClassData = pHIDData;
pdev->pUserData = &USBD_CustomHID_fops;
return(USBD_CUSTOM_HID.Setup(pdev, req));
case USBD_KB_INTERFACE:
pdev->pClassData = pKBData;
pdev->pUserData = &USBD_KEYBOARD_fops_FS;
return(USBD_KEYBOARD.Setup (pdev, req));
default:
break;
}
break;
case USB_REQ_RECIPIENT_ENDPOINT:
switch(req->wIndex)
{
case CUSTOM_HID_EPIN_ADDR:
case CUSTOM_HID_EPOUT_ADDR:
pdev->pClassData = pHIDData;
pdev->pUserData = &USBD_CustomHID_fops;
return(USBD_CUSTOM_HID.Setup(pdev, req));
case KEYBOARD_HID_EPIN_ADDR:
case KEYBOARD_HID_EPOUT_ADDR:
pdev->pClassData = pKBData;
pdev->pUserData = &USBD_KEYBOARD_fops_FS;
return(USBD_KEYBOARD.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 HID_INDATA_NUM:
pdev->pClassData = pHIDData;
pdev->pUserData = &USBD_CustomHID_fops;
return(USBD_CUSTOM_HID.DataIn(pdev,epnum));
case KB_INDATA_NUM:
pdev->pClassData = pKBData;
pdev->pUserData = &USBD_KEYBOARD_fops_FS;
return(USBD_KEYBOARD.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 HID_OUTDATA_NUM:
pdev->pClassData = pHIDData;
pdev->pUserData = &USBD_CustomHID_fops;
return(USBD_CUSTOM_HID.DataOut(pdev,epnum));
case KB_OUTDATA_NUM:
pdev->pClassData = pKBData;
pdev->pUserData = &USBD_KEYBOARD_fops_FS;
return(USBD_KEYBOARD.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****/
usbd_customhid_if.c内容如下,主要修改CustomHID_ReportDesc,CustomHID_OutEvent,前者为报告描述符,后者为接受数据处理函数。
/**
******************************************************************************
* @file USB_Device/CustomHID_Standalone/Src/usbd_customhid_if.c
* @author MCD Application Team
* @version V1.6.0
* @date 12-May-2017
* @brief USB Device Custom HID interface file.
******************************************************************************
* @attention
*
* © Copyright � 2016 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------ */
#include "usbd_customhid_if.h"
/* Private typedef ----------------------------------------------------------- */
/* Private define ------------------------------------------------------------ */
/* Private macro ------------------------------------------------------------- */
/* Private function prototypes ----------------------------------------------- */
static int8_t CustomHID_Init(void);
static int8_t CustomHID_DeInit(void);
static int8_t CustomHID_OutEvent(uint8_t * data);
/* Private variables --------------------------------------------------------- */
ADC_HandleTypeDef AdcHandle;
uint32_t ADCConvertedValue = 0;
uint32_t ADC_Prev_ConvertedValue = 0;
uint8_t SendBuffer[2];
extern USBD_HandleTypeDef USBD_Device;
__ALIGN_BEGIN static uint8_t
CustomHID_ReportDesc[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END = {
/* USER CODE BEGIN 0 */
0x06,0xA0,0xFF, //用法页(FFA0h, vendor defined)
0x09, 0x01, //用法(vendor defined)
0xA1, 0x01, //集合(Application)
0x09, 0x02 , //用法(vendor defined)
0xA1, 0x00, //集合(Physical)
0x06,0xA1,0xFF, //用法页(vendor defined)
//输入报告
//0x85,0x01,
0x09, 0x03 , //用法(vendor defined)
0x09, 0x04, //用法(vendor defined)
0x15, 0x80, //逻辑最小值(0x80 or -128)
0x25, 0x7F, //逻辑最大值(0x7F or 127)
0x35, 0x00, //物理最小值(0)
0x45, 0xFF, //物理最大值(255)
0x75, 0x08, //报告长度Report size (8位)
0x95, 64, //报告数值(64 fields)
0x81, 0x02, //输入(data, variable, absolute)
//输出报告
// 0x85, 0x02,
0x09, 0x05, //用法(vendor defined)
0x09, 0x06, //用法(vendor defined)
0x15, 0x80, //逻辑最小值(0x80 or -128)
0x25, 0x7F, //逻辑最大值(0x7F or 127)
0x35, 0x00, //物理最小值(0)
0x45, 0xFF, //物理最大值(255)
0x75, 0x08, //报告长度(8位)
0x95, 64, //报告数值(64 fields)
0x91, 0x02, //输出(data, variable, absolute)
0xC0, //集合结束(Physical)
0xC0, //集合结束(Physical)
};
USBD_CUSTOM_HID_ItfTypeDef USBD_CustomHID_fops = {
CustomHID_ReportDesc,
CustomHID_Init,
CustomHID_DeInit,
CustomHID_OutEvent,
};
/* Private functions --------------------------------------------------------- */
/**
* @brief CustomHID_Init
* Initializes the CUSTOM HID media low layer
* @param None
* @retval Result of the opeartion: USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t CustomHID_Init(void)
{
// GPIO_InitTypeDef GPIO_InitStructure;
// ADC_ChannelConfTypeDef sConfig;
/* Configure the ADC peripheral */
#if 0
AdcHandle.Instance = ADCx;
__HAL_RCC_ADC1_CLK_ENABLE();
AdcHandle.Init.ScanConvMode = DISABLE;
AdcHandle.Init.ContinuousConvMode = ENABLE;
AdcHandle.Init.DiscontinuousConvMode = DISABLE;
AdcHandle.Init.NbrOfDiscConversion = 0;
AdcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
AdcHandle.Init.NbrOfConversion = 1;
HAL_ADC_Init(&AdcHandle);
/* Configure ADC regular channel */
sConfig.Channel = ADCx_CHANNEL;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;
HAL_ADC_ConfigChannel(&AdcHandle, &sConfig);
/* Start the conversion process and enable interrupt */
HAL_ADC_Start_DMA(&AdcHandle, (uint32_t *) & ADCConvertedValue, 1);
/* Configure LED1, LED2, LED3 and LED4 */
BSP_LED_Init(LED1);
BSP_LED_Init(LED2);
BSP_LED_Init(LED3);
BSP_LED_Init(LED4);
/* Enable GPIOG clock */
__HAL_RCC_GPIOG_CLK_ENABLE();
/* Configure PG8 pin as input floating for key Button */
GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING_FALLING;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Pin = GPIO_PIN_8;
HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
/* Enable and set EXTI2_TSC Interrupt to the lowest priority */
HAL_NVIC_SetPriority(EXTI9_5_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
#endif
return (0);
}
/**
* @brief CustomHID_DeInit
* DeInitializes the CUSTOM HID media low layer
* @param None
* @retval Result of the opeartion: USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t CustomHID_DeInit(void)
{
/*
* Add your deinitialization code here */
return (0);
}
/**
* @brief CustomHID_OutEvent
* Manage the CUSTOM HID class Out Event
* @param event_idx: LED Report Number
* @param state: LED states (ON/OFF)
*/
static int8_t CustomHID_OutEvent(uint8_t * data)
{
printf("CustomHID_OutEvent:\r\n");
for(int i=0;i