本帖最后由 Zoologist 于 2022-3-27 18:19 编辑
最近在看《圈圈教你玩USB(第一版)》,我手上的这本是作者签名版,十多年前买的。 其中提到了一个USB设备同时实现键盘鼠标功能的方案,其中的一种是:在 HID 描述符中分别报告鼠标和键盘,然后通过ReportID 对数据进行区分。于是手工编写一个代码,实现了一个USB设备下有3个键盘的功能。 代码是基于CustomHIDDevice编写的,对于 HID 设备来说,彼此之间主要差别就是HID 描述符。这里定义了三个键盘的HID描述符,
- //报告ID,(这里定义键盘报告的ID为1报告ID 0是保留的)
- 0x85, 0x01, //ReportID (1)
- ….
- //报告ID,这里定义键盘报告的ID为2(报告ID 0是保留的)
- 0x85, 0x02, //ReportID (2)
- ….
- //报告ID,这里定义键盘报告的ID为3(报告ID 0是保留的)
- 0x85, 0x03, //ReportID (3)
- 之后,主循环中有三个发送数据的部分,其中axis[0]给出每隔Report的ID,之后的8Bytes就是键盘的数据。
- //键盘1 输出一个 a
- //其中 axis[0] 是 report ID 这里为 1
- axis[0]=0x01;axis[3]=0x04;
- Device.send(axis);
- delay(20);
- axis[0]=0x01;axis[3]=0x00;
- Device.send(axis);
- delay(20);
- //键盘2 输出一个 b
- //其中 axis[0] 是 report ID 这里为 2
- axis[0]=0x02;axis[3]=0x05;
- Device.send(axis);
- delay(20);
- axis[0]=0x02;axis[3]=0x00;
- Device.send(axis);
- delay(20);
- //键盘3 输出一个 c
- //其中 axis[0] 是 report ID 这里为 3
- axis[0]=0x03;axis[3]=0x06;
- Device.send(axis);
- delay(20);
- axis[0]=0x03;axis[3]=0x00;
- Device.send(axis);
- 此外,代码中还修改了每一次发送的数据从8个改为9个(就是ReportID+8Byte键盘数据)
- bool send(uint8_t *value){
- returnHID.SendReport(0, value, 9);
- }
复制代码
每隔10 秒,电脑会收到输入的 abc 三个字符.
- #include "USB.h"
- #include "USBHID.h"
- USBHID HID;
- static const uint8_t report_descriptor[] = { // 8 axis
- //每行开始的第一字节为该条目的前缀,前缀的格式为:
- //D7~D4:bTag。D3~D2:bType;D1~D0:bSize。以下分别对每个条目注释。
-
- /************************USB键盘部分报告描述符**********************/
- /*******************************************************************/
- //这是一个全局(bType为1)条目,将用途页选择为普通桌面Generic Desktop Page(0x01)
- //后面跟一字节数据(bSize为1),后面的字节数就不注释了,
- //自己根据bSize来判断。
- 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
-
- //这是一个局部(bType为2)条目,说明接下来的集合用途用于键盘
- 0x09, 0x06, // USAGE (Keyboard)
-
- //这是一个主条目(bType为0)条目,开集合,后面跟的数据0x01表示
- //该集合是一个应用集合。它的性质在前面由用途页和用途定义为
- //普通桌面用的键盘。
- 0xa1, 0x01, // COLLECTION (Application)
-
- //报告ID,(这里定义键盘报告的ID为1报告ID 0是保留的)
- 0x85, 0x01, //Report ID (1)
-
- //这是一个全局条目,选择用途页为键盘(Keyboard/Keypad(0x07))
- 0x05, 0x07, // USAGE_PAGE (Keyboard/Keypad)
- //这是一个局部条目,说明用途的最小值为0xe0。实际上是键盘左Ctrl键。
- //具体的用途值可在HID用途表中查看。
- 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
-
- //这是一个局部条目,说明用途的最大值为0xe7。实际上是键盘右GUI键。
- 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
-
- //这是一个全局条目,说明返回的数据的逻辑值(就是我们返回的数据域的值)
- //最小为0。因为我们这里用Bit来表示一个数据域,因此最小为0,最大为1。
- 0x15, 0x00, // LOGICAL_MINIMUM (0)
-
- //这是一个全局条目,说明逻辑值最大为1。
- 0x25, 0x01, // LOGICAL_MAXIMUM (1)
-
- //这是一个全局条目,说明数据域的数量为八个。
- 0x95, 0x08, // REPORT_COUNT (8)
-
- //这是一个全局条目,说明每个数据域的长度为1个bit。
- 0x75, 0x01, // REPORT_SIZE (1)
-
- //这是一个主条目,说明有8个长度为1bit的数据域(数量和长度
- //由前面的两个全局条目所定义)用来做为输入,
- //属性为:Data,Var,Abs。Data表示这些数据可以变动,Var表示
- //这些数据域是独立的,每个域表示一个意思。Abs表示绝对值。
- //这样定义的结果就是,当某个域的值为1时,就表示对应的键按下。
- //bit0就对应着用途最小值0xe0,bit7对应着用途最大值0xe7。
- 0x81, 0x02, // INPUT (Data,Var,Abs)
-
- //这是一个全局条目,说明数据域数量为1个
- 0x95, 0x01, // REPORT_COUNT (1)
-
- //这是一个全局条目,说明每个数据域的长度为8bit。
- 0x75, 0x08, // REPORT_SIZE (8)
-
- //这是一个主条目,输入用,由前面两个全局条目可知,长度为8bit,
- //数量为1个。它的属性为常量(即返回的数据一直是0)。
- //该字节是保留字节(保留给OEM使用)。
- 0x81, 0x03, // INPUT (Cnst,Var,Abs)
-
- //这是一个全局条目。定义位域数量为6个。
- 0x95, 0x06, // REPORT_COUNT (6)
-
- //这是一个全局条目。定义每个位域长度为8bit。
- //其实这里这个条目不要也是可以的,因为在前面已经有一个定义
- //长度为8bit的全局条目了。
- 0x75, 0x08, // REPORT_SIZE (8)
-
- //这是一个全局条目,定义逻辑最小值为0。
- //同上,这里这个全局条目也是可以不要的,因为前面已经有一个
- //定义逻辑最小值为0的全局条目了。
- 0x15, 0x00, // LOGICAL_MINIMUM (0)
-
- //这是一个全局条目,定义逻辑最大值为255。
- 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
-
- //这是一个全局条目,选择用途页为键盘。
- //前面已经选择过用途页为键盘了,所以该条目不要也可以。
- 0x05, 0x07, // USAGE_PAGE (Keyboard/Keypad)
-
- //这是一个局部条目,定义用途最小值为0(0表示没有键按下)
- 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
-
- //这是一个局部条目,定义用途最大值为0x65
- 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
-
- //这是一个主条目。它说明这六个8bit的数据域是输入用的,
- //属性为:Data,Ary,Abs。Data说明数据是可以变的,Ary说明
- //这些数据域是一个数组,即每个8bit都可以表示某个键值,
- //如果按下的键太多(例如超过这里定义的长度或者键盘本身无法
- //扫描出按键情况时),则这些数据返回全1(二进制),表示按键无效。
- //Abs表示这些值是绝对值。
- 0x81, 0x00, // INPUT (Data,Ary,Abs)
- //以下为输出报告的描述
- //逻辑最小值前面已经有定义为0了,这里可以省略。
- //这是一个全局条目,说明逻辑值最大为1。
- 0x25, 0x01, // LOGICAL_MAXIMUM (1)
-
- //这是一个全局条目,说明数据域数量为5个。
- 0x95, 0x05, // REPORT_COUNT (5)
-
- //这是一个全局条目,说明数据域的长度为1bit。
- 0x75, 0x01, // REPORT_SIZE (1)
-
- //这是一个全局条目,说明使用的用途页为指示灯(LED)
- 0x05, 0x08, // USAGE_PAGE (LEDs)
-
- //这是一个局部条目,说明用途最小值为数字键盘灯。
- 0x19, 0x01, // USAGE_MINIMUM (Num Lock)
-
- //这是一个局部条目,说明用途最大值为Kana灯。
- 0x29, 0x05, // USAGE_MAXIMUM (Kana)
-
- //这是一个主条目。定义输出数据,即前面定义的5个LED。
- 0x91, 0x02, // OUTPUT (Data,Var,Abs)
-
- //这是一个全局条目。定义位域数量为1个。
- 0x95, 0x01, // REPORT_COUNT (1)
-
- //这是一个全局条目。定义位域长度为3bit。
- 0x75, 0x03, // REPORT_SIZE (3)
-
- //这是一个主条目,定义输出常量,前面用了5bit,所以这里需要
- //3个bit来凑成一字节。
- 0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
-
- //下面这个主条目用来关闭前面的集合。bSize为0,所以后面没数据。
- 0xc0, // END_COLLECTION
- /************************USB键盘部分报告描述符**********************/
- /*******************************************************************/
- //这是一个全局(bType为1)条目,将用途页选择为普通桌面Generic Desktop Page(0x01)
- //后面跟一字节数据(bSize为1),后面的字节数就不注释了,
- //自己根据bSize来判断。
- 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
-
- //这是一个局部(bType为2)条目,说明接下来的集合用途用于键盘
- 0x09, 0x06, // USAGE (Keyboard)
-
- //这是一个主条目(bType为0)条目,开集合,后面跟的数据0x01表示
- //该集合是一个应用集合。它的性质在前面由用途页和用途定义为
- //普通桌面用的键盘。
- 0xa1, 0x01, // COLLECTION (Application)
-
- //报告ID,这里定义键盘报告的ID为2(报告ID 0是保留的)
- 0x85, 0x02, //Report ID (2)
-
- //这是一个全局条目,选择用途页为键盘(Keyboard/Keypad(0x07))
- 0x05, 0x07, // USAGE_PAGE (Keyboard/Keypad)
- //这是一个局部条目,说明用途的最小值为0xe0。实际上是键盘左Ctrl键。
- //具体的用途值可在HID用途表中查看。
- 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
-
- //这是一个局部条目,说明用途的最大值为0xe7。实际上是键盘右GUI键。
- 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
-
- //这是一个全局条目,说明返回的数据的逻辑值(就是我们返回的数据域的值)
- //最小为0。因为我们这里用Bit来表示一个数据域,因此最小为0,最大为1。
- 0x15, 0x00, // LOGICAL_MINIMUM (0)
-
- //这是一个全局条目,说明逻辑值最大为1。
- 0x25, 0x01, // LOGICAL_MAXIMUM (1)
-
- //这是一个全局条目,说明数据域的数量为八个。
- 0x95, 0x08, // REPORT_COUNT (8)
-
- //这是一个全局条目,说明每个数据域的长度为1个bit。
- 0x75, 0x01, // REPORT_SIZE (1)
-
- //这是一个主条目,说明有8个长度为1bit的数据域(数量和长度
- //由前面的两个全局条目所定义)用来做为输入,
- //属性为:Data,Var,Abs。Data表示这些数据可以变动,Var表示
- //这些数据域是独立的,每个域表示一个意思。Abs表示绝对值。
- //这样定义的结果就是,当某个域的值为1时,就表示对应的键按下。
- //bit0就对应着用途最小值0xe0,bit7对应着用途最大值0xe7。
- 0x81, 0x02, // INPUT (Data,Var,Abs)
-
- //这是一个全局条目,说明数据域数量为1个
- 0x95, 0x01, // REPORT_COUNT (1)
-
- //这是一个全局条目,说明每个数据域的长度为8bit。
- 0x75, 0x08, // REPORT_SIZE (8)
-
- //这是一个主条目,输入用,由前面两个全局条目可知,长度为8bit,
- //数量为1个。它的属性为常量(即返回的数据一直是0)。
- //该字节是保留字节(保留给OEM使用)。
- 0x81, 0x03, // INPUT (Cnst,Var,Abs)
-
- //这是一个全局条目。定义位域数量为6个。
- 0x95, 0x06, // REPORT_COUNT (6)
-
- //这是一个全局条目。定义每个位域长度为8bit。
- //其实这里这个条目不要也是可以的,因为在前面已经有一个定义
- //长度为8bit的全局条目了。
- 0x75, 0x08, // REPORT_SIZE (8)
-
- //这是一个全局条目,定义逻辑最小值为0。
- //同上,这里这个全局条目也是可以不要的,因为前面已经有一个
- //定义逻辑最小值为0的全局条目了。
- 0x15, 0x00, // LOGICAL_MINIMUM (0)
-
- //这是一个全局条目,定义逻辑最大值为255。
- 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
-
- //这是一个全局条目,选择用途页为键盘。
- //前面已经选择过用途页为键盘了,所以该条目不要也可以。
- 0x05, 0x07, // USAGE_PAGE (Keyboard/Keypad)
-
- //这是一个局部条目,定义用途最小值为0(0表示没有键按下)
- 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
-
- //这是一个局部条目,定义用途最大值为0x65
- 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
-
- //这是一个主条目。它说明这六个8bit的数据域是输入用的,
- //属性为:Data,Ary,Abs。Data说明数据是可以变的,Ary说明
- //这些数据域是一个数组,即每个8bit都可以表示某个键值,
- //如果按下的键太多(例如超过这里定义的长度或者键盘本身无法
- //扫描出按键情况时),则这些数据返回全1(二进制),表示按键无效。
- //Abs表示这些值是绝对值。
- 0x81, 0x00, // INPUT (Data,Ary,Abs)
- //以下为输出报告的描述
- //逻辑最小值前面已经有定义为0了,这里可以省略。
- //这是一个全局条目,说明逻辑值最大为1。
- 0x25, 0x01, // LOGICAL_MAXIMUM (1)
-
- //这是一个全局条目,说明数据域数量为5个。
- 0x95, 0x05, // REPORT_COUNT (5)
-
- //这是一个全局条目,说明数据域的长度为1bit。
- 0x75, 0x01, // REPORT_SIZE (1)
-
- //这是一个全局条目,说明使用的用途页为指示灯(LED)
- 0x05, 0x08, // USAGE_PAGE (LEDs)
-
- //这是一个局部条目,说明用途最小值为数字键盘灯。
- 0x19, 0x01, // USAGE_MINIMUM (Num Lock)
-
- //这是一个局部条目,说明用途最大值为Kana灯。
- 0x29, 0x05, // USAGE_MAXIMUM (Kana)
-
- //这是一个主条目。定义输出数据,即前面定义的5个LED。
- 0x91, 0x02, // OUTPUT (Data,Var,Abs)
-
- //这是一个全局条目。定义位域数量为1个。
- 0x95, 0x01, // REPORT_COUNT (1)
-
- //这是一个全局条目。定义位域长度为3bit。
- 0x75, 0x03, // REPORT_SIZE (3)
-
- //这是一个主条目,定义输出常量,前面用了5bit,所以这里需要
- //3个bit来凑成一字节。
- 0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
-
- //下面这个主条目用来关闭前面的集合。bSize为0,所以后面没数据。
- 0xc0, // END_COLLECTION
- /************************USB键盘部分报告描述符**********************/
- /*******************************************************************/
- //这是一个全局(bType为1)条目,将用途页选择为普通桌面Generic Desktop Page(0x01)
- //后面跟一字节数据(bSize为1),后面的字节数就不注释了,
- //自己根据bSize来判断。
- 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
-
- //这是一个局部(bType为2)条目,说明接下来的集合用途用于键盘
- 0x09, 0x06, // USAGE (Keyboard)
-
- //这是一个主条目(bType为0)条目,开集合,后面跟的数据0x01表示
- //该集合是一个应用集合。它的性质在前面由用途页和用途定义为
- //普通桌面用的键盘。
- 0xa1, 0x01, // COLLECTION (Application)
-
- //报告ID,这里定义键盘报告的ID为3(报告ID 0是保留的)
- 0x85, 0x03, //Report ID (3)
-
- //这是一个全局条目,选择用途页为键盘(Keyboard/Keypad(0x07))
- 0x05, 0x07, // USAGE_PAGE (Keyboard/Keypad)
- //这是一个局部条目,说明用途的最小值为0xe0。实际上是键盘左Ctrl键。
- //具体的用途值可在HID用途表中查看。
- 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
-
- //这是一个局部条目,说明用途的最大值为0xe7。实际上是键盘右GUI键。
- 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
-
- //这是一个全局条目,说明返回的数据的逻辑值(就是我们返回的数据域的值)
- //最小为0。因为我们这里用Bit来表示一个数据域,因此最小为0,最大为1。
- 0x15, 0x00, // LOGICAL_MINIMUM (0)
-
- //这是一个全局条目,说明逻辑值最大为1。
- 0x25, 0x01, // LOGICAL_MAXIMUM (1)
-
- //这是一个全局条目,说明数据域的数量为八个。
- 0x95, 0x08, // REPORT_COUNT (8)
-
- //这是一个全局条目,说明每个数据域的长度为1个bit。
- 0x75, 0x01, // REPORT_SIZE (1)
-
- //这是一个主条目,说明有8个长度为1bit的数据域(数量和长度
- //由前面的两个全局条目所定义)用来做为输入,
- //属性为:Data,Var,Abs。Data表示这些数据可以变动,Var表示
- //这些数据域是独立的,每个域表示一个意思。Abs表示绝对值。
- //这样定义的结果就是,当某个域的值为1时,就表示对应的键按下。
- //bit0就对应着用途最小值0xe0,bit7对应着用途最大值0xe7。
- 0x81, 0x02, // INPUT (Data,Var,Abs)
-
- //这是一个全局条目,说明数据域数量为1个
- 0x95, 0x01, // REPORT_COUNT (1)
-
- //这是一个全局条目,说明每个数据域的长度为8bit。
- 0x75, 0x08, // REPORT_SIZE (8)
-
- //这是一个主条目,输入用,由前面两个全局条目可知,长度为8bit,
- //数量为1个。它的属性为常量(即返回的数据一直是0)。
- //该字节是保留字节(保留给OEM使用)。
- 0x81, 0x03, // INPUT (Cnst,Var,Abs)
-
- //这是一个全局条目。定义位域数量为6个。
- 0x95, 0x06, // REPORT_COUNT (6)
-
- //这是一个全局条目。定义每个位域长度为8bit。
- //其实这里这个条目不要也是可以的,因为在前面已经有一个定义
- //长度为8bit的全局条目了。
- 0x75, 0x08, // REPORT_SIZE (8)
-
- //这是一个全局条目,定义逻辑最小值为0。
- //同上,这里这个全局条目也是可以不要的,因为前面已经有一个
- //定义逻辑最小值为0的全局条目了。
- 0x15, 0x00, // LOGICAL_MINIMUM (0)
-
- //这是一个全局条目,定义逻辑最大值为255。
- 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
-
- //这是一个全局条目,选择用途页为键盘。
- //前面已经选择过用途页为键盘了,所以该条目不要也可以。
- 0x05, 0x07, // USAGE_PAGE (Keyboard/Keypad)
-
- //这是一个局部条目,定义用途最小值为0(0表示没有键按下)
- 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
-
- //这是一个局部条目,定义用途最大值为0x65
- 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
-
- //这是一个主条目。它说明这六个8bit的数据域是输入用的,
- //属性为:Data,Ary,Abs。Data说明数据是可以变的,Ary说明
- //这些数据域是一个数组,即每个8bit都可以表示某个键值,
- //如果按下的键太多(例如超过这里定义的长度或者键盘本身无法
- //扫描出按键情况时),则这些数据返回全1(二进制),表示按键无效。
- //Abs表示这些值是绝对值。
- 0x81, 0x00, // INPUT (Data,Ary,Abs)
- //以下为输出报告的描述
- //逻辑最小值前面已经有定义为0了,这里可以省略。
- //这是一个全局条目,说明逻辑值最大为1。
- 0x25, 0x01, // LOGICAL_MAXIMUM (1)
-
- //这是一个全局条目,说明数据域数量为5个。
- 0x95, 0x05, // REPORT_COUNT (5)
-
- //这是一个全局条目,说明数据域的长度为1bit。
- 0x75, 0x01, // REPORT_SIZE (1)
-
- //这是一个全局条目,说明使用的用途页为指示灯(LED)
- 0x05, 0x08, // USAGE_PAGE (LEDs)
-
- //这是一个局部条目,说明用途最小值为数字键盘灯。
- 0x19, 0x01, // USAGE_MINIMUM (Num Lock)
-
- //这是一个局部条目,说明用途最大值为Kana灯。
- 0x29, 0x05, // USAGE_MAXIMUM (Kana)
-
- //这是一个主条目。定义输出数据,即前面定义的5个LED。
- 0x91, 0x02, // OUTPUT (Data,Var,Abs)
-
- //这是一个全局条目。定义位域数量为1个。
- 0x95, 0x01, // REPORT_COUNT (1)
-
- //这是一个全局条目。定义位域长度为3bit。
- 0x75, 0x03, // REPORT_SIZE (3)
-
- //这是一个主条目,定义输出常量,前面用了5bit,所以这里需要
- //3个bit来凑成一字节。
- 0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
-
- //下面这个主条目用来关闭前面的集合。bSize为0,所以后面没数据。
- 0xc0, // END_COLLECTION
- };
- class CustomHIDDevice: public USBHIDDevice {
- public:
- CustomHIDDevice(void){
- static bool initialized = false;
- if(!initialized){
- initialized = true;
- HID.addDevice(this, sizeof(report_descriptor));
- }
- }
-
- void begin(void){
- HID.begin();
- }
-
- uint16_t _onGetDescriptor(uint8_t* buffer){
- memcpy(buffer, report_descriptor, sizeof(report_descriptor));
- return sizeof(report_descriptor);
- }
- bool send(uint8_t * value){
- return HID.SendReport(0, value, 9);
- }
- };
- CustomHIDDevice Device;
- const int buttonPin = 0;
- int previousButtonState = HIGH;
- uint8_t axis[9];
- void setup() {
- Serial.begin(115200);
- Serial.setDebugOutput(true);
- pinMode(buttonPin, INPUT_PULLUP);
- Device.begin();
- USB.begin();
- }
- void loop() {
- int buttonState = digitalRead(buttonPin);
- if (HID.ready() && buttonState != previousButtonState) {
- previousButtonState = buttonState;
- if (buttonState == LOW) {
- Serial.println("Button Pressed");
- axis[0] = random() & 0xFF;
- Device.send(axis);
- } else {
- Serial.println("Button Released");
- }
- delay(100);
- }
- //每隔10秒
- delay(10000);
- //键盘1 输出一个 a
- //其中 axis[0] 是 report ID 这里为 1
- axis[0]=0x01;axis[3]=0x04;
- Device.send(axis);
- delay(20);
- axis[0]=0x01;axis[3]=0x00;
- Device.send(axis);
-
- delay(20);
- //键盘2 输出一个 b
- //其中 axis[0] 是 report ID 这里为 2
- axis[0]=0x02;axis[3]=0x05;
- Device.send(axis);
- delay(20);
- axis[0]=0x02;axis[3]=0x00;
- Device.send(axis);
- delay(20);
- //键盘3 输出一个 c
- //其中 axis[0] 是 report ID 这里为 3
- axis[0]=0x03;axis[3]=0x06;
- Device.send(axis);
- delay(20);
- axis[0]=0x03;axis[3]=0x00;
- Device.send(axis);
- }
复制代码
对于 PS2 键盘来说是“全键无冲突的”,意思是可以按下任意多的按键;对于标准的USB键盘来说,最多只能同时按下6个按键。这是因为 PS2 是分开发送按下和抬起消息的;而标准USB键盘,有8Bytes的数据,其中第一个byte用来指示 alt ctrl 等等,第二个Byte始终为0,接下来剩下6Byte,需要放置按下键的信息,如果抬起还需要用0来指示。这样只能支持同时按下6个键。上面提到的这个方法可以用来扩展USB键盘,比如,声明3个键盘就可以支持 6*3=18键无冲突。
完整代码:
|