实现这种USB HID复合设备有两种方法,在《USB HID协议入门》一节已经讲到其中一种方法,说一个USB HID设备可以包含多种功能的报告描述符合集,这样可以实现复合设备,如带鼠标功能的USB键盘,这种复合键盘可以通过在报告描述里包含键盘和鼠标两种报告来实现,两个报告用报告ID来区分。这节我们就用这种方法来实现同时带鼠标和键盘功能的USB HID复合设备,有关另外一种方法的详细教程和实例可以参考本工作室推出的USB学习板。

  既然可以用“在报告描述里包含键盘和鼠标两种报告来实现”,那么我们就把上两节的键盘和鼠标实例的报告描述符放在一起,再加上报告ID就是了,修改后的报告描述符如下:

  1. //该报告描述符号由HID Descriptor tool生成
  2. code ] = {
  3.  
  4. //-------------键盘部分报告描述符----------------
  5. //表示用途页为通用桌面设备
  6. 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
  7.  
  8. //表示用途为键盘
  9. 0x09, 0x06, // USAGE (Keyboard)
  10.  
  11. //表示应用集合,必须要以END_COLLECTION来结束它,见最后的END_COLLECTION
  12. 0xa1, 0x01, // COLLECTION (Application)
  13.  
  14. //报告ID(报告ID 0是保留的)
  15. 0x85, 0x01, //Report ID (1)
  16.  
  17. //表示用途页为按键
  18. 0x05, 0x07, // USAGE_PAGE (Keyboard)
  19.  
  20. //用途最小值,这里为左ctrl键
  21. 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
  22. //用途最大值,这里为右GUI键,即window键
  23. 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
  24. //逻辑最小值为0
  25. 0x15, 0x00, // LOGICAL_MINIMUM (0)
  26. //逻辑最大值为1
  27. 0x25, 0x01, // LOGICAL_MAXIMUM (1)
  28. //报告大小(即这个字段的宽度)为1bit,所以前面的逻辑最小值为0,逻辑最大值为1
  29. 0x75, 0x01, // REPORT_SIZE (1)
  30. //报告的个数为8,即总共有8个bits
  31. 0x95, 0x08, // REPORT_COUNT (8)
  32. //输入用,变量,值,绝对值。像键盘这类一般报告绝对值,
  33. //而鼠标移动这样的则报告相对值,表示鼠标移动多少
  34. 0x81, 0x02, // INPUT (Data,Var,Abs)
  35. //上面这这几项描述了一个输入用的字段,总共为8个bits,每个bit表示一个按键
  36. //分别从左ctrl键到右GUI键。这8个bits刚好构成一个字节,它位于报告的第一个字节。
  37. //它的最低位,即bit-0对应着左ctrl键,如果返回的数据该位为1,则表示左ctrl键被按下,
  38. //否则,左ctrl键没有按下。最高位,即bit-7表示右GUI键的按下情况。中间的几个位,
  39. //需要根据HID协议中规定的用途页表(HID Usage Tables)来确定。这里通常用来表示
  40. //特殊键,例如ctrl,shift,del键等
  41.  
  42. //这样的数据段个数为1
  43. 0x95, 0x01, // REPORT_COUNT (1)
  44. //每个段长度为8bits
  45. 0x75, 0x08, // REPORT_SIZE (8)
  46. //输入用,常量,值,绝对值
  47. 0x81, 0x03, // INPUT (Cnst,Var,Abs)
  48.  
  49. //上面这8个bit是常量,设备必须返回0
  50.  
  51. //这样的数据段个数为5
  52. 0x95, 0x05, // REPORT_COUNT (5)
  53. //每个段大小为1bit
  54. 0x75, 0x01, // REPORT_SIZE (1)
  55. //用途是LED,即用来控制键盘上的LED用的,因此下面会说明它是输出用
  56. 0x05, 0x08, // USAGE_PAGE (LEDs)
  57. //用途最小值是Num Lock,即数字键锁定灯
  58. 0x19, 0x01, // USAGE_MINIMUM (Num Lock)
  59. //用途最大值是Kana,这个是什么灯我也不清楚^_^
  60. 0x29, 0x05, // USAGE_MAXIMUM (Kana)
  61. //如前面所说,这个字段是输出用的,用来控制LED。变量,值,绝对值。
  62. //1表示灯亮,0表示灯灭
  63. 0x91, 0x02, // OUTPUT (Data,Var,Abs)
  64.  
  65. //这样的数据段个数为1
  66. 0x95, 0x01, // REPORT_COUNT (1)
  67. //每个段大小为3bits
  68. 0x75, 0x03, // REPORT_SIZE (3)
  69. //输出用,常量,值,绝对
  70. 0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
  71. //由于要按字节对齐,而前面控制LED的只用了5个bit,
  72. //所以后面需要附加3个不用bit,设置为常量。
  73.  
  74. //报告个数为6
  75. 0x95, 0x06, // REPORT_COUNT (6)
  76. //每个段大小为8bits
  77. 0x75, 0x08, // REPORT_SIZE (8)
  78. //逻辑最小值0
  79. 0x15, 0x00, // LOGICAL_MINIMUM (0)
  80. //逻辑最大值255
  81. 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
  82. //用途页为按键
  83. 0x05, 0x07, // USAGE_PAGE (Keyboard)
  84. //使用最小值为0
  85. 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
  86. //使用最大值为0x65
  87. 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
  88. //输入用,变量,数组,绝对值
  89. 0x81, 0x00, // INPUT (Data,Ary,Abs)
  90. //以上定义了6个8bit宽的数组,每个8bit(即一个字节)用来表示一个按键,所以可以同时
  91. //有6个按键按下。没有按键按下时,全部返回0。如果按下的键太多,导致键盘扫描系统
  92. //无法区分按键时,则全部返回0x01,即6个0x01。如果有一个键按下,则这6个字节中的第一
  93. //个字节为相应的键值(具体的值参看HID Usage Tables),如果两个键按下,则第1、2两个
  94. //字节分别为相应的键值,以次类推。
  95.  
  96. //关集合,跟上面的对应
  97. 0xc0 , // END_COLLECTION
  98.  
  99. //-----------------------鼠标部分报告描述符----------------------------
  100. 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
  101. 0x09, 0x02, // USAGE (Mouse)
  102. 0xa1, 0x01, // COLLECTION (Application)
  103. 0x85, 0x02, // 报告ID (2)
  104. 0x09, 0x01, // USAGE (Pointer)
  105. 0xa1, 0x00, // COLLECTION (Physical)
  106. 0x05, 0x09, // USAGE_PAGE (Button)
  107. 0x19, 0x01, // USAGE_MINIMUM (Button 1)
  108. 0x29, 0x03, // USAGE_MAXIMUM (Button 3)
  109. 0x15, 0x00, // LOGICAL_MINIMUM (0)
  110. 0x25, 0x01, // LOGICAL_MAXIMUM (1)
  111. 0x95, 0x03, // REPORT_COUNT (3)
  112. 0x75, 0x01, // REPORT_SIZE (1)
  113. 0x81, 0x02, // INPUT (Data,Var,Abs)
  114. 0x95, 0x01, // REPORT_COUNT (1)
  115. 0x75, 0x05, // REPORT_SIZE (5)
  116. 0x81, 0x03, // INPUT (Cnst,Var,Abs)
  117. 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
  118. 0x09, 0x30, // USAGE (X)
  119. 0x09, 0x31, // USAGE (Y)
  120. 0x09, 0x38, // USAGE (Wheel)
  121. 0x15, 0x81, // LOGICAL_MINIMUM (-127)
  122. 0x25, 0x7f, // LOGICAL_MAXIMUM (127)
  123. 0x75, 0x08, // REPORT_SIZE (8)
  124. 0x95, 0x03, // REPORT_COUNT (3)
  125. 0x81, 0x06, // INPUT (Data,Var,Rel)
  126. 0xc0, // END_COLLECTION
  127. 0xc0 // END_COLLECTION
  128. };

  这个报告描述符定义了两个报告输入报告(即数据包)和一个输出报告,两个输入报告中,一个是键盘,一个是鼠标,输出报告是用于指示LED状态的,两个输出报告所定义的数据包格式可以参考上两节内容。

  由于电路板上只有两个按键,所有用K1模拟鼠标左移,K2 模拟键盘上的Windows图标键(按下K2后会弹出开始菜单),其代码如下:

  1. void main()
  2. {
  3. unsigned ;
  4. signed ];
  5. ; //键按下标志,防止重入
  6. ; //键按下标志,防止重入
  7.  
  8. ) //初始化D12
  9. return; //如果初始化不成功,返回
  10.  
  11. IT0 = ; //外部中断0为电平触发方式
  12.  
  13. EX0 = ; //开外部中断0
  14. PX0 = ; //设置外部中断0中断优先级
  15. EA = ; //开80C51总中断
  16.  
  17. P0 = ;
  18.  
  19. )
  20. {
  21. usbserve(); //处理USB事件
  22. if(bEPPflags.bits.configuration)
  23. {
  24. //在这里添加端点操作代码
  25. if(bEPPflags.bits.ep2_rxdone ) //主端点接收到数据(从主机发往设备的数据)
  26. {
  27. bEPPflags.bits.ep2_rxdone = ;
  28.  
  29. //判断NumLock状态
  30. ] & 0x01) //EpBuf为接收缓冲
  31. {
  32. P0 = 0x01;
  33. }
  34. else
  35. {
  36. P0 = 0x00;
  37. }
  38.  
  39. }
  40.  
  41. K1 = ; //P3.5
  42. K2 = ; //P3.6
  43.  
  44. ;i<;i++); //延时
  45.  
  46. if(~K1 ) //K1按下(模拟鼠标左移)
  47. {
  48. cKeyIn[]=; //报告ID,第一个字节为报告ID(报告描述符里定义了鼠标ID为2)
  49.  
  50. cKeyIn[]=;
  51. cKeyIn[]=-; //鼠标左移
  52. cKeyIn[]=;
  53. cKeyIn[]=;
  54.  
  55. D12_WriteEndpoint(,,cKeyIn); //发5个字节到PC机,第一个字节为报告ID(报告描述符里定义了鼠标ID为2)
  56. }
  57.  
  58. if(~K2 & !bKey2Pressed) //K2按下(模拟左Windows键)
  59. {
  60.  
  61. bKey2Pressed = ; //防止重入
  62.  
  63. cKeyIn[]=; //报告ID,第一个字节为报告ID(报告描述符里定义了键盘ID为1)
  64. cKeyIn[]=0x08; //特殊键
  65. cKeyIn[]=; //保留
  66. cKeyIn[]=;
  67. cKeyIn[]=;
  68. cKeyIn[]=;
  69. cKeyIn[]=;
  70. cKeyIn[]=;
  71. cKeyIn[]=;
  72.  
  73. D12_WriteEndpoint(,,cKeyIn); //发9个字节到PC机,第一个字节为报告ID(报告描述符里定义了键盘ID为1)
  74. }
  75. else if(K2 & bKey2Pressed) //K2弹起
  76. {
  77. bKey2Pressed = ; //防止重入
  78.  
  79. cKeyIn[]=; //报告ID,第一个字节为报告ID(报告描述符里定义了键盘ID为1)
  80. cKeyIn[]=;
  81. cKeyIn[]=; //保留
  82. cKeyIn[]=;
  83. cKeyIn[]=;
  84. cKeyIn[]=;
  85. cKeyIn[]=;
  86. cKeyIn[]=;
  87. cKeyIn[]=;
  88.  
  89. D12_WriteEndpoint(,,cKeyIn); //发9个字节到PC机,第一个字节为报告ID(报告描述符里定义了键盘ID为1)
  90. }
  91.  
  92. }
  93. }
  94. }

  实例中为了演示方便,没有加入按键消抖功能,实际应用中应加上。

  从实例中可以看出,所有输入输出数据包都在最低字节位置插入了一个报告ID,有用数据都从第二个字节开始。

  编译、烧录程序,插上设备,实际测试看看效果,再打开设备管理器,发现人体学输入设备里只多出了一个人体学输入设备(图中另一个是我本来的USB鼠标),在键盘和鼠标里都多了一个HID类型的键盘和鼠标,再分别查看它们的PID和VID,发现都是一样的。

       
 

