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

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

//该报告描述符号由HID Descriptor tool生成
code ] = {

//-------------键盘部分报告描述符----------------
    //表示用途页为通用桌面设备
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)

    //表示用途为键盘
    0x09, 0x06,                    // USAGE (Keyboard)

    //表示应用集合,必须要以END_COLLECTION来结束它,见最后的END_COLLECTION
    0xa1, 0x01,                    // COLLECTION (Application)

    //报告ID(报告ID 0是保留的)
    0x85, 0x01, //Report ID (1)

    //表示用途页为按键
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)

    //用途最小值,这里为左ctrl键
    0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
    //用途最大值,这里为右GUI键,即window键
    0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
    //逻辑最小值为0
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    //逻辑最大值为1
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    //报告大小(即这个字段的宽度)为1bit,所以前面的逻辑最小值为0,逻辑最大值为1
    0x75, 0x01,                    //   REPORT_SIZE (1)
    //报告的个数为8,即总共有8个bits
    0x95, 0x08,                    //   REPORT_COUNT (8)
    //输入用,变量,值,绝对值。像键盘这类一般报告绝对值,
    //而鼠标移动这样的则报告相对值,表示鼠标移动多少
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    //上面这这几项描述了一个输入用的字段,总共为8个bits,每个bit表示一个按键
    //分别从左ctrl键到右GUI键。这8个bits刚好构成一个字节,它位于报告的第一个字节。
    //它的最低位,即bit-0对应着左ctrl键,如果返回的数据该位为1,则表示左ctrl键被按下,
    //否则,左ctrl键没有按下。最高位,即bit-7表示右GUI键的按下情况。中间的几个位,
    //需要根据HID协议中规定的用途页表(HID Usage Tables)来确定。这里通常用来表示
    //特殊键,例如ctrl,shift,del键等 

    //这样的数据段个数为1
    0x95, 0x01,                    //   REPORT_COUNT (1)
    //每个段长度为8bits
    0x75, 0x08,                    //   REPORT_SIZE (8)
    //输入用,常量,值,绝对值
    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)

    //上面这8个bit是常量,设备必须返回0

    //这样的数据段个数为5
    0x95, 0x05,                    //   REPORT_COUNT (5)
    //每个段大小为1bit
    0x75, 0x01,                    //   REPORT_SIZE (1)
    //用途是LED,即用来控制键盘上的LED用的,因此下面会说明它是输出用
    0x05, 0x08,                    //   USAGE_PAGE (LEDs)
    //用途最小值是Num Lock,即数字键锁定灯
    0x19, 0x01,                    //   USAGE_MINIMUM (Num Lock)
    //用途最大值是Kana,这个是什么灯我也不清楚^_^
    0x29, 0x05,                    //   USAGE_MAXIMUM (Kana)
    //如前面所说,这个字段是输出用的,用来控制LED。变量,值,绝对值。
    //1表示灯亮,0表示灯灭
    0x91, 0x02,                    //   OUTPUT (Data,Var,Abs)

    //这样的数据段个数为1
    0x95, 0x01,                    //   REPORT_COUNT (1)
    //每个段大小为3bits
    0x75, 0x03,                    //   REPORT_SIZE (3)
    //输出用,常量,值,绝对
    0x91, 0x03,                    //   OUTPUT (Cnst,Var,Abs)
    //由于要按字节对齐,而前面控制LED的只用了5个bit,
    //所以后面需要附加3个不用bit,设置为常量。

    //报告个数为6
    0x95, 0x06,                    //   REPORT_COUNT (6)
    //每个段大小为8bits
    0x75, 0x08,                    //   REPORT_SIZE (8)
    //逻辑最小值0
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    //逻辑最大值255
    0x25, 0xFF,                    //   LOGICAL_MAXIMUM (255)
    //用途页为按键
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    //使用最小值为0
    0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
    //使用最大值为0x65
    0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
    //输入用,变量,数组,绝对值
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
    //以上定义了6个8bit宽的数组,每个8bit(即一个字节)用来表示一个按键,所以可以同时
    //有6个按键按下。没有按键按下时,全部返回0。如果按下的键太多,导致键盘扫描系统
    //无法区分按键时,则全部返回0x01,即6个0x01。如果有一个键按下,则这6个字节中的第一
    //个字节为相应的键值(具体的值参看HID Usage Tables),如果两个键按下,则第1、2两个
    //字节分别为相应的键值,以次类推。

    //关集合,跟上面的对应
    0xc0 ,                          // END_COLLECTION

    //-----------------------鼠标部分报告描述符----------------------------
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x02,                    // USAGE (Mouse)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x85, 0x02,               // 报告ID (2)
    0x09, 0x01,                    //   USAGE (Pointer)
    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x95, 0x03,                    //     REPORT_COUNT (3)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x95, 0x01,                    //     REPORT_COUNT (1)
    0x75, 0x05,                    //     REPORT_SIZE (5)
    0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x09, 0x38,                    //     USAGE (Wheel)
    0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x03,                    //     REPORT_COUNT (3)
    0x81, 0x06,                    //     INPUT (Data,Var,Rel)
    0xc0,                          //   END_COLLECTION
    0xc0                           // END_COLLECTION
};

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

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

