前言
查阅网上的博客与代码,很多都是关于USB的鼠标配置、USB的键盘配置、USB的虚拟串口配置,稍微深入一点的会将鼠标键盘合在一起,但移植起来就会报很多错误,要么是检测不到,要么是警告,这很正常,因为不理解这些数字代表着什么。但只要理解每个数字代表什么意思,想错都难,干货满满,和我一起学习吧
1、STM32 usbdesc.c USB描述符配置
1.1、设备描述符
/* USB Standard Device Descriptor */
const uint8_t Mouse_DeviceDescriptor[Mouse_SIZ_DEVICE_DESC] =
{
0x12, /*bLength */
USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/
0x00, /*bcdUSB */
0x02,
0xEF, /*bDeviceClass*/
0x02, /*bDeviceSubClass*/
0x01, /*bDeviceProtocol*/
0x40, /*bMaxPacketSize40*/
0x83, /*idVendor (0x0483)*/
0x04,
0x10, /*idProduct = 0x5710*/
0x57,
0x00, /*bcdDevice rel. 2.00*/
0x02,
1, /*Index of string descriptor describing
manufacturer */
2, /*Index of string descriptor describing
product*/
3, /*Index of string descriptor describing the
device serial number */
0x01 /*bNumConfigurations*/
}
; /* Mouse_DeviceDescriptor */
其一,组合设备需要对设备描述符修改为0xEF,0x02,0x01,若想了解这三个参数可以百度了解一下,里面有详细的介绍,这里不做说明。
看起来很懵对吧,其实很多值是固定的不需要修改,只有几项需要修改而已。而且这个配置不随着增加设备或减少设备而改变,配置好后不需要修改,嫌麻烦直接复制上面设备描述符代码就好,兼容性很强。
1.2、配置描述符
const uint8_t Mouse_ConfigDescriptor[Mouse_SIZ_CONFIG_DESC] =
{
配置描述符 //Configuration Descriptor 只能有1个
/*功能1 HID键盘*/
IAD描述符 //复合设备才有 在单接口的设备这个可以不要
接口1描述符 //Interface Descriptor
类描述符 //Class Desdriptor
端点描述符 //Endpoint Descriptor
/*功能2 HID鼠标*/
IAD描述符 //复合设备才有 在单接口的设备这个可以不要
接口2描述符 //Interface Descriptor
类描述符 //Class Desdriptor
端点描述符 //Endpoint Descriptor
/*功能3 虚拟串口*/
IAD描述符 //复合设备才有 在单接口的设备这个可以不要
接口3描述符 //Interface Descriptor
类描述符 //Class Desdriptor
端点描述符 //Endpoint Descriptor
/*如果有多个接口 下面还可以继续添加*/
...
}
举例说明:鼠标+键盘+虚拟串口
const uint8_t Mouse_ConfigDescriptor[Mouse_SIZ_CONFIG_DESC] =
{
0x09, /* bLength: Configuation Descriptor size */
USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
Mouse_SIZ_CONFIG_DESC,
/* wTotalLength: Bytes returned */
0x00,
0x04, /*bNumInterfaces: 4 interface*/设备总数
0x01, /*bConfigurationValue: Configuration value*/必须为0x01,或者更高
0x00, /*iConfiguration: Index of string descriptor describing
the configuration*/字符串索引,若没有字符串描述符,这个字段为0
0x80, /*bmAttributes: bus powered */
0x32, /*MaxPower 100 mA: this current is used for detecting Vbus*/
/*************************************功能1 HID键盘**************************************/
/*IAD描述符*/
0x08, //bLength:IAD描述符大小
0x0B, //bDescriptorType:IAD描述符类型
0x00, //bFirstInterface:功能1 HID键盘的第一个接口描述符是在总的配置描述符中的第几个从0开始数
0x01, //bInferfaceCount:功能1 HID键盘有1个接口描述符
0x03, //bFunctionClass:同单HID功能时,设备符中的bDeviceClass
0x00, //bFunctionSubClass:同单HID功能时,设备符中的bDeviceSubClass
0x00, //bFunctionProtocol:同单HID功能时,设备符中的bDeviceProtocol
0x01, //iFunction:字符串描述中关于此设备的索引(个人理解是一个字符串描述符中有比如0~5是功能1的字符串,
//6~10是功能2的字符串,如果是功能2的话,此值为6)
/*******************第一个接口描述符*********************/
0x09, //bLength字段
0x04, //bDescriptorType字段
0x00, /*bInterfaceNumber字段*/从0开始编号
0x00, /*bAlternateSetting字段*/必须为0x00
0x02, /*bNumEndpoints字段*/表示接口描述符有多少个端点
0x03, /*bInterfaceClass字段*/设备类型,例如HID、VCP、CDC
0x01, /*bInterfaceSubClass字段*/0x00代表No subclass,0x01代表Boot Interface subclass
0x01, /*bInterfaceProtocol字段*/0x00 None,0x01 keyboard,0x02 mouse,3~255 reserved
0x00, //iConfiguration字段
/******************HID描述符************************/
0x09, //bLength字段
0x21, //bDescriptorType字段
0x10, /*bcdHID字段*/设备遵循的HID版本号
0x01,
0x21, /*bCountyCode字段*/语言
0x01, /*bNumDescriptors字段*/下级报告描述符数量,至少为1,0x01
0x22, /*bDescriptorType字段*/报告描述符必须为0x22
//bDescriptorLength字段。
//下级描述符的长度。下级描述符为键盘报告描述符。
sizeof(KeyboardReportDescriptor)&0xFF,
(sizeof(KeyboardReportDescriptor)>>8)&0xFF,
/**********************输入端点描述符***********************/
0x07, //bLength字段
0x05, //bDescriptorType字段
0x81, /*bEndpointAddress字段*/端口的地址
bit 3~0:端口号
bit 7 :0 输入端口(主机到设备)
1 输出端口(设备到主机)
0x03, //bmAttributes字段
0x10, //wMaxPacketSize字段
0x00,
0x0A, //bInterval字段
/**********************输出端点描述符***********************/
0x07, //bLength字段
0x05, //bDescriptorType字段
0x01, //bEndpointAddress字段
0x03, //bmAttributes字段
0x10, //wMaxPacketSize字段
0x00,
0x0A, //bInterval字段
/*IAD描述符*/
0x08, //bLength:IAD描述符大小
0x0B, //bDescriptorType:IAD描述符类型
0x01, //bFirstInterface:功能1 HID键盘的第一个接口描述符是在总的配置描述符中的第几个从0开始数
0x01, //bInferfaceCount:功能1 HID键盘有1个接口描述符
0x03, //bFunctionClass:同单HID功能时,设备符中的bDeviceClass
0x00, //bFunctionSubClass:同单HID功能时,设备符中的bDeviceSubClass
0x00, //bFunctionProtocol:同单HID功能时,设备符中的bDeviceProtocol
0x01, //iFunction:字符串描述中关于此设备的索引(个人理解是一个字符串描述符中有比如0~5是功能1的字符串,
//6~10是功能2的字符串,如果是功能2的话,此值为6)
/************** Descriptor of Mouse Mouse interface ****************/
/* 09 */
0x09, /*bLength: Interface Descriptor size*/
USB_INTERFACE_DESCRIPTOR_TYPE,/*bDescriptorType: Interface descriptor type*/
0x01, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x01, /*bNumEndpoints*/
0x03, /*bInterfaceClass: HID*/
0x01, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x02, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
0, /*iInterface: Index of string descriptor*/
/******************** Descriptor of Mouse Mouse HID ********************/
/* 18 */
0x09, /*bLength: HID Descriptor size*/
HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
0x10, /*bcdHID: HID Class Spec release number*/
0x01,
0x21, /*bCountryCode: Hardware target country*/
0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/
0x22, /*bDescriptorType*/
Mouse_SIZ_REPORT_DESC,/*wItemLength: Total length of Report descriptor*/
0x00,
/******************** Descriptor of Mouse Mouse endpoint ********************/
/* 27 */
0x07, /*bLength: Endpoint Descriptor size*/
USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
0x82, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
0x40, /*wMaxPacketSize: 4 Byte max */
0x00,
0x0A, /*bInterval: Polling Interval (32 ms)*/
/* 34 */
/*IAD描述符 66*/
/* Interface Association Descriptor(IAD Descriptor) */
0x08, /* bLength */
0x0B, /* bDescriptorType*/
0x02, /* bFirstInterface*/
0x02, /* bInterfaceCount*/
0x02, /* bFunctionClass --CDC*/
0x02, /* bFunctionSubClass*/
0x01, /* bFunctionProtocoll*/
0x00, /* iFunction */
/*Interface Descriptor*/
0x09, /* bLength: Interface Descriptor size */
USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: Interface */
/* Interface descriptor type */
0x02, /* 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 */
0x00, /* 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 Managment 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 */
0x00, /* bMasterInterface: Communication class interface */
0x01, /* bSlaveInterface0: Data Class Interface */
/*Endpoint 2 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
0x85, /* bEndpointAddress: (IN5) */
0x03, /* bmAttributes: Interrupt */
0x40, /* wMaxPacketSize: */
0x00,
0xFF, /* bInterval: */
/*Data class interface descriptor*/
0x09, /* bLength: Endpoint Descriptor size */
USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: */
0x03, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints: Two endpoints used */
0x0A, /* bInterfaceClass: CDC */
0x00, /* bInterfaceSubClass: */
0x00, /* bInterfaceProtocol: */
0x00, /* iInterface: */
/*Endpoint 3 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
0x06, /* bEndpointAddress: (OUT6) */
0x02, /* bmAttributes: Bulk */
0x40, /* wMaxPacketSize: */
0x00,
0x00, /* bInterval: ignore for Bulk transfer */
/*Endpoint 1 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
0x87, /* bEndpointAddress: (IN7) */
0x02, /* bmAttributes: Bulk */
0x40, /* wMaxPacketSize: */
0x00,
0x00 /* bInterval */
}
需要注意的点
1、 配置描述符中0x04, /*bNumInterfaces: 4 interface*/
必须与设备个数相等。
2、 每个设备都需要一个IAD描述符。
3、bFirstInterface:功能1 HID键盘的第一个接口描述符是在总的配置描述符中的第几个从0开始数
4、bNumEndpoints
端点数量必须与下面配置的端点数量相同
5、bEndpointAddress
需要注意地址
1.3、HID报告描述符
例如:HID键盘,建议直接复制。
//USB键盘报告描述符的定义
const u8 KeyboardReportDescriptor[KP_ReportDescriptor_Size]=
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop) //63
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x95, 0x05, // REPORT_COUNT (5)
0x75, 0x01, // REPORT_SIZE (1)
0x05, 0x08, // USAGE_PAGE (LEDs)
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
0x29, 0x05, // USAGE_MAXIMUM (Kana)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x03, // REPORT_SIZE (3)
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0, // END_COLLECTION
//0xc0,
}; /* KeyboardMouse_ReportDescriptor */
1.4、字符串描述符
说明一下,一般这里不会出错,不做解释。
/* USB String Descriptors (optional) */
const uint8_t Mouse_StringLangID[Mouse_SIZ_STRING_LANGID] =
{
Mouse_SIZ_STRING_LANGID,
USB_STRING_DESCRIPTOR_TYPE,
0x09,
0x04
}
; /* LangID = 0x0409: U.S. English */
const uint8_t Mouse_StringVendor[Mouse_SIZ_STRING_VENDOR] =
{
Mouse_SIZ_STRING_VENDOR, /* Size of Vendor string */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType*/
/* Manufacturer: "STMicroelectronics" */
'S', 0, 'T', 0, 'M', 0, 'i', 0, 'c', 0, 'r', 0, 'o', 0, 'e', 0,
'l', 0, 'e', 0, 'c', 0, 't', 0, 'r', 0, 'o', 0, 'n', 0, 'i', 0,
'c', 0, 's', 0
};
const uint8_t Mouse_StringProduct[Mouse_SIZ_STRING_PRODUCT] =
{
Mouse_SIZ_STRING_PRODUCT, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
'S', 0, 'T', 0, 'M', 0, '3', 0, '2', 0, '1', 0, '0', 0
};
uint8_t Mouse_StringSerial[Mouse_SIZ_STRING_SERIAL] =
{
Mouse_SIZ_STRING_SERIAL, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
'S', 0, 'T', 0, 'M', 0, '3', 0, '2', 0, '1', 0, '0', 0
};
2、STM32 usbprop.c USB应用相关的属性配置
主要包括两个函数的修改,参考此模板进行添加,如下
void Mouse_Reset(void)
{
/* Set Mouse_DEVICE as not configured */
pInformation->Current_Configuration = 0;
pInformation->Current_Interface = 0;/*the default Interface*/
/* Current Feature initialization */
pInformation->Current_Feature = Mouse_ConfigDescriptor[7];
SetBTABLE(BTABLE_ADDRESS);
/* Initialize Endpoint 0 */
SetEPType(ENDP0, EP_CONTROL);
SetEPTxStatus(ENDP0, EP_TX_STALL);
SetEPRxAddr(ENDP0, ENDP0_RXADDR);
SetEPTxAddr(ENDP0, ENDP0_TXADDR);
Clear_Status_Out(ENDP0);
SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
SetEPRxValid(ENDP0);
/* Initialize Endpoint 1 */
SetEPType(ENDP1, EP_INTERRUPT);
SetEPTxAddr(ENDP1, ENDP1_TXADDR);
SetEPTxCount(ENDP1, 4);
SetEPRxStatus(ENDP1, EP_RX_DIS);
SetEPTxStatus(ENDP1, EP_TX_NAK);
/* Initialize Endpoint Out 1 */
SetEPRxAddr(ENDP1, ENDP1_RXADDR); //设置接收数据的地址
SetEPRxCount(ENDP1, 2); //设置接收长度
SetEPRxStatus(ENDP1, EP_RX_VALID); //设置端点有效,可以接收数据
/* Initialize Endpoint In 2 */
SetEPType(ENDP2, EP_INTERRUPT); //初始化为中断端点类型
SetEPTxAddr(ENDP2, ENDP2_TXADDR); //设置发送数据的地址
SetEPTxCount(ENDP2, 5); //设置发送的长度
SetEPTxStatus(ENDP2, EP_TX_NAK); //设置端点处于忙状态
/* Initialize Endpoint 1 */
SetEPType(ENDP7, EP_BULK);
SetEPTxAddr(ENDP7, ENDP7_TXADDR);
SetEPTxStatus(ENDP7, EP_TX_NAK);
SetEPRxStatus(ENDP7, EP_RX_DIS);
/* Initialize Endpoint 2 */
SetEPType(ENDP5, EP_INTERRUPT);
SetEPTxAddr(ENDP5, ENDP5_TXADDR);
SetEPRxStatus(ENDP5, EP_RX_DIS);
SetEPTxStatus(ENDP5, EP_TX_NAK);
/* Initialize Endpoint 3 */
SetEPType(ENDP6, EP_BULK);
SetEPRxAddr(ENDP6, ENDP6_RXADDR);
SetEPRxCount(ENDP6, 64);
SetEPRxStatus(ENDP6, EP_RX_VALID);
SetEPTxStatus(ENDP6, EP_TX_DIS);
bDeviceState = ATTACHED;
/* Set this device to response on default address */
SetDeviceAddress(0);
}
RESULT Mouse_Data_Setup(uint8_t RequestNo)
{
uint8_t *(*CopyRoutine)(uint16_t);
CopyRoutine = NULL;
if ((RequestNo == GET_DESCRIPTOR)
&& (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))
&& (pInformation->USBwIndex0 < 4))
{
if (pInformation->USBwValue1 == REPORT_DESCRIPTOR)
{
if (pInformation->USBwIndex0 == 0)
CopyRoutine = KP_GetReportDescriptor;
else
CopyRoutine = Mouse_GetReportDescriptor;
}
else if (pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE)
{
if (pInformation->USBwIndex0 == 0)
CopyRoutine = KP_GetHIDDescriptor;
else
CopyRoutine = Mouse_GetHIDDescriptor;
}
} /* End of GET_DESCRIPTOR */
/*** GET_PROTOCOL ***/
else if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
&& RequestNo == GET_PROTOCOL)
{
CopyRoutine = Mouse_GetProtocolValue;
}
if (RequestNo == GET_LINE_CODING)
{
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
{
CopyRoutine = Virtual_Com_Port_GetLineCoding;
}
}
else if (RequestNo == SET_LINE_CODING)
{
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
{
CopyRoutine = Virtual_Com_Port_SetLineCoding;
}
Request = SET_LINE_CODING;
}
if (CopyRoutine == NULL)
{
return USB_UNSUPPORT;
}
pInformation->Ctrl_Info.CopyData = CopyRoutine;
pInformation->Ctrl_Info.Usb_wOffset = 0;
(*CopyRoutine)(0);
return USB_SUCCESS;
}
需要注意的点:
KP_OFF_HID_DESC
必须与配置描述符中HID对应的类描述符的地址相等,可以理解为地址偏移量
ONE_DESCRIPTOR KP_Hid_Descriptor = //
{ //
(u8*)Mouse_ConfigDescriptor + KP_OFF_HID_DESC, //
Mouse_SIZ_HID_DESC //
};
3、STM32 usb conf.h USB配置文件
主要做配置端点基地址
#define BTABLE_ADDRESS (0x00)
/* EP0 */
/* rx/tx buffer base address */
#define ENDP0_RXADDR (0x18)
#define ENDP0_TXADDR (0x58)
/* EP1 */
/* tx buffer base address */
#define ENDP1_TXADDR (0x100)
#define ENDP1_RXADDR (0x110)
/* EP2 */
/* tx buffer base address */
#define ENDP2_TXADDR (0x120)
#define ENDP7_TXADDR (0x140)
#define ENDP5_TXADDR (0x180)
#define ENDP6_RXADDR (0x1C0)
4、STM32 usb istr.c USB中断处理函数
在文件最后添加对应端点的中断处理函数
例如如下代码
u8 EP1BUSY=0;
u8 EP2BUSY=0;
void EP1_IN_Callback(void)
{
// EP1BUSY=0;
}
void EP2_IN_Callback(void)
{
// EP2BUSY=0;
}
void EP1_OUT_Callback(void)
{
// SetEPRxValid(ENDP1);
}
最后看一下效果
接入stm32 USB接口时
文章来源:https://www.toymoban.com/news/detail-545605.html
资源地址 https://download.csdn.net/download/weixin_44748127/85983409?spm=1001.2014.3001.5501文章来源地址https://www.toymoban.com/news/detail-545605.html
到了这里,关于stm32 USB HID+CDC 鼠标键盘串口 组合设备配置解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!