USB HID复合设备实例—键盘+鼠标的更多相关文章

  1. linux 读取 USB HID鼠标坐标和点击 在 LCD上显示

    首先要,编译内核时启用了 USB HID 设备.启用了 鼠标 . 在开发板上插入usb 时会有如下提示. 可以看到,多了一个 mouse0 和 eventX 打出来的是我的 联想鼠标. 1, 在 终端 ...

  2. USB HID 协议入门

    转载请注明来源:cuixiaolei的技术博客 USB HID设备类的应用场合 USB HID类是USB设备的一个标准设备类,包括的设备非常多.HID类设备定义它属于人机交互操作的设备,用于控制计算机 ...

  3. C#进阶——记一次USB HID的各种坑(x86,x64,win10,win7)

    一.简叙 写工控上位机的搬砖人,难免会遇到USB通讯,在一个项目中,我写的上位机使用USB HID协议和STM32通讯传输数据,从零大概花了几天找例程,找资料,最后是各种搬砖修补,终于出来了一个出版D ...

  4. USB HID介绍【转】

    本文转载自:http://blog.csdn.net/leo_wonty/article/details/6721214 HID是一种USB通信协议,无需安装驱动就能进行交互,在学习HID之前,先来复 ...

  5. USB HID描述符【转】

    本文转载自: USB是个通用的总线,端口都是统一的.但是USB设备却各种各样,例如USB鼠标,USB键盘,U盘等等,那么USB主机是如何识别出不同的设备的呢?这就要依赖于描述符了.USB的描述符主要有 ...

  6. C# 访问USB(HID)设备

    原文:C# 访问USB(HID)设备 二话不说,直接给代码,如果您真想做这方面的东西,还是稍微研究下,没有现成的好类用,就需要自己了解其原理 //引用空间 using System; using Sy ...

  7. USB HID报告及报告描述符简介

    在USB中,USB HOST是通过各种描述符来识别设备的,有设备描述符,配置描述符,接口描述符,端点描述符,字符串描述符,报告描述符等等.USB报告描述符(Report Descriptor)是HID ...

  8. Windows与自定义USB HID设备通信说明.

    1 .   所使用的典型 Windows API CreateFile ReadFile WriteFile 以下函数是 DDK 的内容: HidD_SetFeature HidD_GetFeatur ...

  9. USB HID Report Descriptor 报告描述符详解

    Report descriptors are composed of pieces of information. Each piece of information is called an Ite ...

