目的:完成一个HID + MIDI的组合设备
准备工作:
·用CUBE生成HID工程;
·复制一份工程修改为MIDI工程;(参考之前博客已完成这两个工程)
·新建USB_User文件夹,将USB相关配置文件放到这里,并新建usbd_composite.c,usbd_composite.h文件;
·修改:
·分配端点号
#define HID_EPIN_ADDR 0x81
#define HID_EPIN_SIZE 0x40
#define HID_EPOUT_ADDR 0x01
#define HID_EPOUT_SIZE 0x40
#define MIDI_EPIN_ADDR 0x82
#define MIDI_EPOUT_ADDR 0x02
#define MIDI_EPIN_SIZE 0x40
#define MIDI_EPOUT_SIZE 0x40
·添加usbd_composite.c,usbd_composite.h文件内容:
<
#include "usbd_composite.h"
#include "usbd_hid_if.h"
#include "usbd_midi_if.h"
static USBD_HID_HandleTypeDef *pHIDData;
static USBD_MIDI_HandleTypeDef *pMIDIData;
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, /*EP0_RxReady*/
USBD_Composite_DataIn,
USBD_Composite_DataOut,
NULL,
NULL,
NULL,
USBD_Composite_GetFSCfgDesc,
USBD_Composite_GetFSCfgDesc,
USBD_Composite_GetFSCfgDesc,
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 =
{
/*Configuration Descriptor*/
0x09, /* bLength: Configuration Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
USBD_COMPOSITE_DESC_SIZE,
0x00,
USBD_MAX_NUM_INTERFACES, /* bNumInterfaces: 2 interface */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
0xC0, /* bmAttributes: self powered */
0x00, /* MaxPower 0 mA */
/*--HID 1-------------------------------------------------------------------------*/
/* 9 */
/*******************接口描述符*********************/
//bLength字段。接口描述符的长度为9字节。
0x09,
//bDescriptorType字段。接口描述符的编号为0x04。
0x04,
//bInterfaceNumber字段。该接口的编号,第一个接口,编号为0。
USB_INTERFACE_HID_INDEX,
//bAlternateSetting字段。该接口的备用编号,为0。
0x00,
//bNumEndpoints字段。非0端点的数目。本实例需要二个
//中断端点(一个输入一个输出),因此该值为2。
0x02,
//bInterfaceClass字段。该接口所使用的类。本实例是HID类,
//HID类的编码为0x03。
0x03,
//bInterfaceSubClass字段。该接口所使用的子类。在HID1.1协议中,
//只规定了一种子类:支持BIOS引导启动的子类。
//USB键盘、鼠标属于该子类,子类代码为0x01。
//但这里我们是自定义的HID设备,所以不使用子类。
0x00,
//bInterfaceProtocol字段。如果子类为支持引导启动的子类,
//则协议可选择鼠标和键盘。键盘代码为0x01,鼠标代码为0x02。
//自定义的HID设备,也不使用协议。
0x00,
//iConfiguration字段。该接口的字符串索引值。这里没有,为0。
0x00,
/* 18 */
/******************HID描述符************************/
//bLength字段。本HID描述符下只有一个下级描述符。所以长度为9字节。
0x09,
//bDescriptorType字段。HID描述符的编号为0x21。
0x21,
//bcdHID字段。本协议使用的HID1.1协议。注意低字节在先。
0x10,
0x01,
//bCountyCode字段。设备适用的国家代码,这里选择为美国,代码0x21。
0x21,
//bNumDescriptors字段。下级描述符的数目。我们只有一个报告描述符。
0x01,
//bDescritporType字段。下级描述符的类型,为报告描述符,编号为0x22。
0x22,
//bDescriptorLength字段。下级描述符的长度。下级描述符为报告描述符。
USBD_HID_REPORT_DESC_SIZE,
0,
/* 27 */
/**********************输入端点描述符***********************/
//bLength字段。端点描述符长度为7字节。
0x07,
//bDescriptorType字段。端点描述符编号为0x05。
0x05,
//bEndpointAddress字段。端点的地址。我们使用D12的输入端点1。
//D7位表示数据方向,输入端点D7为1。所以输入端点1的地址为0x81。
HID_EPIN_ADDR,//IN 1
//bmAttributes字段。D1~D0为端点传输类型选择。
//该端点为中断端点。中断端点的编号为3。其它位保留为0。
0x03,
//wMaxPacketSize字段。该端点的最大包长。端点1的最大包长为16字节。
//注意低字节在先。
HID_EPIN_SIZE,
0x00,
//bInterval字段。端点查询的时间,我们设置为10个帧时间,即1ms。
0x01,
/* 34 */
/**********************输出端点描述符***********************/
//bLength字段。端点描述符长度为7字节。
0x07,
//bDescriptorType字段。端点描述符编号为0x05。
0x05,
//bEndpointAddress字段。端点的地址。我们使用D12的输出端点1。
//D7位表示数据方向,输出端点D7为0。所以输出端点1的地址为0x01。
HID_EPOUT_ADDR,
//bmAttributes字段。D1~D0为端点传输类型选择。
//该端点为中断端点。中断端点的编号为3。其它位保留为0。
0x03,
//wMaxPacketSize字段。该端点的最大包长。端点1的最大包长为16字节。
//注意低字节在先。
HID_EPOUT_SIZE,
0x00,
//bInterval字段。端点查询的时间,我们设置为10个帧时间,即1ms。
0x01,
/* 41 */
#if 1
/*--MIDI-------------------------------------------------------------------------*/
/******************* Standard AC Interface Descriptor *********************/
/* 00 */
0x09, /*bLength: Interface Descriptor size*/
USB_DESC_TYPE_INTERFACE, /*bDescriptorType: Interface descriptor type*/
USB_INTERFACE_MIDI_CONTRL_INDEX, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x00, /*bNumEndpoints*/
0x01, /*bInterfaceClass: Audio*/
0x01, /*bInterfaceSubClass : Audio Control*/
0, /*nInterfaceProtocol*/
0, /*iInterface: Index of string descriptor*/
/**************** Class-specific AC Interface Descriptor ******************/
/* 9 */
0x09, /*bLength: Interface Descriptor size*/
0x24, /*bDescriptorType: Class-specific interface descriptor type*/
0x01, /*bDescriptorSubType: Header*/
0x00, /*bcdADC: Revision of class specification - 1.0*/
0x01,
0x09, /*wTotalLength: Total size of class specific discriptor*/
0x00,
0x01, /*bInCollection: Number of streaming interfaces*/
USB_INTERFACE_MIDI_INDEX, /*baInterfaceNr : MIDIStreaming interface 1 belongs to
this AudioControl interface*/
/******************* Standard MS Interface Descriptor *********************/
/* 18 */
0x09, /*bLength: Interface Descriptor size*/
USB_DESC_TYPE_INTERFACE, /*bDescriptorType: Interface descriptor type*/
USB_INTERFACE_MIDI_INDEX, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x02, /*bNumEndpoints*/
0x01, /*bInterfaceClass: Audio*/
0x03, /*bInterfaceSubClass : MIDI Streaming*/
0, /*nInterfaceProtocol*/
0, /*iInterface: Index of string descriptor*/
/**************** Class-specific MS Interface Descriptor ******************/
/* 27 */
0x07, /*bLength: Interface Descriptor size*/
0x24, /*bDescriptorType: Class-specific interface descriptor type*/
0x01, /*bDescriptorSubType: MS Header*/
0x00, /*bcdADC: Revision of class specification*/
0x01,
0x41, /*wTotalLength: Total size of class specific discriptor*/
0x00,
/******************* MIDI IN Jack Descriptor (Embedded) *******************/
/* 34 */
0x06, /*bLength: Size of this descriptor*/
0x24, /*bDescriptorType: Class-specific interface descriptor type*/
0x02, /*bDescriptorSubType: MIDI IN Jack*/
0x01, /*bJackType: Embedded*/
0x01, /*bJackID: ID of this Jack*/
0x00, /*iJack*/
/******************* MIDI IN Jack Descriptor (External) *******************/
/* 40 */
0x06, /*bLength: Size of this descriptor*/
0x24, /*bDescriptorType: Class-specific interface descriptor type*/
0x02, /*bDescriptorSubType: MIDI IN Jack*/
0x02, /*bJackType: External*/
0x02, /*bJackID: ID of this Jack*/
0x00, /*iJack*/
/******************* MIDI OUT Jack Descriptor (Embedded) ******************/
/* 46 */
0x09, /*bLength: Size of this descriptor*/
0x24, /*bDescriptorType: Class-specific interface descriptor type*/
0x03, /*bDescriptorSubType: MIDI OUT Jack*/
0x01, /*bJackType: Embedded*/
0x03, /*bJackID: ID of this Jack*/
0x01, /*bNrInputPins: Number of Input Pins of this Jack*/
0x02, /*BaSourceID: ID of the Entry to which this Pin is connected*/
0x01, /*BaSourceID: Output Pin number of the Entry to
which this Input Pin is connected*/
0x00, /*iJack*/
/******************* MIDI OUT Jack Descriptor (External) ******************/
/* 55 */
0x09, /*bLength: Size of this descriptor*/
0x24, /*bDescriptorType: Class-specific interface descriptor type*/
0x03, /*bDescriptorSubType: MIDI OUT Jack*/
0x02, /*bJackType: External*/
0x04, /*bJackID: ID of this Jack*/
0x01, /*bNrInputPins: Number of Input Pins of this Jack*/
0x02, /*BaSourceID: ID of the Entry to which this Pin is connected*/
0x01, /*BaSourceID: Output Pin number of the Entry to
which this Input Pin is connected*/
0x00, /*iJack*/
/****************** Standard Bulk OUT Endpoint Descriptor *****************/
/* 64 */
0x09, /*bLength: Size of this descriptor*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType: Endpoint descriptor type*/
MIDI_EPOUT_ADDR, /*bEndpointAddress: OUT Endpoint 1*/
0x02, /*bmAttributes: Bulk, not shared.*/
0x40, /*wMaxPacketSize 64*/
0x00,
0x00, /*bInterval*/
0x00, /*bRefresh*/
0x00, /*bSynchAddress*/
/************* Class-specific MS Bulk OUT Endpoint Descriptor *************/
/* 73 */
0x05, /*bLength: Size of this descriptor*/
0x25, /*bDescriptorType: Class-specific endpoint descriptor type*/
0x01, /*bDescriptorSubType: MS General*/
0x01, /*bNumEmbMIDIJack: Number of embedded MIDI IN Jack*/
0x01, /*BaAssocJackID: ID of the Embedded MIDI IN Jack*/
/****************** Standard Bulk IN Endpoint Descriptor *****************/
/* 78 */
0x09, /*bLength: Size of this descriptor*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType: Endpoint descriptor type*/
MIDI_EPIN_ADDR, /*bEndpointAddress: IN Endpoint 1*/
0x02, /*bmAttributes: Bulk, not shared.*/
0x40, /*wMaxPacketSize 64*/
0x00,
0x00, /*bInterval*/
0x00, /*bRefresh*/
0x00, /*bSynchAddress*/
/************* Class-specific MS Bulk OUT Endpoint Descriptor *************/
/* 87 */
0x05, /*bLength: Size of this descriptor*/
0x25, /*bDescriptorType: Class-specific endpoint descriptor type*/
0x01, /*bDescriptorSubType: MS General*/
0x01, /*bNumEmbMIDIJack: Number of embedded MIDI OUT Jack*/
0x03, /*BaAssocJackID: ID of the Embedded MIDI OUT Jack*/
/* 92 */
#endif
};
/* 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_HID_fops_FS;
res += USBD_HID.Init(pdev,cfgidx);
pHIDData = pdev->pClassData;
pdev->pUserData = &USBD_MIDI_fops_FS;
res += USBD_MIDI.Init(pdev,cfgidx);
pMIDIData = 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->pUserData = &USBD_HID_fops_FS;
pdev->pClassData = pHIDData;
res += USBD_HID.DeInit(pdev,cfgidx);
pdev->pUserData = &USBD_MIDI_fops_FS;
pdev->pClassData = pMIDIData;
res += USBD_MIDI.DeInit(pdev,cfgidx);
return res;
}
/**
* @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 USB_INTERFACE_HID_INDEX:
pdev->pUserData = &USBD_HID_fops_FS;
pdev->pClassData = pHIDData;
return(USBD_HID.Setup(pdev, req));
case USB_INTERFACE_MIDI_INDEX:
pdev->pUserData = &USBD_MIDI_fops_FS;
pdev->pClassData = pMIDIData;
return(USBD_MIDI.Setup(pdev, req));
default:
break;
}
break;
case USB_REQ_RECIPIENT_ENDPOINT:
switch(req->wIndex)
{
case HID_EPIN_ADDR:
case HID_EPOUT_ADDR:
pdev->pClassData = pHIDData;
pdev->pUserData = &USBD_HID_fops_FS;
return(USBD_HID.Setup(pdev, req));
case MIDI_EPIN_ADDR:
case MIDI_EPOUT_ADDR:
pdev->pClassData = pMIDIData;
pdev->pUserData = &USBD_MIDI_fops_FS;
return(USBD_MIDI.Setup(pdev, req));
default:
break;
}
break;
}
return USBD_OK;
}
/**
* @brief USBD_HID_EP0_RxReady
* Handles control request data.
* @param pdev: device instance
* @retval status
*/
uint8_t USBD_Composite_EP0_RxReady(USBD_HandleTypeDef *pdev)
{
return USBD_HID.EP0_RxReady(pdev);
}
/**
* @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->pUserData = &USBD_HID_fops_FS;
pdev->pClassData = pHIDData;
return(USBD_HID.DataIn(pdev,epnum));
case MIDI_INDATA_NUM:
pdev->pUserData = &USBD_MIDI_fops_FS;
pdev->pClassData = pMIDIData;
return(USBD_MIDI.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->pUserData = &USBD_HID_fops_FS;
pdev->pClassData = pHIDData;
return(USBD_HID.DataOut(pdev,epnum));
case MIDI_OUTDATA_NUM:
pdev->pUserData = &USBD_MIDI_fops_FS;
pdev->pClassData = pMIDIData;
return(USBD_MIDI.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;
}
<
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USBD_COMPOSITE_H
#define __USBD_COMPOSITE_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "usbd_hid.h"
#include "usbd_midi.h"
#include "usbd_desc.h"
#include "usbd_ctlreq.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_COMPOSITE_DESC_SIZE (41+92)
#define USB_INTERFACE_HID_INDEX 0
#define USB_INTERFACE_MIDI_CONTRL_INDEX 1
#define USB_INTERFACE_MIDI_INDEX 2
#define HID_INDATA_NUM (HID_EPIN_ADDR & 0x0F)
#define HID_OUTDATA_NUM (HID_EPOUT_ADDR & 0x0F)
#define MIDI_INDATA_NUM (MIDI_EPIN_ADDR & 0x0F)
#define MIDI_OUTDATA_NUM (MIDI_EPOUT_ADDR & 0x0F)
extern USBD_ClassTypeDef USBD_COMPOSITE;
#ifdef __cplusplus
}
#endif
#endif
·修改usbd_device.c
void MX_USB_DEVICE_Init(void)
{
USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
USBD_RegisterClass(&hUsbDeviceFS, &USBD_COMPOSITE);
// USBD_HID_RegisterInterface(&hUsbDeviceFS, &USBD_HID_fops_FS);
USBD_Start(&hUsbDeviceFS);
}
·修改usbd_config.c
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 , HID_EPIN_ADDR , PCD_SNG_BUF, 0x98);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , HID_EPOUT_ADDR , PCD_SNG_BUF, 0xD8);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , MIDI_EPIN_ADDR , PCD_SNG_BUF, 0x118);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , MIDI_EPOUT_ADDR , PCD_SNG_BUF, 0x158);
return USBD_OK;
}
·修改usbd_config.h
#define USBD_MAX_NUM_INTERFACES 3
·修改usbd_desc.c
#define USBD_PRODUCT_STRING_FS "STM32_HID+MIDI"
·修改main.c文件,
·烧录测试
识别到HID设备和MIDI设备了
MIDI数据收发OK
HID数据收发OK
参考资料:
使用STM32CubeMX编写USB复合设备 https://www.taterli.com/2355/
STM32 USB复合设备编写 https://www.cnblogs.com/WeyneChen/p/6007049.html