void main()
{
    unsigned ;
    signed ];
    ;        //键按下标志,防止重入
    ;        //键按下标志,防止重入

    )                    //初始化D12
        return;                            //如果初始化不成功,返回

    IT0    = ;                            //外部中断0为电平触发方式

    EX0 = ;                            //开外部中断0
    PX0 = ;                            //设置外部中断0中断优先级
    EA     = ;                                //开80C51总中断

    P0    = ;

    )
    {
        usbserve();                        //处理USB事件
        if(bEPPflags.bits.configuration)
        {
            //在这里添加端点操作代码
            if(bEPPflags.bits.ep2_rxdone )    //主端点接收到数据(从主机发往设备的数据)
            {
                bEPPflags.bits.ep2_rxdone        = ;

                //判断NumLock状态
                ] & 0x01)    //EpBuf为接收缓冲
                {
                    P0    = 0x01;
                }
                else
                {
                    P0    = 0x00;
                }                

            }

            K1    = ;        //P3.5
            K2    = ;        //P3.6

            ;i<;i++);    //延时 

            if(~K1 )    //K1按下(模拟鼠标左移)
            {
                cKeyIn[]=;        //报告ID,第一个字节为报告ID(报告描述符里定义了鼠标ID为2)

                cKeyIn[]=;
                cKeyIn[]=-;            //鼠标左移
                cKeyIn[]=;
                cKeyIn[]=;        

                D12_WriteEndpoint(,,cKeyIn);            //发5个字节到PC机,第一个字节为报告ID(报告描述符里定义了鼠标ID为2)
            }

            if(~K2 & !bKey2Pressed)    //K2按下(模拟左Windows键)
            {

                bKey2Pressed    = ;    //防止重入

                cKeyIn[]=;        //报告ID,第一个字节为报告ID(报告描述符里定义了键盘ID为1)
                cKeyIn[]=0x08;        //特殊键
                cKeyIn[]=;        //保留
                cKeyIn[]=;
                cKeyIn[]=;
                cKeyIn[]=;
                cKeyIn[]=;
                cKeyIn[]=;
                cKeyIn[]=;        

                D12_WriteEndpoint(,,cKeyIn);            //发9个字节到PC机,第一个字节为报告ID(报告描述符里定义了键盘ID为1)
            }
            else if(K2 & bKey2Pressed)    //K2弹起
            {
                bKey2Pressed    = ;    //防止重入

                cKeyIn[]=;        //报告ID,第一个字节为报告ID(报告描述符里定义了键盘ID为1)
                cKeyIn[]=;
                cKeyIn[]=;        //保留
                cKeyIn[]=;
                cKeyIn[]=;
                cKeyIn[]=;
                cKeyIn[]=;
                cKeyIn[]=;
                cKeyIn[]=;        

                D12_WriteEndpoint(,,cKeyIn);            //发9个字节到PC机,第一个字节为报告ID(报告描述符里定义了键盘ID为1)
            }

        }
    }
}

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

  从实例中可以看出,所有输入输出数据包都在最低字节位置插入了一个报告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. Azure File文件共享(6):使用Python开发

    Azure文件共享服务提供了多种方式的访问接口,包括Powershell,.Net, Java, Python等等,本章主要介绍如何使用Python来访问Azure File存储. 关于Python环 ...

  2. log4net使用(包括单个文件和按日期生成多个文件)

    1.log4net生成单个文件 直接将这段代码考到config中即可用 <log4net> <!--定义输出到文件中--> <appender name="Lo ...

  3. Strtus2工作流程及原理

    从上图可以看出,一个请求在Struts2 框架中的处理大概分为以下几个步骤:1.客户端初始化一个指向Servlet 容器(例如Tomcat)的请求:2.这个请求经过一系列的过滤器(Filter)(这些 ...

  4. php修改配置文件php.ini设置文件上传大小讲解

    打开php.ini,首先找到;;;;;;;;;;;;;;;;; File Uploads ;;;;;;;;;;;;;;;;;区域,有影响文件上传的以下几个参数: file_uploads   =   ...

  5. MAC上python环境搭建

    mac自带的有python,如果你需要查看版本,打开terminal输入以下命令: python --version 如果你需要安装新的python版本,有几种方法可以安装,一是去python官网下载 ...

  6. CF 39E What Has Dirichlet Got to Do with That? (博弈)

    转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove 题意:给出a ^ b,两个人轮流操作,可以  a ...

  7. Zookeeper 5、Zookeeper应用场景

    应用场景1 .统一命名服务 » 分布式应用中,通常需要有一套完整的命名规则,既能够产生唯一的名称又便于人识别和记住,通常情况 下用树形的名称结构是一个理想的选择,树形的名称结构是一个有层次的目录结构, ...

  8. error C2440

    error C2440: "初始化": 无法从"std::_List_const_iterator<std::_List_val<std::_List_sim ...

  9. VS2015 MVC5项目部署

    刚看到一个年初的一个帖子说VS2015新建的MVC5项目部署后报错,自己捣鼓了一下,发现是Roslyn编译器的错误,简单处理后运行成功,分享如下: 新建一个MVC5的项目,保持不要动,执行以下几个步骤 ...

  10. FMDB使用

    FMDBManager.h #import <Foundation/Foundation.h> #import "FMDatabase.h" @interface FM ...