关于STM32的USB设备库DIY机械键盘

关于STM32的USB设备库DIY机械键盘
前⾔
为什么想写这个呢,⾸先⼀⽅⾯是因为⾃⼰喜欢DIY⼀些⼩玩意,另⼀⽅⾯关于USB-HID的东西断断续续的学习了不少东西,想总结整理⼀下。其次就是⽹络上关于STM32制作USB-HID的案例很多,我这⾥尽量说⼀些不⼀样的,以便⼤家参考,内容主要还是关于机械键盘⽅⾯的。
本来早就应该写的,⾃⼰懒,然后⼜喜欢拖延,最近想着利⽤下班的时间将它陆续整理完善吧。
DIY机械键盘相⽐于客制化机械键盘更为彻底,也没有客制化出来的机械键盘好⽤,我追寻的只是DIY给我带来的乐趣与成就感。
如果只是想要⼀把好⽤的键盘,建议还是买⼀把量产键盘。客制化烧钱,有这个钱,能卖⾼⾄少⼀个等级的量产键盘。
前后做了三种键盘,GH60,Keycool84,还有标准的87配列。有的送⼈了,有的还在⾃⼰⼿上。其中GH60和Keycool84的PCB是⾃⼰设计的,代码也是⾃⼰敲的,很丑陋,代码习惯不好,就不放出来献丑了,后⾯主要还是告诉各位实现原理与⽅法。
⼀ STM32的设备库与USB协议
STM32的设备库中有很多的例程,⽐如DFU,MSC,HID等等,或者直接使⽤STM32CubeMX⽣成的CoustomHID⼯程也可以⼀样使⽤,如何移植,如何让⾃⼰的PCBA在电脑上成功的识别为⼀个HID设备,我这⾥就不过多赘述了,⽹上有⼤量的例⼦可以参考。因为库中的例⼦都是依托STM32的评估板来做的,所以修改起来会⽐较⿇烦,倒不如后⼀种⽅法省事。
重点说⼀句,就是HID设备的特别之处就在于HID描述符和报告描述符,你这个设备发送给电脑的数据有啥⽤,需要电脑给你什么数据,都是通过报告描述符来实现的。⽽这两个描述符中的各个数据有什么作⽤,在HID Usage Table这个⽂档中都有说明,⽽且,写的很详细。但是,但是,但是是英⽂⽂档,⼜臭⼜长,看了下⼀段,忘了上⼀段。所以不想去看的也可以直接复制下⾯的描述符直接⽤。
1.1 设备描述符
const uint8_t CustomHID_DeviceDescriptor[CUSTOMHID_SIZ_DEVICE_DESC]=
{
0x12,/*bLength:18Bytes */
USB_DEVICE_DESCRIPTOR_TYPE,/*bDescriptorType*/
0x00,/*bcdUSB */
0x02,
0x00,/*bDeviceClass*/
0x00,/*bDeviceSubClass*/
0x00,/*bDeviceProtocol*/
0x40,/*bMaxPacketSize:64Bytes*/
0x83,/*idVendor (0x0483)*/
0x04,
0x12,/*idProduct = 0x5750*/
0x34,
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*/
}
关于设备描述符,我只说⼀个点,即idVendor和idProduct这两个数据。这两个数值是不能随便定义的,但是你随便定义也没有关系。我在调试的过程中发现这样的⼀个问题,如果我更改了譬如配置描述符的内容,但是没有更改上⾯这两个数据,电脑就显⽰上⼀次的设备,⽽不是你更改了的设备。只有在更改了这两个数据以后,PC才会重新识别这个设备。这个情况在Win7上是如此。Win10没有出现过。
1.2 配置描述符滚山虫
0x09,/* bLength: Configuration Descriptor size */
USB_CONFIGURATION_DESCRIPTOR_TYPE,/* bDescriptorType: Configuration */
CUSTOMHID_SIZ_CONFIG_DESC,
/* wTotalLength: Bytes returned */
0x00,
0x01,/* bNumInterfaces: 1 interface */
0x01,/* bConfigurationValue: Configuration value */
0x00,/* iConfiguration: Index of string descriptor describing
the configuration*/
0x80,/* bmAttributes: Self powered */
0x64,/* MaxPower 200 mA: this current is used for detecting Vbus */
关于配置描述符,bNumInterfaces说的是你这个设备有多少个接⼝。还有最后⼀个0xC8这个数据表⽰你这个设备需要从总线上获取多少电流,这个视你设备功耗决定。0xC8 * 2 = 400mA,最⼤能获取500mA的电流。
1.3 接⼝描述符
0x09,/* bLength: Interface Descriptor size */
USB_INTERFACE_DESCRIPTOR_TYPE,/* bDescriptorType: Interface descriptor type */
0x00,/* bInterfaceNumber: Number of Interface */
0x00,/* bAlternateSetting: Alternate setting */
0x02,/* bNumEndpoints,except endpoint 0 */
0x03,/* bInterfaceClass: HID */
0x01,/* bInterfaceSubClass : 1=BOOT, 0=no boot */
0x01,/* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */
0,/* iInterface: Index of string descriptor */
关于接⼝描述符,如果只需要⼀个接⼝,直接复制粘贴就能⽤。另外注意的是,多接⼝复合设备中,只能有⼀个Boot设备,即bInterfaceSubClass这个变量,只有⼀个接⼝可以为1,其他的为0。具体为什么,我也不清楚,有知道的朋友可以回复⼀下,谢谢。因为我有⼀次定义了两个Boot接⼝,设备没有枚举成功。
1.4 HID描述符
0x09,/* bLength: HID Descriptor size */
HID_DESCRIPTOR_TYPE,/* bDescriptorType: HID */
0x10,/* bcdHID: HID Class Spec release number */
0x01,
0x00,/* bCountryCode: Hardware target country */
0x01,/* bNumDescriptors: Number of HID class descriptors to follow */
0x22,/* bDescriptorType */
CUSTOMHID_SIZ_REPORT_DESC,/* wItemLength: Total length of Report descriptor */
0x00,
键盘映射
关于HID描述符,CUSTOMHID_SIZ_REPORT_DESC这个值需要根据报告描述符的长度来修改。
1.5 端点描述符
0x07,/* bLength: Endpoint Descriptor size */阻燃剂tbc
USB_ENDPOINT_DESCRIPTOR_TYPE,/* bDescriptorType: */
0x81,/* bEndpointAddress: Endpoint Address (IN) */
0x03,/* bmAttributes: Interrupt endpoint */
0x08,/* wMaxPacketSize: 8 Bytes max */
0x00,
0x05,/* bInterval: Polling Interval (5 ms) */
0x07,/* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE,/* bDescriptorType: */
/* Endpoint descriptor type */
0x02,/* bEndpointAddress:Endpoint Address (OUT) */
0x03,/* bmAttributes: Interrupt endpoint */
0x01,/* wMaxPacketSize: 1 Bytes max  */
0x00,
0x05,/* bInterval: Polling Interval (5 ms) */
关于端点描述符,bEndpointAddress表⽰端点地址,表⽰当前这个接⼝所需要的端点资源,输⼊(相对
于主机⽽⾔)端点最⾼位为1,输出(相对于主机⽽⾔)端点最⾼位为0。然后说⼀句,HID设备⼀般都是使⽤中断端点进⾏数据传输。wMaxPacketSize表⽰该端点上数据传输的数量。bInterval表⽰主机查询设备数据的时间间隔,如果设置的太长,则键盘输⼊延迟很⾼,深有体会。
1.6 报告描述符
0x05,0x01,// USAGE_PAGE (Generic Desktop)
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)
tm2007
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,0xFF,// LOGICAL_MAXIMUM (255)
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  /* 63 */
关于报告描述符,具体每⼀个数据什么意思,可以参考前⾯说的⽂档HID Usage Table。这个就是标准6KRO键盘的报告描述符。
⾄于还有 字符串 描述符,不说了,有⽤,⽤处不⼤,不修改直接⽤,没有⼀点关系。
⼆多媒体按键的实现⽅法
2.1 关于复合设备和组合设备
我倒不是去纠结两个概念的含义,只是想说⽬前⽐较主流的实现多媒体按键的⽅法就上⾯两种。其实多个接⼝⼀个设备的这种设备按照定义应该称之为组合设备。我下⾯讲的⽅法也是通过组合设备,即⼀个设备下多个接⼝描述符来实现的。
就是说⼀个配置描述符下可以挂多个接⼝描述符,每个接⼝做⾃⼰的事情,多媒体按键就是通过另外⼀个接⼝来实现的。
2.2 多媒体按键的实现⽅法
如果⼀个HID设备中需要实现标准键盘,⿏标,多媒体按键功能,第⼀种⽅法就是通过三个接⼝来实现。第⼆种⽅法就是只使⽤两个接⼝,第⼆个接⼝通过报告ID这么⼀个字段,来向PC机报告我这⼀包数据是⿏标的数据还是多媒体按键数据。还有第三种⽅法,就是前⾯说的复合设备,即把多个设备作为⼀个hub,这样也可以实现(这个⽅法我没有证实过)。
/* system control */
0x05,0x01,// USAGE_PAGE (Generic Desktop)
0x09,0x80,// USAGE (System Control)
0xa1,0x01,// COLLECTION (Application)
0x85,0x02,//  REPORT_ID (2)
0x15,0x01,//  LOGICAL_MINIMUM (0x1)
0x26,0xb7,0x00,//  LOGICAL_MAXIMUM (0xb7)
0x19,0x01,//  USAGE_MINIMUM (0x1)
0x29,0xb7,//  USAGE_MAXIMUM (0xb7)
0x75,0x10,//  REPORT_SIZE (16)
0x95,0x01,//  REPORT_COUNT (1)
0x81,0x00,//  INPUT (Data,Array,Abs)
0xc0,// END_COLLECTION    /* 24 */
/* consumer */
0x05,0x0c,// USAGE_PAGE (Consumer Devices)
0x09,0x01,// USAGE (Consumer Control)
0xa1,0x01,// COLLECTION (Application)
0x85,0x03,//  REPORT_ID (3)
0x15,0x01,//  LOGICAL_MINIMUM (0x1)
0x26,0x9c,0x02,//  LOGICAL_MAXIMUM (0x29c)
0x19,0x01,//  USAGE_MINIMUM (0x1)
0x2a,0x9c,0x02,//  USAGE_MAXIMUM (0x29c)
0x75,0x10,//  REPORT_SIZE (16)
0x95,0x01,//  REPORT_COUNT (1)
0x81,0x00,//  INPUT (Data,Array,Abs)
0xc0,// END_COLLECTION    /* 25 */
上⾯的代码中,仅展⽰了⼀个例⼦,使⽤Report ID来完成Consumer数据和System Control数据的区分。⽐如报告ID为2的字段表⽰这⼀帧的数据是System Control数据,如果报告ID为3,则表⽰这次的数据是Consumer数据。
额外提⼀句,如果使⽤的报告ID,那么发送给PC机的数据就需要附上报告ID这个字节,且这个ID必须在所有数据的最前⾯。
2.3 如何在键盘的⼯程上添加这个功能
1、⾸先在usb_desc.c⽂件中得定义这个多媒体的报告描述符,也有在⾥⾯包含⿏标的数据的,这个随意,只要复合HID标准即可。然后在对应的.h⽂件中也要声明这个变量。
2、修改前⾯说的配置描述符,bNumInterfaces这个字段更改为2,表⽰有两个接⼝。
tornado3、在第⼀个接⼝描述符后⾯,添加第⼆个接⼝的描述符,bInterfaceNumber这个字段改为1,这是序号为1(起始序号为0)的接⼝描述符,然后依次添加这个接⼝的HID描述符和端点描述符。
4、在usb_conf.h中添加相应的端点资源。⽤到哪个端点,就添加哪个端点。
#define BTABLE_ADDRESS      (0x00)
/* EP0  */
/* rx/tx buffer base address */
#define ENDP0_RXADDR        (0x30)
涪江#define ENDP0_TXADDR        (0x70)
/* EP1  */
/* tx buffer base address */
#define ENDP1_TXADDR        (0xB0)
/* EP2  */
/* Rx buffer base address */
#define ENDP2_RXADDR        (0xF0)
/* EP3  */
/* Tx buffer base address */
#define ENDP3_TXADDR        (0x130)
⽐如说,我这⾥⽤到了三个端点,除了端点0 (控制端点)以外,端点1⽤来发送键盘数据,端点2⽤来接收键盘上灯的数据,端点3⽤来发送多媒体数据。
重点来了
这⾥每⼀个宏定义后的值并不是可以随便定义的,如果翻看⼀下STM32的参考⼿册USB章节,发现USB模块中有512字节的缓冲区,这个缓缓区和CAN是共⽤的。但重点不在这⾥,如果我把端点0的ENDP0_RXADDR定义为0x10,那么这样USB设备接收的数据或者发送的数据就会出错。为什么呢?看下图。
包缓冲区中有⼀个叫缓冲区描述表的东西,这个东西的位置也位于包缓冲区中。它的意义就在于告诉USB设备,哪个端点的数据放置与缓冲区的哪个位置。⽽不管我们定义的端点是否为双向端点,即不管我们是否同时使⽤⼀个端点来收发数据,它的缓冲区描述表都在那⾥,都要占据8个字节。⽽且这个缓冲区描述表的位置就位于包缓冲区的开始的位置。也就是说,如果我使⽤了三个端点,那么ENDP0_RXADDR的值最⼩也得是3*8 = 0x18。⾄于你定义在0x30这个位置,只是说对于包缓冲区的 空间利⽤不充分,因为缓冲区描述表后⾯的部分是都可以⽤来作为数据缓冲区的,但如果定义的⼩了,则会造成通信异常。
5、这⾥修改好以后,在usb_prop.c⽂件中,需要定义如下⼏个函数。并在相应头⽂件中声明。

本文发布于:2024-09-21 20:23:18,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/722293.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:数据   设备   端点   描述符   键盘   缓冲区
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议