Usb自定义HID设备(游戏控制器)总结
工程是由cubemx创建的不会创建的可以参考文章最后链接。该链接教程为未定义输入输出设备,本教程为游戏控制器属于输入设备。
在建立好USB工程之后,我们主要修改的东西就两个,一个是报告描述符可以在usbd_custom_hid_if.c文件中找到报告描述符位置。还有一个是设备描述符我们可以在usbd_customhid.c文件中找到。
首先我们修改报告描述符,报告描述符如下。
/** Usb HID report descriptor. */
__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
// 0x09, 0x04, // USAGE (Joystick)
0x09, 0x05, // USAGE (Game Pad)
0xa1, 0x01, // COLLECTION (Application)
0xa1, 0x02, // COLLECTION (Logical)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0xff, 0x00, // PHYSICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x02, // INPUT (Data,Var,Abs)28
0x09, 0x39, // USAGE (Hat switch)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x03, // LOGICAL_MAXIMUM (3)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0x0e, 0x01, // PHYSICAL_MAXIMUM (270)
0x65, 0x14, // UNIT (Eng Rot:Angular Pos)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs) 19
0x09, 0x36, // USAGE (Slider)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0xff, 0x7f, // PHYSICAL_MAXIMUM (32767)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)18
0x09, 0x36, // USAGE (Slider)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0xff, 0x7f, // PHYSICAL_MAXIMUM (32767)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)18
0x09, 0x36, // USAGE (Slider)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0xff, 0x7f, // PHYSICAL_MAXIMUM (32767)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)18
//
0x09, 0x33, // USAGE (x)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0xff, 0x7f, // PHYSICAL_MAXIMUM (32767)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)46 18
//
0x09, 0x34, // USAGE (y)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0xff, 0x7f, // PHYSICAL_MAXIMUM (32767)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)46 18
0x09, 0x35, // USAGE (z)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0xff, 0x7f, // PHYSICAL_MAXIMUM (32767)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)46 18
//
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x20, // USAGE_MAXIMUM (Button 64)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x20, // REPORT_COUNT (64)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
报告描述符,主要是形容USB的属性功能,他会告诉PC端我这个USB自定义HID设备是干什么的,我会向PC端报告多少个字节,每个字节又有什么含义。
报告描述符就是一个数组,我们定义报告描述符的大小一定要数清楚。数好之后我们goto到USBD_CUSTOM_HID_REPORT_DESC_SIZE,
#define USBD_CUSTOM_HID_REPORT_DESC_SIZE 173//173//报告描述符长度
可以看到173就是这个报告描述符的长度。
这个报告描述符我是从官方那里下载的,然后进行修改,毕竟自己定义的报告描述符没有官方的标准很多细节还是要参考官方为准。这是一个游戏手柄的例程。
接下来我们看这个报告描述符到底怎么描述自己的属性,
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
告诉PC我是桌面类的
0x09, 0x04, // USAGE (Joystick)
告诉PC我是干什么的,Joystick是一个摇杆类,同样你也可以定义别的比如 0x09, 0x05, // USAGE (Game Pad),
他告诉PC我是一个手柄类。他也可以定义成鼠标或者键盘,具体根据自己需求。
0xa1, 0x01, // COLLECTION (Application)
0xa1, 0x02, // COLLECTION (Logical)
他告诉PC端我的物理逻辑,我们属于输入设备,以外部物理逻辑判断我们的行为再向PC端输入。
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
上面说过,这是一个游戏手柄的报告描述符,所以拥有X轴和Y轴
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0xff, 0x00, // PHYSICAL_MAXIMUM (255)
这是形容XY轴所占的字节大小0-255,没错就是一个字节。
0x75, 0x08, // REPORT_SIZE (8)
一次性报告的大小,这里面的大小是按照BIT来算的,一个字节就是8个bit位,在这里我们要知道,usb向PC端报告最小是一个字节,不能比一个字节小,后面会说到按键,按键是按照一个按键占用一个BIT位,当你的按键数量不够八个的时候,也就是小于一个字节,此时我们要补足,必须要补足一个字节。
0x95, 0x02, // REPORT_COUNT (2)
这是报告次数的一次,X轴报告一次,Y轴报告一次共两次。一次一个字节,共两个字节。
0x81, 0x02, // INPUT (Data,Var,Abs)
是输入还是输出,这里的输入输出是PC端为主。
0x09, 0x39, // USAGE (Hat switch)
告诉PC端我们要加一个Hat switch是一个可视头盔有多个方向,
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x03, // LOGICAL_MAXIMUM (3)
告诉PC我们的最小逻辑是0,最大逻辑是3,也就是从0-3,共四个方向如果我们要定义八个方向只要将最大逻辑定义成7,0-7共八个方向。
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0x0e, 0x01, // PHYSICAL_MAXIMUM (270)
告诉PC我们的最大逻辑最小逻辑,四个方向,第一个方向是0度,第二个人方向是90,第三个是180,第四个是270,将360平均分成四份。
0x65, 0x14, // UNIT (Eng Rot:Angular Pos)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
报告大小,和报告次数,以及是输入输出,可以看出这个头盔是占一个字节的。
0x09, 0x36, // USAGE (Slider)
告诉PC我要添加一个滑块,滑块就是一个ADC采集输入,游戏设备上的油门之类的。
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0xff, 0x7f, // PHYSICAL_MAXIMUM (32767)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)18
告诉PC我的报告字节大小,输入还是输出,该滑块占用一个字节。
接下来我添加了共6个滑块,PC端最多只能添加6个滑块,添加滑块的时候要注意,这些东西都是PC上定死的,我们添加的时候要注意PC上的规定,比如滑块属性最多只能添加3个,位置在1,5,6,当你再添加的时候就不会再添加了,所以我加了x(在位置2),y(在位置3),z(在位置4)另外三个滑块凑足6个滑块。这些位置是PC定死的。
0x05, 0x09, // USAGE_PAGE (Button)
告诉PC我要添加按键属性
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x20, // USAGE_MAXIMUM (Button 32)
按键最小是1最大是32,这里PC最多只能添加32个按键
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
按键逻辑最小为0,最大为1,按键就0和1的逻辑,按下和抬起。
0x95, 0x20, // REPORT_COUNT (32)
0x75, 0x01, // REPORT_SIZE (1)
按键报告是1个bit,共报告32次,所以占用4个字节
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
结束,前面用到几个COLLECTION结尾就用到几个结束。
上述报告描述符共用到13个字节,X轴一个字节、Y轴一个字节、可视头盔一个字节、滑块6个共六个字节、按键32个共4个字节,加起来是13个字节。报告描述符修改完了。
接下来修改设备描述符
```c
_ALIGN_BEGIN static uint8_t USBD_CUSTOM_HID_CfgFSDesc[USB_CUSTOM_HID_CONFIG_DESC_SIZ] __ALIGN_END =
{
0x09, /* bLength: Configuration Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
USB_CUSTOM_HID_CONFIG_DESC_SIZ,
/* wTotalLength: Bytes returned */
0x00,
0x01, /*bNumInterfaces: 1 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: 2 Byte max */
0x00,
CUSTOM_HID_FS_BINTERVAL, /*bInterval: Polling Interval */
/* 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: 2 Bytes max */
0x00,
CUSTOM_HID_FS_BINTERVAL, /* bInterval: Polling Interval */
/* 41 */
};
设备描述符,告诉PC端我有几个端点,其中输入端点要向你报告的字节多少个,输出端点要接收你的字节多少个(我们属于输入设备,这个不需要考虑)。我们只需要了解这三点,修改着三点就行了。
找到 0x02, /bNumEndpoints/
这是告诉PC我们端点数目是2,因为是gamepad设备能用到只有输入端点,添加两个也没问题,后面如果你要改成两个端点设备就不用修改了
/* 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: 2 Byte max */
0x00,
CUSTOM_HID_HS_BINTERVAL, /*bInterval: Polling Interval /
/ 34 */
这一块是描述输入端点的我们可以看到 CUSTOM_HID_EPIN_ADDR, /bEndpointAddress: Endpoint Address (IN)/后面的IN就是输入的意思
CUSTOM_HID_EPIN_SIZE, /*wMaxPacketSize: 2 Byte max */这个是我们输入的大小,我们goto去修改他,上面说到我们共是13个字节,所以修改成 #define CUSTOM_HID_EPIN_SIZE 0x0dU
/* 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: 2 Bytes max /
0x00,
CUSTOM_HID_HS_BINTERVAL, / bInterval: Polling Interval /
/ 41 */
这一块是描述输出端点的我们用不到,添加日后好修改,他的意思是跟输入差不多,我们只需修改接收字节大小就行了
CUSTOM_HID_EPOUT_SIZE, /* wMaxPacketSize: 2 Bytes max */
这两处修改完了你的USB自定义游戏手柄也就完成了。代码烧进板子后,我们在我的电脑,右击选择属性,找到控制面板
进入后可以看到一盒游戏手柄
右击他,选择游戏控制器
进入属性后,我们可以看到我们刚才定义的东西
32个按键、XY轴、六个滑块、一个视觉头盔,这基本满足了所有的游戏控制器,当我们不想交这个名字的时候我们可以修改usbd_desc.c文件中的
#define USBD_PRODUCT_STRING_FS “Game Control Panez”
可以看到我只改了最后一个字母。
我们同样可以修改厂商ID和设备ID也就是VID和PID也在这个文件下
#define USBD_VID 1155
#define USBD_PID_FS 22351
记住同一个设备不能拥有两个ID一样的设备,具体根据自己情况修改。
接下来我们看如何使用找到usbd_custom_hid_if.c这个文件,我们可以找到
int8_t USBD_CUSTOM_HID_SendReport_FS(uint8_t *report, uint16_t len)
{
return USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, report, len);
}
这个函数是usb发送函数,共两个参数,第一个参数是我们要报告的数组,记住我们当时定义的是13个字节,所以这个数组必须是13个字节的,否则无法识别,第二个参数是这个数组大小。
该例程只用于向PC端发数据,GAMEPAD设备无法接收PC端数据,如果要自定义可以接收和发送的HID设备可以参考该例程只用于向PC端发数据,GAMEPAD设备无法接收PC端数据,如果要自定义可以接收和发送的HID设备可以参考链接教程
最后向PC端发送数据的时候,我们定了13个字节大小的数据,数组的每个字节的意思,和我们定义的报告描述符顺序一样,如我们刚开始定义了X那么第0个字节就是X走的意思,然后我们定义了Y那个第1个字节就是Y的意思以此类推。这里要注意,视觉头盔是从0x00开始的,0x00表示上,0x01表示右,0x02表示下,0x03表示左。并非按照bit控制,按键是按照位控制的。
本人纯属自己搞着玩,有错误请大佬嘴下留情
链接:https://www.bilibili.com/video/BV1eE411o7Qf?p=3