linux usb组合设备,(转)一个usb口实现多usb设备之USB HID复合设备实例(键盘+鼠标)

2023-05-16

4076426411de657585834f1511f54f62.bmp

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

上两节我们实现了USB鼠标和USB键盘的实例,也许有人会问能不能用一套硬件同时实现USB鼠标和USB键盘的功能,答案是肯定的,而且这种应用场合是有的,比如带鼠标功能的多媒体键盘。

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

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

//-------------键盘部分报告描述符----------------

//表示用途页为通用桌面设备

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 char i = 0;

signed char cKeyIn[9];

static bit bKey1Pressed = 0;        //键按下标志,防止重入

static bit bKey2Pressed = 0;        //键按下标志,防止重入

if (Init_D12()!=0)                  //初始化D12

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

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

EX0 = 1;                            //开外部中断0

PX0 = 0;                            //设置外部中断0中断优先级

EA  = 1;                                //开80C51总中断

P0  = 0;

while(1)

{

usbserve();                     //处理USB事件

if(bEPPflags.bits.configuration)

{

//在这里添加端点操作代码

if(bEPPflags.bits.ep2_rxdone )  //主端点接收到数据(从主机发往设备的数据)

{

bEPPflags.bits.ep2_rxdone       = 0;

//判断NumLock状态

if(EpBuf[1] & 0x01) //EpBuf为接收缓冲

{

P0  = 0x01;

}

else

{

P0  = 0x00;

}

}

K1  = 1;        //P3.5

K2  = 1;        //P3.6

for(i=0;i<100;i++); //延时

if(~K1 )    //K1按下(模拟鼠标左移)

{

cKeyIn[0]=2;        //报告ID,第一个字节为报告ID(报告描述符里定义了鼠标ID为2)

cKeyIn[1]=0;

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

cKeyIn[3]=0;

cKeyIn[4]=0;

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

}

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

{

bKey2Pressed    = 1;    //防止重入

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

cKeyIn[1]=0x08;     //特殊键

cKeyIn[2]=0;        //保留

cKeyIn[3]=0;

cKeyIn[4]=0;

cKeyIn[5]=0;

cKeyIn[6]=0;

cKeyIn[6]=0;

cKeyIn[8]=0;

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

}

else if(K2 & bKey2Pressed)  //K2弹起

{

bKey2Pressed    = 0;    //防止重入

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

cKeyIn[1]=0;

cKeyIn[2]=0;        //保留

cKeyIn[3]=0;

cKeyIn[4]=0;

cKeyIn[5]=0;

cKeyIn[6]=0;

cKeyIn[7]=0;

cKeyIn[8]=0;

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

}

}

}

}

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

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

论坛中还有另外一篇组合设备的使用

USB复合设备和USB组合设备的区别和实现代码分析

