ESP32S2 一个设备多个键盘的实现-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 1015|回复: 0

ESP32S2 一个设备多个键盘的实现

[复制链接]
发表于 2022-3-14 13:21 | 显示全部楼层 |阅读模式
本帖最后由 Zoologist 于 2022-3-27 18:19 编辑

最近在看《圈圈教你玩USB(第一版)》,我手上的这本是作者签名版,十多年前买的。
2014373073.jpg 1285252056.jpg
其中提到了一个USB设备同时实现键盘鼠标功能的方案,其中的一种是:在 HID 描述符中分别报告鼠标和键盘,然后通过ReportID 对数据进行区分。于是手工编写一个代码,实现了一个USB设备下有3个键盘的功能。
代码是基于CustomHIDDevice编写的,对于 HID 设备来说,彼此之间主要差别就是HID 描述符。这里定义了三个键盘的HID描述符,

  1. //报告ID,(这里定义键盘报告的ID为1报告ID 0是保留的)
  2. 0x85, 0x01, //ReportID (1)
  3. ….
  4. //报告ID,这里定义键盘报告的ID为2(报告ID 0是保留的)
  5. 0x85, 0x02, //ReportID (2)
  6. ….
  7. //报告ID,这里定义键盘报告的ID为3(报告ID 0是保留的)
  8. 0x85, 0x03, //ReportID (3)
  9. 之后,主循环中有三个发送数据的部分,其中axis[0]给出每隔Report的ID,之后的8Bytes就是键盘的数据。
  10.   //键盘1 输出一个 a
  11.   //其中 axis[0] 是 report ID 这里为 1
  12. axis[0]=0x01;axis[3]=0x04;
  13.   Device.send(axis);
  14.   delay(20);
  15. axis[0]=0x01;axis[3]=0x00;
  16.   Device.send(axis);


  17.   delay(20);
  18.   //键盘2 输出一个 b
  19.   //其中 axis[0] 是 report ID 这里为 2
  20. axis[0]=0x02;axis[3]=0x05;
  21.   Device.send(axis);
  22.   delay(20);
  23. axis[0]=0x02;axis[3]=0x00;
  24.   Device.send(axis);

  25.   delay(20);
  26.   //键盘3 输出一个 c
  27.   //其中 axis[0] 是 report ID 这里为 3
  28. axis[0]=0x03;axis[3]=0x06;
  29.   Device.send(axis);
  30.   delay(20);
  31. axis[0]=0x03;axis[3]=0x00;
  32.   Device.send(axis);
  33. 此外,代码中还修改了每一次发送的数据从8个改为9个(就是ReportID+8Byte键盘数据)
  34.   bool send(uint8_t *value){
  35.     returnHID.SendReport(0, value, 9);
  36.   }
复制代码

