找回密码
 立即注册
查看: 595|回复: 2

一种STC-USB模拟键盘全键无冲的实现思路

[复制链接]
  • TA的每日心情
    奋斗
    昨天 11:10
  • 签到天数: 133 天

    [LV.7]常住居民III

    8

    主题

    79

    回帖

    1092

    积分

    金牌会员

    机长

    积分
    1092
    发表于 2023-5-31 11:11:20 | 显示全部楼层 |阅读模式
    本帖最后由 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



    已知问题
    思路二和思路三都修改了标准的键盘描述符,这会造成只有在进入操作系统后才能使用
    业余撸代码,专业开飞机
    回复 送花

    使用道具 举报

  • TA的每日心情
    慵懒
    昨天 08:37
  • 签到天数: 95 天

    [LV.6]常住居民II

    14

    主题

    616

    回帖

    2337

    积分

    超级版主

    积分
    2337
    QQ
    发表于 2023-5-31 15:43:16 | 显示全部楼层
    涨知识了!感谢分享~
    www.STCAI.com
    微信&手机:18106296591
    QQ:3133693787
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    昨天 12:42
  • 签到天数: 2 天

    [LV.1]初来乍到

    1

    主题

    10

    回帖

    211

    积分

    中级会员

    积分
    211
    发表于 2023-10-20 00:51:25 | 显示全部楼层
    楼主思路清奇,膜拜中。。。。。
    回复 支持 反对 送花

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

    GMT+8, 2024-5-18 07:56 , Processed in 0.058381 second(s), 38 queries .

    Powered by Discuz! X3.5

    © 2001-2024 Discuz! Team.

    快速回复 返回顶部 返回列表