鸣谢:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux usb组合设备,(转)一个usb口实现多usb设备之USB HID复合设备实例(键盘+鼠标) 的相关文章

  • 精彩网页

    新华网美国频道 xff1a http us xinhuanet com php加密网址 xff1a https www phpjiami com 转载于 https www cnblogs com net5x p 6570727 html
  • java集合类(二)List学习

    接上篇 java集合类 xff08 一 xff09 List接口继承了Collection接口和Iterable接口 xff0c 即同样含有Collection和 Iterable的特性 xff0c 还有方法 xff0c 其基本方法有 xf
  • 构建安全的数据访问-部署注意事项(九)

    部署注意事项 以安全方式设计和开发的数据访问组件如果不以安全的方式进行部署 xff0c 仍然容易受到攻击 常见的部署做法是使数据访问代码和数据库驻留在单独的服务器上 这些服务器通常由内部防火墙隔开 xff0c 这就引进了额外的部署注意事项
  • matlab练习程序(随机直线采样)

    我只是感觉好玩 xff0c 写了这样一段程序 原理就是先随机生成两个点 xff0c 然后根据这两个点画直线 xff0c 最后在直线上的像素保留 xff0c 没在直线上的像素丢弃就行了 最后生成了一幅含有很多空洞的图像 当然 xff0c 对含
  • 跳一跳j算法ava代码_麻将游戏算法深入解析及实现代码

    麻将游戏算法深入解析及实现代码 这两天为了工具箱的完善 xff0c 整理了这些年引擎开发的一些资料 xff0c 无意中发现06年写的一个麻将算法 xff0c 编译运行了一下 xff0c 还是有点意思的 xff0c 拿出来整理一下分享给大家
  • 使用SP Racing F3飞控&ROSflight软件包的无人机自主飞行系统

    搭建四旋翼系统 机架 xff1a XR215 Plus 328 分线板 xff1a XR215 Plus PDB 飞控 xff1a SP Racing F3 标准版 xff08 Acro xff09 86 电机 xff1a 银燕RS2205
  • PostMan使用手册

    Postman 使用手册系列教程 xff1a Postman软件安装 Postman使用手册1 导入导出和发送请求查看响应 Postman使用手册2 管理收藏 Postman使用手册3 环境变量 Postman使用手册4 API test
  • python 网站文件及数据库备份脚本

    初学python xff0c 试着写了一份python网站文件备份和数据库备份的脚本 xff0c 功能是写出来了 xff0c 但感觉还是不太适应 xff0c 写得不太好 xff0c 以后还要努力哈 xff01 backup py读取back
  • 八款值得尝试的精美的 Linux 发行版(2017 版)

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 在这篇文章中 xff0c 将会列出让一些令 Linux 用户印象最深刻且精美的 Linux 发行版 xff0c 包括对初学者友好和流行的发行版 1 elementary O
  • 大型网站架构系列:20本技术书籍推荐

    学习是技术人员成长的基础 xff0c 本次分享20本技术方面的书籍 xff0c 这些书不是每一本都是经典 xff0c 但是每一本都有其特点 以下20本大部分本人都看过 xff0c 因此推荐给大家 xff08 本次推荐的20本只是一个参考 x
  • 正确判断无人机指向故障 让电子罗盘远离磁干扰

    电子罗盘作为无人机产品的重要组成部件 xff0c 承载着为无人机引导绝对方位的功能 对于普通设计者而言 xff0c 经常会遇到电子罗盘校正困难 xff0c 校正需求过于频繁 xff0c 动态 高速运行时突发偏离 xff0c 以及无论怎么校正
  • 测试计划驱动开发模式 TPDD:一种比 TDD 更友好的开发模式

    相信大部分开发团队都在使用TDD xff0c 并且还有很多开发团队都 对外声明 在使用 TDD 开发模式 之所以说是 对外声明 xff0c 是因为很多开发团队虽然号称使用的是 TDD 开发模式 xff0c 实际开发过程中却无法满足 TDD
  • Android Button悬浮在SurfaceView上

    实现Button悬浮于与SurfaceView之上实现 注意 xff1a 你实现的SurfaceView和android中的Button EditView是同级的 xff0c 不能把一个包含在另一个里面 1 创建自己的SurfaceView
  • 异域linux内核漏洞,Ubuntu本地提权攻击漏洞复现(CVE-2017-16995)

    Ubuntu 16 04 4 kernel priv esc all credits to 64 bleidl vnik Tested on 4 4 0 116 generic 140 Ubuntu SMP Mon Feb 12 21 23
  • ROS 自定义消息类型

    引言 学习ROS的过程中 xff0c 在话题的发布与订阅之间 xff0c 我一直在思考 xff0c 我们能不能定义自己的话题名 xff0c 甚至在编写我们自己的ROS软件包时 xff0c 定义我们自己的消息类型 首先能不能定义自己的话题名
  • 计算机方便了我的工作作文,电脑给我们的生活带来了方便作文2篇

    电脑给我们的生活带来了方便作文2篇 在生活 工作和学习中 xff0c 大家都尝试过写作文吧 xff0c 借助作文人们可以反映客观事物 表达思想感情 传递知识信息 那么你有了解过作文吗 xff1f 下面是小编为大家收集的电脑给我们的生活带来了
  • python播放mp3文件 playsound_播放声音文件 函数playsound()

    播放声音文件 1 用途 xff1a 游戏里播放音乐 xff1b 自己动手给女友做音乐贺卡 图片 滚动的字母 温馨的音乐 2 可以使用PlaySound 函数播放声音文件 xff0c 该函数原型位于 include 中 因此要使用PlaySo
  • arduinouno组装无人机_Arduino UNO四自由度机械臂制作

    四自由度机械臂制作 准备材料 1 某宝上的四自由度机械臂拼装板 2 9克舵机 x 3 3 Arduino UNO 4 步进电机 x 1 5 外电源 及 导线 制作改进 拼装后发现原来的底盘旋转舵机出现头重脚轻 摇摇欲坠的感觉 决定改进 换装
  • android接收arduino字符串,【教程】Android手机通过OTG线连接Arduino,读写串口数据...

    因为自己对电子电路是半桶水 xff0c 复杂的单片机电路把我难倒了 xff0c 幸好Arduino的出现 xff0c 让一切都简化了不少 平时和朋友们就喜欢制作一些Arduino的小玩意儿来帮助生活和工作 因为朋友单位需要采购一些数据采集和
  • 计算机计算exp函数原理,exp函数表示什么意思(exp怎么按计算器)

    目的 用于 Exp 2 是e的平方 xff0c 指数函数是数学中的一个重要函数 应用于值e的函数写成 扩展数据 作为实变量x的函数 xff0c y 61 e的x次方的像总是正的 x轴以上 且递增的 从左到右 它从不接触x轴 xff0c 虽然

随机推荐