每隔10秒,电脑会收到输入的 abc 三个字符.

  1. #include "USB.h"
  2. #include "USBHID.h"
  3. USBHID HID;

  4. static const uint8_t report_descriptor[] = { // 8 axis
  5. //每行开始的第一字节为该条目的前缀,前缀的格式为:
  6. //D7~D4:bTag。D3~D2:bType;D1~D0:bSize。以下分别对每个条目注释。

  7. /************************USB键盘部分报告描述符**********************/
  8. /*******************************************************************/
  9. //这是一个全局(bType为1)条目,将用途页选择为普通桌面Generic Desktop Page(0x01)
  10. //后面跟一字节数据(bSize为1),后面的字节数就不注释了,
  11. //自己根据bSize来判断。
  12. 0x05, 0x01, // USAGE_PAGE (Generic Desktop)

  13. //这是一个局部(bType为2)条目,说明接下来的集合用途用于键盘
  14. 0x09, 0x06, // USAGE (Keyboard)

  15. //这是一个主条目(bType为0)条目,开集合,后面跟的数据0x01表示
  16. //该集合是一个应用集合。它的性质在前面由用途页和用途定义为
  17. //普通桌面用的键盘。
  18. 0xa1, 0x01, // COLLECTION (Application)

  19. //报告ID,(这里定义键盘报告的ID为1报告ID 0是保留的)
  20. 0x85, 0x01, //Report ID (1)

  21. //这是一个全局条目,选择用途页为键盘(Keyboard/Keypad(0x07))
  22. 0x05, 0x07, //     USAGE_PAGE (Keyboard/Keypad)

  23. //这是一个局部条目,说明用途的最小值为0xe0。实际上是键盘左Ctrl键。
  24. //具体的用途值可在HID用途表中查看。
  25. 0x19, 0xe0, //     USAGE_MINIMUM (Keyboard LeftControl)

  26. //这是一个局部条目,说明用途的最大值为0xe7。实际上是键盘右GUI键。
  27. 0x29, 0xe7, //     USAGE_MAXIMUM (Keyboard Right GUI)

  28. //这是一个全局条目,说明返回的数据的逻辑值(就是我们返回的数据域的值)
  29. //最小为0。因为我们这里用Bit来表示一个数据域,因此最小为0,最大为1。
  30. 0x15, 0x00, //     LOGICAL_MINIMUM (0)

  31. //这是一个全局条目,说明逻辑值最大为1。
  32. 0x25, 0x01, //     LOGICAL_MAXIMUM (1)

  33. //这是一个全局条目,说明数据域的数量为八个。
  34. 0x95, 0x08, //     REPORT_COUNT (8)

  35. //这是一个全局条目,说明每个数据域的长度为1个bit。
  36. 0x75, 0x01, //     REPORT_SIZE (1)

  37. //这是一个主条目,说明有8个长度为1bit的数据域(数量和长度
  38. //由前面的两个全局条目所定义)用来做为输入,
  39. //属性为:Data,Var,Abs。Data表示这些数据可以变动,Var表示
  40. //这些数据域是独立的,每个域表示一个意思。Abs表示绝对值。
  41. //这样定义的结果就是,当某个域的值为1时,就表示对应的键按下。
  42. //bit0就对应着用途最小值0xe0,bit7对应着用途最大值0xe7。
  43. 0x81, 0x02, //     INPUT (Data,Var,Abs)

  44. //这是一个全局条目,说明数据域数量为1个
  45. 0x95, 0x01, //     REPORT_COUNT (1)

  46. //这是一个全局条目,说明每个数据域的长度为8bit。
  47. 0x75, 0x08, //     REPORT_SIZE (8)

  48. //这是一个主条目,输入用,由前面两个全局条目可知,长度为8bit,
  49. //数量为1个。它的属性为常量(即返回的数据一直是0)。
  50. //该字节是保留字节(保留给OEM使用)。
  51. 0x81, 0x03, //     INPUT (Cnst,Var,Abs)

  52. //这是一个全局条目。定义位域数量为6个。
  53. 0x95, 0x06, //   REPORT_COUNT (6)

  54. //这是一个全局条目。定义每个位域长度为8bit。
  55. //其实这里这个条目不要也是可以的,因为在前面已经有一个定义
  56. //长度为8bit的全局条目了。
  57. 0x75, 0x08, //   REPORT_SIZE (8)

  58. //这是一个全局条目,定义逻辑最小值为0。
  59. //同上,这里这个全局条目也是可以不要的,因为前面已经有一个
  60. //定义逻辑最小值为0的全局条目了。
  61. 0x15, 0x00, //   LOGICAL_MINIMUM (0)

  62. //这是一个全局条目,定义逻辑最大值为255。
  63. 0x25, 0xFF, //   LOGICAL_MAXIMUM (255)

  64. //这是一个全局条目,选择用途页为键盘。
  65. //前面已经选择过用途页为键盘了,所以该条目不要也可以。
  66. 0x05, 0x07, //   USAGE_PAGE (Keyboard/Keypad)

  67. //这是一个局部条目,定义用途最小值为0(0表示没有键按下)
  68. 0x19, 0x00, //   USAGE_MINIMUM (Reserved (no event indicated))

  69. //这是一个局部条目,定义用途最大值为0x65
  70. 0x29, 0x65, //   USAGE_MAXIMUM (Keyboard Application)

  71. //这是一个主条目。它说明这六个8bit的数据域是输入用的,
  72. //属性为:Data,Ary,Abs。Data说明数据是可以变的,Ary说明
  73. //这些数据域是一个数组,即每个8bit都可以表示某个键值,
  74. //如果按下的键太多(例如超过这里定义的长度或者键盘本身无法
  75. //扫描出按键情况时),则这些数据返回全1(二进制),表示按键无效。
  76. //Abs表示这些值是绝对值。
  77. 0x81, 0x00, //     INPUT (Data,Ary,Abs)

  78. //以下为输出报告的描述
  79. //逻辑最小值前面已经有定义为0了,这里可以省略。
  80. //这是一个全局条目,说明逻辑值最大为1。
  81. 0x25, 0x01, //     LOGICAL_MAXIMUM (1)

  82. //这是一个全局条目,说明数据域数量为5个。
  83. 0x95, 0x05, //   REPORT_COUNT (5)

  84. //这是一个全局条目,说明数据域的长度为1bit。
  85. 0x75, 0x01, //   REPORT_SIZE (1)

  86. //这是一个全局条目,说明使用的用途页为指示灯(LED)
  87. 0x05, 0x08, //   USAGE_PAGE (LEDs)

  88. //这是一个局部条目,说明用途最小值为数字键盘灯。
  89. 0x19, 0x01, //   USAGE_MINIMUM (Num Lock)

  90. //这是一个局部条目,说明用途最大值为Kana灯。
  91. 0x29, 0x05, //   USAGE_MAXIMUM (Kana)

  92. //这是一个主条目。定义输出数据,即前面定义的5个LED。
  93. 0x91, 0x02, //   OUTPUT (Data,Var,Abs)

  94. //这是一个全局条目。定义位域数量为1个。
  95. 0x95, 0x01, //   REPORT_COUNT (1)

  96. //这是一个全局条目。定义位域长度为3bit。
  97. 0x75, 0x03, //   REPORT_SIZE (3)

  98. //这是一个主条目,定义输出常量,前面用了5bit,所以这里需要
  99. //3个bit来凑成一字节。
  100. 0x91, 0x03, //   OUTPUT (Cnst,Var,Abs)

  101. //下面这个主条目用来关闭前面的集合。bSize为0,所以后面没数据。
  102. 0xc0,        // END_COLLECTION

  103. /************************USB键盘部分报告描述符**********************/
  104. /*******************************************************************/
  105. //这是一个全局(bType为1)条目,将用途页选择为普通桌面Generic Desktop Page(0x01)
  106. //后面跟一字节数据(bSize为1),后面的字节数就不注释了,
  107. //自己根据bSize来判断。
  108. 0x05, 0x01, // USAGE_PAGE (Generic Desktop)

  109. //这是一个局部(bType为2)条目,说明接下来的集合用途用于键盘
  110. 0x09, 0x06, // USAGE (Keyboard)

  111. //这是一个主条目(bType为0)条目,开集合,后面跟的数据0x01表示
  112. //该集合是一个应用集合。它的性质在前面由用途页和用途定义为
  113. //普通桌面用的键盘。
  114. 0xa1, 0x01, // COLLECTION (Application)

  115. //报告ID,这里定义键盘报告的ID为2(报告ID 0是保留的)
  116. 0x85, 0x02, //Report ID (2)

  117. //这是一个全局条目,选择用途页为键盘(Keyboard/Keypad(0x07))
  118. 0x05, 0x07, //     USAGE_PAGE (Keyboard/Keypad)

  119. //这是一个局部条目,说明用途的最小值为0xe0。实际上是键盘左Ctrl键。
  120. //具体的用途值可在HID用途表中查看。
  121. 0x19, 0xe0, //     USAGE_MINIMUM (Keyboard LeftControl)

  122. //这是一个局部条目,说明用途的最大值为0xe7。实际上是键盘右GUI键。
  123. 0x29, 0xe7, //     USAGE_MAXIMUM (Keyboard Right GUI)

  124. //这是一个全局条目,说明返回的数据的逻辑值(就是我们返回的数据域的值)
  125. //最小为0。因为我们这里用Bit来表示一个数据域,因此最小为0,最大为1。
  126. 0x15, 0x00, //     LOGICAL_MINIMUM (0)

  127. //这是一个全局条目,说明逻辑值最大为1。
  128. 0x25, 0x01, //     LOGICAL_MAXIMUM (1)

  129. //这是一个全局条目,说明数据域的数量为八个。
  130. 0x95, 0x08, //     REPORT_COUNT (8)

  131. //这是一个全局条目,说明每个数据域的长度为1个bit。
  132. 0x75, 0x01, //     REPORT_SIZE (1)

  133. //这是一个主条目,说明有8个长度为1bit的数据域(数量和长度
  134. //由前面的两个全局条目所定义)用来做为输入,
  135. //属性为:Data,Var,Abs。Data表示这些数据可以变动,Var表示
  136. //这些数据域是独立的,每个域表示一个意思。Abs表示绝对值。
  137. //这样定义的结果就是,当某个域的值为1时,就表示对应的键按下。
  138. //bit0就对应着用途最小值0xe0,bit7对应着用途最大值0xe7。
  139. 0x81, 0x02, //     INPUT (Data,Var,Abs)

  140. //这是一个全局条目,说明数据域数量为1个
  141. 0x95, 0x01, //     REPORT_COUNT (1)

  142. //这是一个全局条目,说明每个数据域的长度为8bit。
  143. 0x75, 0x08, //     REPORT_SIZE (8)

  144. //这是一个主条目,输入用,由前面两个全局条目可知,长度为8bit,
  145. //数量为1个。它的属性为常量(即返回的数据一直是0)。
  146. //该字节是保留字节(保留给OEM使用)。
  147. 0x81, 0x03, //     INPUT (Cnst,Var,Abs)

  148. //这是一个全局条目。定义位域数量为6个。
  149. 0x95, 0x06, //   REPORT_COUNT (6)

  150. //这是一个全局条目。定义每个位域长度为8bit。
  151. //其实这里这个条目不要也是可以的,因为在前面已经有一个定义
  152. //长度为8bit的全局条目了。
  153. 0x75, 0x08, //   REPORT_SIZE (8)

  154. //这是一个全局条目,定义逻辑最小值为0。
  155. //同上,这里这个全局条目也是可以不要的,因为前面已经有一个
  156. //定义逻辑最小值为0的全局条目了。
  157. 0x15, 0x00, //   LOGICAL_MINIMUM (0)

  158. //这是一个全局条目,定义逻辑最大值为255。
  159. 0x25, 0xFF, //   LOGICAL_MAXIMUM (255)

  160. //这是一个全局条目,选择用途页为键盘。
  161. //前面已经选择过用途页为键盘了,所以该条目不要也可以。
  162. 0x05, 0x07, //   USAGE_PAGE (Keyboard/Keypad)

  163. //这是一个局部条目,定义用途最小值为0(0表示没有键按下)
  164. 0x19, 0x00, //   USAGE_MINIMUM (Reserved (no event indicated))

  165. //这是一个局部条目,定义用途最大值为0x65
  166. 0x29, 0x65, //   USAGE_MAXIMUM (Keyboard Application)

  167. //这是一个主条目。它说明这六个8bit的数据域是输入用的,
  168. //属性为:Data,Ary,Abs。Data说明数据是可以变的,Ary说明
  169. //这些数据域是一个数组,即每个8bit都可以表示某个键值,
  170. //如果按下的键太多(例如超过这里定义的长度或者键盘本身无法
  171. //扫描出按键情况时),则这些数据返回全1(二进制),表示按键无效。
  172. //Abs表示这些值是绝对值。
  173. 0x81, 0x00, //     INPUT (Data,Ary,Abs)

  174. //以下为输出报告的描述
  175. //逻辑最小值前面已经有定义为0了,这里可以省略。
  176. //这是一个全局条目,说明逻辑值最大为1。
  177. 0x25, 0x01, //     LOGICAL_MAXIMUM (1)

  178. //这是一个全局条目,说明数据域数量为5个。
  179. 0x95, 0x05, //   REPORT_COUNT (5)

  180. //这是一个全局条目,说明数据域的长度为1bit。
  181. 0x75, 0x01, //   REPORT_SIZE (1)

  182. //这是一个全局条目,说明使用的用途页为指示灯(LED)
  183. 0x05, 0x08, //   USAGE_PAGE (LEDs)

  184. //这是一个局部条目,说明用途最小值为数字键盘灯。
  185. 0x19, 0x01, //   USAGE_MINIMUM (Num Lock)

  186. //这是一个局部条目,说明用途最大值为Kana灯。
  187. 0x29, 0x05, //   USAGE_MAXIMUM (Kana)

  188. //这是一个主条目。定义输出数据,即前面定义的5个LED。
  189. 0x91, 0x02, //   OUTPUT (Data,Var,Abs)

  190. //这是一个全局条目。定义位域数量为1个。
  191. 0x95, 0x01, //   REPORT_COUNT (1)

  192. //这是一个全局条目。定义位域长度为3bit。
  193. 0x75, 0x03, //   REPORT_SIZE (3)

  194. //这是一个主条目,定义输出常量,前面用了5bit,所以这里需要
  195. //3个bit来凑成一字节。
  196. 0x91, 0x03, //   OUTPUT (Cnst,Var,Abs)

  197. //下面这个主条目用来关闭前面的集合。bSize为0,所以后面没数据。
  198. 0xc0,        // END_COLLECTION

  199. /************************USB键盘部分报告描述符**********************/
  200. /*******************************************************************/
  201. //这是一个全局(bType为1)条目,将用途页选择为普通桌面Generic Desktop Page(0x01)
  202. //后面跟一字节数据(bSize为1),后面的字节数就不注释了,
  203. //自己根据bSize来判断。
  204. 0x05, 0x01, // USAGE_PAGE (Generic Desktop)

  205. //这是一个局部(bType为2)条目,说明接下来的集合用途用于键盘
  206. 0x09, 0x06, // USAGE (Keyboard)

  207. //这是一个主条目(bType为0)条目,开集合,后面跟的数据0x01表示
  208. //该集合是一个应用集合。它的性质在前面由用途页和用途定义为
  209. //普通桌面用的键盘。
  210. 0xa1, 0x01, // COLLECTION (Application)

  211. //报告ID,这里定义键盘报告的ID为3(报告ID 0是保留的)
  212. 0x85, 0x03, //Report ID (3)

  213. //这是一个全局条目,选择用途页为键盘(Keyboard/Keypad(0x07))
  214. 0x05, 0x07, //     USAGE_PAGE (Keyboard/Keypad)

  215. //这是一个局部条目,说明用途的最小值为0xe0。实际上是键盘左Ctrl键。
  216. //具体的用途值可在HID用途表中查看。
  217. 0x19, 0xe0, //     USAGE_MINIMUM (Keyboard LeftControl)

  218. //这是一个局部条目,说明用途的最大值为0xe7。实际上是键盘右GUI键。
  219. 0x29, 0xe7, //     USAGE_MAXIMUM (Keyboard Right GUI)

  220. //这是一个全局条目,说明返回的数据的逻辑值(就是我们返回的数据域的值)
  221. //最小为0。因为我们这里用Bit来表示一个数据域,因此最小为0,最大为1。
  222. 0x15, 0x00, //     LOGICAL_MINIMUM (0)

  223. //这是一个全局条目,说明逻辑值最大为1。
  224. 0x25, 0x01, //     LOGICAL_MAXIMUM (1)

  225. //这是一个全局条目,说明数据域的数量为八个。
  226. 0x95, 0x08, //     REPORT_COUNT (8)

  227. //这是一个全局条目,说明每个数据域的长度为1个bit。
  228. 0x75, 0x01, //     REPORT_SIZE (1)

  229. //这是一个主条目,说明有8个长度为1bit的数据域(数量和长度
  230. //由前面的两个全局条目所定义)用来做为输入,
  231. //属性为:Data,Var,Abs。Data表示这些数据可以变动,Var表示
  232. //这些数据域是独立的,每个域表示一个意思。Abs表示绝对值。
  233. //这样定义的结果就是,当某个域的值为1时,就表示对应的键按下。
  234. //bit0就对应着用途最小值0xe0,bit7对应着用途最大值0xe7。
  235. 0x81, 0x02, //     INPUT (Data,Var,Abs)

  236. //这是一个全局条目,说明数据域数量为1个
  237. 0x95, 0x01, //     REPORT_COUNT (1)

  238. //这是一个全局条目,说明每个数据域的长度为8bit。
  239. 0x75, 0x08, //     REPORT_SIZE (8)

  240. //这是一个主条目,输入用,由前面两个全局条目可知,长度为8bit,
  241. //数量为1个。它的属性为常量(即返回的数据一直是0)。
  242. //该字节是保留字节(保留给OEM使用)。
  243. 0x81, 0x03, //     INPUT (Cnst,Var,Abs)

  244. //这是一个全局条目。定义位域数量为6个。
  245. 0x95, 0x06, //   REPORT_COUNT (6)

  246. //这是一个全局条目。定义每个位域长度为8bit。
  247. //其实这里这个条目不要也是可以的,因为在前面已经有一个定义
  248. //长度为8bit的全局条目了。
  249. 0x75, 0x08, //   REPORT_SIZE (8)

  250. //这是一个全局条目,定义逻辑最小值为0。
  251. //同上,这里这个全局条目也是可以不要的,因为前面已经有一个
  252. //定义逻辑最小值为0的全局条目了。
  253. 0x15, 0x00, //   LOGICAL_MINIMUM (0)

  254. //这是一个全局条目,定义逻辑最大值为255。
  255. 0x25, 0xFF, //   LOGICAL_MAXIMUM (255)

  256. //这是一个全局条目,选择用途页为键盘。
  257. //前面已经选择过用途页为键盘了,所以该条目不要也可以。
  258. 0x05, 0x07, //   USAGE_PAGE (Keyboard/Keypad)

  259. //这是一个局部条目,定义用途最小值为0(0表示没有键按下)
  260. 0x19, 0x00, //   USAGE_MINIMUM (Reserved (no event indicated))

  261. //这是一个局部条目,定义用途最大值为0x65
  262. 0x29, 0x65, //   USAGE_MAXIMUM (Keyboard Application)

  263. //这是一个主条目。它说明这六个8bit的数据域是输入用的,
  264. //属性为:Data,Ary,Abs。Data说明数据是可以变的,Ary说明
  265. //这些数据域是一个数组,即每个8bit都可以表示某个键值,
  266. //如果按下的键太多(例如超过这里定义的长度或者键盘本身无法
  267. //扫描出按键情况时),则这些数据返回全1(二进制),表示按键无效。
  268. //Abs表示这些值是绝对值。
  269. 0x81, 0x00, //     INPUT (Data,Ary,Abs)

  270. //以下为输出报告的描述
  271. //逻辑最小值前面已经有定义为0了,这里可以省略。
  272. //这是一个全局条目,说明逻辑值最大为1。
  273. 0x25, 0x01, //     LOGICAL_MAXIMUM (1)

  274. //这是一个全局条目,说明数据域数量为5个。
  275. 0x95, 0x05, //   REPORT_COUNT (5)

  276. //这是一个全局条目,说明数据域的长度为1bit。
  277. 0x75, 0x01, //   REPORT_SIZE (1)

  278. //这是一个全局条目,说明使用的用途页为指示灯(LED)
  279. 0x05, 0x08, //   USAGE_PAGE (LEDs)

  280. //这是一个局部条目,说明用途最小值为数字键盘灯。
  281. 0x19, 0x01, //   USAGE_MINIMUM (Num Lock)

  282. //这是一个局部条目,说明用途最大值为Kana灯。
  283. 0x29, 0x05, //   USAGE_MAXIMUM (Kana)

  284. //这是一个主条目。定义输出数据,即前面定义的5个LED。
  285. 0x91, 0x02, //   OUTPUT (Data,Var,Abs)

  286. //这是一个全局条目。定义位域数量为1个。
  287. 0x95, 0x01, //   REPORT_COUNT (1)

  288. //这是一个全局条目。定义位域长度为3bit。
  289. 0x75, 0x03, //   REPORT_SIZE (3)

  290. //这是一个主条目,定义输出常量,前面用了5bit,所以这里需要
  291. //3个bit来凑成一字节。
  292. 0x91, 0x03, //   OUTPUT (Cnst,Var,Abs)

  293. //下面这个主条目用来关闭前面的集合。bSize为0,所以后面没数据。
  294. 0xc0,        // END_COLLECTION
  295. };

  296. class CustomHIDDevice: public USBHIDDevice {
  297. public:
  298.   CustomHIDDevice(void){
  299.     static bool initialized = false;
  300.     if(!initialized){
  301.       initialized = true;
  302.       HID.addDevice(this, sizeof(report_descriptor));
  303.     }
  304.   }
  305.   
  306.   void begin(void){
  307.     HID.begin();
  308.   }
  309.    
  310.   uint16_t _onGetDescriptor(uint8_t* buffer){
  311.     memcpy(buffer, report_descriptor, sizeof(report_descriptor));
  312.     return sizeof(report_descriptor);
  313.   }

  314.   bool send(uint8_t * value){
  315.     return HID.SendReport(0, value, 9);
  316.   }
  317. };

  318. CustomHIDDevice Device;

  319. const int buttonPin = 0;
  320. int previousButtonState = HIGH;
  321. uint8_t axis[9];

  322. void setup() {
  323.   Serial.begin(115200);
  324.   Serial.setDebugOutput(true);
  325.   pinMode(buttonPin, INPUT_PULLUP);
  326.   Device.begin();
  327.   USB.begin();
  328. }

  329. void loop() {
  330.   int buttonState = digitalRead(buttonPin);
  331.   if (HID.ready() && buttonState != previousButtonState) {
  332.     previousButtonState = buttonState;
  333.     if (buttonState == LOW) {
  334.       Serial.println("Button Pressed");
  335.       axis[0] = random() & 0xFF;
  336.       Device.send(axis);
  337.     } else {
  338.       Serial.println("Button Released");
  339.     }
  340.     delay(100);
  341.   }
  342.   //每隔10秒
  343.   delay(10000);
  344.   //键盘1 输出一个 a
  345.   //其中 axis[0] 是 report ID 这里为 1
  346.   axis[0]=0x01;axis[3]=0x04;
  347.   Device.send(axis);
  348.   delay(20);
  349.   axis[0]=0x01;axis[3]=0x00;
  350.   Device.send(axis);

  351.   
  352.   delay(20);
  353.   //键盘2 输出一个 b
  354.   //其中 axis[0] 是 report ID 这里为 2
  355.   axis[0]=0x02;axis[3]=0x05;
  356.   Device.send(axis);
  357.   delay(20);
  358.   axis[0]=0x02;axis[3]=0x00;
  359.   Device.send(axis);

  360.   delay(20);
  361.   //键盘3 输出一个 c
  362.   //其中 axis[0] 是 report ID 这里为 3
  363.   axis[0]=0x03;axis[3]=0x06;
  364.   Device.send(axis);
  365.   delay(20);
  366.   axis[0]=0x03;axis[3]=0x00;
  367.   Device.send(axis);
  368. }
复制代码

对于 PS2 键盘来说是“全键无冲突的”,意思是可以按下任意多的按键;对于标准的USB键盘来说,最多只能同时按下6个按键。这是因为 PS2 是分开发送按下和抬起消息的;而标准USB键盘,有8Bytes的数据,其中第一个byte用来指示 alt ctrl 等等,第二个Byte始终为0,接下来剩下6Byte,需要放置按下键的信息,如果抬起还需要用0来指示。这样只能支持同时按下6个键。上面提到的这个方法可以用来扩展USB键盘,比如,声明3个键盘就可以支持 6*3=18键无冲突。

完整代码:

HIDTest.zip (3.04 KB, 下载次数: 3)


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

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 09:40 , Processed in 0.201338 second(s), 19 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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