- 打卡等级:以坛为家II
- 打卡总天数:456
- 最近打卡:2025-05-02 08:26:53
已绑定手机
金牌会员
机长
- 积分
- 1691
|
本帖最后由 hsrzq 于 2023-5-31 11:37 编辑
我们都知道,一个键盘报文最大8个字节,其中:
1. 第0个字节是Shift、Ctrl、Alt、Win这些修饰键,左右两组正好8位一个字节
2. 第1个字节功能保留,仅做填充用
3. 剩下6个字节每个字节各表示一个键码
因此,除开那几个修饰键外,其它的的键最多能表示6个。这在大多数时候都够用了,但是如果不够用了呢?甚至需要全键无冲呢?
实现思路一
既然一个键盘最多只能报告6个按键,那我多模拟几个键盘出来不就好了?
可以参考 https://www.stcaimcu.com/forum.p ... id=572&pid=3705 这个代码,将键盘HID多报告几次即可。不过要注意的是,STC8/STC32只有5个端点(端点0不能用),因此最多只能报告5个键盘,最多也只能做到30键无冲。
实现思路二
既然最多支持6个键的原因是REPORT_COUNT为6,那我改一下这个值不就行了?
char code KEYBOARDREPORTDESC[65] =
{
0x05,0x01, //USAGE_PAGE(Generic Desktop);
0x09,0x06, //USAGE(Keyboard);
0xa1,0x01, //COLLECTION(Application);
0x05,0x07, // USAGE_PAGE(Keyboard);
0x19,0xe0, // USAGE_MINIMUM(224);
0x29,0xe7, // USAGE_MAXIMUM(255);
0x15,0x00, // LOGICAL_MINIMUM(0);
0x25,0x01, // LOGICAL_MAXIMUM(1);
0x75,0x01, // REPORT_SIZE(1);
0x95,0x08, // REPORT_COUNT(8);
0x81,0x02, // INPUT(Data,Variable,Absolute);
0x75,0x08, // REPORT_SIZE(8);
0x95,0x01, // REPORT_COUNT(1);
0x81,0x01, // INPUT(Constant);
0x19,0x00, // USAGE_MINIMUM(0);
0x29,0x65, // USAGE_MAXIMUM(101);
0x15,0x00, // LOGICAL_MINIMUM(0);
0x25,0x65, // LOGICAL_MAXIMUM(101);
0x75,0x08, // REPORT_SIZE(8);
0x95,0x5E, // REPORT_COUNT(94); 最多94个键无冲同时按
0x81,0x00, // INPUT(Data,Array);
0x05,0x08, // USAGE_PAGE(LEDs);
0x19,0x01, // USAGE_MINIMUM(1);
0x29,0x03, // USAGE_MAXIMUM(3);
0x15,0x00, // LOGICAL_MINIMUM(0);
0x25,0x01, // LOGICAL_MAXIMUM(1);
0x75,0x01, // REPORT_SIZE(1);
0x95,0x03, // REPORT_COUNT(3);
0x91,0x02, // OUTPUT(Data,Variable,Absolute);
0x75,0x05, // REPORT_SIZE(5);
0x95,0x01, // REPORT_COUNT(1);
0x91,0x01, // OUTPUT(Constant);
0xc0, //END_COLLECTION;
};
不过要注意的是,STC8/STC32的1、2、3IN端点只有64Bytes,因此只能使用4、5端点,并且还需要修改一下配置描述符的端点描述部分。
0x09, //bLength(9);
0x04, //bDescriptorType(Interface);
0x00, //bInterfaceNumber(0);
0x00, //bAlternateSetting(0);
0x02, //bNumEndpoints(2);
0x03, //bInterfaceClass(HID);
0x00, //bInterfaceSubClass(No Boot); 非标准键盘,不支持BIOS使用
0x01, //bInterfaceProtocol(Keyboard);
0x00, //iInterface(0);
0x09, //bLength(9);
0x21, //bDescriptorType(HID);
0x01,0x01, //bcdHID(1.01);
0x00, //bCountryCode(0);
0x01, //bNumDescriptors(1);
0x22, //bDescriptorType(HID Report);
0x41,0x00, //wDescriptorLength(65);
0x07, //bLength(7);
0x05, //bDescriptorType(Endpoint);
0x84, //bEndpointAddress(EndPoint4 as IN); 使用端点4
0x03, //bmAttributes(Interrupt);
0x60,0x00, //wMaxPacketSize(96); 最大96字节(修饰符1字节+填充1字节+键码94字节)
0x0a, //bInterval(10ms);
0x07, //bLength(7);
0x05, //bDescriptorType(Endpoint);
0x04, //bEndpointAddress(EndPoint1 as OUT);使用端点4
0x03, //bmAttributes(Interrupt);
0x01,0x00, //wMaxPacketSize(1);
0x0a, //bInterval(10ms);
实现思路三
默认键盘描述符的第一个字节,一个字节就可以表示8个键,那么我们是不是也可以模仿一下,用少量的字节表示大量的键呢?
// 部分配置描述符
0x09, //bLength(9);
0x04, //bDescriptorType(Interface);
0x00, //bInterfaceNumber(0);
0x00, //bAlternateSetting(0);
0x02, //bNumEndpoints(2);
0x03, //bInterfaceClass(HID);
0x00, //bInterfaceSubClass(No Boot); 非标准键盘,不支持BIOS使用
0x01, //bInterfaceProtocol(Keyboard);
0x00, //iInterface(0);
0x09, //bLength(9);
0x21, //bDescriptorType(HID);
0x01,0x01, //bcdHID(1.01);
0x00, //bCountryCode(0);
0x01, //bNumDescriptors(1);
0x22, //bDescriptorType(HID Report);
0x41,0x00, //wDescriptorLength(65);
0x07, //bLength(7);
0x05, //bDescriptorType(Endpoint);
0x81, //bEndpointAddress(EndPoint1 as IN);
0x03, //bmAttributes(Interrupt);
0x16,0x00, //wMaxPacketSize(16); 最大16字节(修饰符1字节 + 普通键15字节/120位)
0x0a, //bInterval(10ms);
0x07, //bLength(7);
0x05, //bDescriptorType(Endpoint);
0x01, //bEndpointAddress(EndPoint1 as OUT);
0x03, //bmAttributes(Interrupt);
0x01,0x00, //wMaxPacketSize(1);
0x0a, //bInterval(10ms);
键盘报告描述符
char code KEYBOARDREPORTDESC[65] =
{
0x05,0x01, //USAGE_PAGE(Generic Desktop);
0x09,0x06, //USAGE(Keyboard);
0xa1,0x01, //COLLECTION(Application);
0x05,0x07, // USAGE_PAGE(Keyboard);
// 修饰键
0x19,0xe0, // USAGE_MINIMUM(224);
0x29,0xe7, // USAGE_MAXIMUM(231);
0x15,0x00, // LOGICAL_MINIMUM(0);
0x25,0x01, // LOGICAL_MAXIMUM(1);
0x75,0x08, // REPORT_SIZE(8);
0x95,0x01, // REPORT_COUNT(1);
0x81,0x02, // INPUT(Data,Variable,Absolute);
// 第一组
0x19,0x00, // USAGE_MINIMUM(0);
0x29,0x66, // USAGE_MAXIMUM(102);
0x15,0x00, // LOGICAL_MINIMUM(0);
0x25,0x01, // LOGICAL_MAXIMUM(1);
0x75,0x67, // REPORT_SIZE(103);
0x95,0x06, // REPORT_COUNT(1);
0x81,0x02, // INPUT(Data,Variable,Absolute);
// 第二组
0x19,0x74, // USAGE_MINIMUM(116);
0x29,0x84, // USAGE_MAXIMUM(132);
0x15,0x00, // LOGICAL_MINIMUM(0);
0x25,0x01, // LOGICAL_MAXIMUM(1);
0x75,0x11, // REPORT_SIZE(17);
0x95,0x06, // REPORT_COUNT(1);
0x81,0x02, // INPUT(Data,Variable,Absolute);
0x05,0x08, // USAGE_PAGE(LEDs);
0x19,0x01, // USAGE_MINIMUM(1);
0x29,0x03, // USAGE_MAXIMUM(3);
0x15,0x00, // LOGICAL_MINIMUM(0);
0x25,0x01, // LOGICAL_MAXIMUM(1);
0x75,0x01, // REPORT_SIZE(1);
0x95,0x03, // REPORT_COUNT(3);
0x91,0x02, // OUTPUT(Data,Variable,Absolute);
0x75,0x05, // REPORT_SIZE(5);
0x95,0x01, // REPORT_COUNT(1);
0x91,0x01, // OUTPUT(Constant);
0xc0, //END_COLLECTION;
};
Logical Minimum (0)/Logical Maximum (1)表明这是一个OOC(On/Off Control)类型的条目,即每一个bit可表示一个键的开(1)或关;
第一组Usage Minimum (0)/Usage Maximum (102)表明每一位代表的是哪个真正的键值,具体键值表可以参考usb官方文档https://usb.org/sites/default/files/hut1_4.pdf 的第10节《Keyboard/Keypad Page (0x07)》。
第二组Usage Minimum (116)/Usage Maximum (132)功能上与第一组完全相同,是另一组表明剩余位代表的真正键值。
由于第一组只有103个键,所以另找了17个键将报告补全成16个字节(128位,修饰符8位+第一组103位+第二组17位)
当USB主机端收到以下报告时:
0b10010010 0b00110000 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0b00010000
就可以解析出来
0b10100001 → 左Ctrl,右Shift,右Alt
0b00110000 → Keyboard a/A,Keyboard b/B
0b00010000 → Keyboard Volume Down
已知问题
思路二和思路三都修改了标准的键盘描述符,这会造成只有在进入操作系统后才能使用
|
|