随机推荐

  1. Effective Java2读书笔记-创建和销毁对象(一)

    第1条:考虑用静态工厂方法代替构造器 通常情况下,我们创建一个对象采取new的形式,但是还有一种方法也是经常使用到的,它的名称叫做静态工厂方法. 例如,java中基本类型boolean的包装类Bool ...

  2. uestc poj2559 秋实大哥去打工

    //感觉有必要把这题放博客上待复习 刚刚写解题报告的时候发现自己又不会做这题了 //我不会告诉你这题绝对是命题人抄poj2559 这题使用一个单调递增的栈,栈内存储的元素有两个值,一个高度,一个长度. ...

  3. hdu 3874 Necklace(bit树+事先对查询区间右端点排序)

    Mery has a beautiful necklace. The necklace is made up of N magic balls. Each ball has a beautiful v ...

  4. HDU1242 Rescue(BFS+优先队列)

    Rescue Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Subm ...

  5. 网易云课堂_C++开发入门到精通_章节8:设计模式

    课时44设计模式简介 设计模式简介 面向对象设计的第一个原则:针对接口编程,而不是针对实现编程 接口->指针 实现->实例 若已存在一个类Class A,现在希望复用Class A,则有以 ...

  6. ModelAndView解析

    查看spring的帮助文档得到下面信息: org.springframework.web.servlet Class ModelAndViewjava.lang.Object org.springfr ...

  7. 关于Node.js, Jade一点小小的介绍。

    本文出自:http://blog.csdn.net/svitter node.js大家知道的可能比較多,可是jade大家可能就不知道了.. GFW封杀掉google以后.今天在百度上找了好久也没有找到 ...

  8. Android应用程序请求SurfaceFlinger服务渲染Surface的过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/7932268 在前面一篇文章中,我们分析了And ...

  9. Codevs1992题解

    题目大意 求有向图中经过某一点k的最大环(数据规模不支持floyd). 题解 以k为起点在正向图中spfa求单源最短路.再在反向图中spfa求单源最短路. 枚举除k外的每个点i.假设有一个同一时候包括 ...

  10. yum安裝的包如何保留到本地

    一, 很多时候,我们一直用yum安装的软件,但是毫无疑问,很多人都会想yum安装的软件的包存放在哪里了呢? 这是因为yum默认并不保存你所安装的包,那么如何才能保留安装的软件包呢? 方法很简单:修改y ...