上一篇文章提到了如何用STM32CubeMX自动生成一个USB鼠标的过程,其中包括比较复杂的报告描述符。这里,我们以USB鼠标的报告描述符为例,说一下报告描述符的结构和含义。
首先要说明的一点,报告描述符和设备描述符、配置描述符、接口描述符等的结构完全不一样,不能以后者的架构去解读。同时,报告描述符的复杂程度也很高,它涉及到了很多的定义:长条目、短条目、主条目、输入条目、输出条目、开集合、关集合、全局条目、局部条目等等,有的下面还包含其它的内容。所以,短时间是不可能全部理解。
我这里也不想写太多,毕竟写多了没人愿意看。我想换一种方式来描述,什么方式?
以目标为导向!
前期,我们先不考虑它包含的这些定义的含义,我们只需要知道它的目的是什么?
报告描述符!自然是用来描述报告的!这个报告又是什么?前一篇文章已经提到了,报告是一组数据,由USB设备发给USB主机,用来报告当前鼠标进行了哪些操作!
所以,我们可以知道,USB鼠标,把自己的动作以报告的形式发送给USB主机(也就是电脑),而电脑要解析这个报告,依据就是报告描述符!
明白了这一点,我们再来看报告描述符,就简单多了。
首先,看一个手册(Device Class Definition for Human Interface Devices (HID))里的截图,对报告描述符的解析流程有一个初步的认识。
感谢有道词典的赞助,这段话的意思是:
来自控件的一个或多个数据域由一个主项定义,并由前面的全局项和局部项进一步描述。本地项只描述由下一个主项定义的数据字段。全局项成为该描述符中所有后续数据字段的默认属性。
看不太懂没关系,继续。猜一下上面的那几行代码是什么意思,那就是报告描述符的一部分。直接看结果:
报告描述符里的数据,是以bit为单位进行描述的。
Report Size (3)就表示一组有3个bit,紧接着的一行:Report Count (2),就表示这样的组有2个。
Input,表示这两组数据是输入的(在USB协议中,输入、输出是相对USB主机来说的)。
Report Size (8),表示一组有8个bit,也就是一个字节的数据。
Input,数据输入。没有说这样的数据有几组,那么小组的个数就沿用之前的,还是2组。
Output,数据输出。小组大小、小组数量同上。
通过这种方式解释,是不是瞬间明白了报告描述符的解析思路?
接下来我们直接看USB鼠标的报告描述符,但看的不是STM32里提供的,因为它没有提供注释,看起来太痛苦,这里看报告描述符工具里自带的例程:
导出后,生成.h文件,内容如下:
char ReportDescriptor[50] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
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)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
其实,相比.h文件,工具里的描述符格式更好理解。因为工具里通过空格的多少,表明了哪些数据是一组。我们大致可以知道这些描述符的功能:
用途页-桌面通用
用途-鼠标
开集合-应用
用途-指针
开集合-物理
用途页-按键
用途最小值-鼠标左键
用途最大值-鼠标中键
逻辑最小值-0:按键抬起
逻辑最大值-1:按键按下
报告数量-3:3个按键
报告小组-1:1组
数据输入:可变,独立,绝对值
报告数量:1
报告小组:5,为了和前面的3bit凑成一个字节
数据输入:常量,独立,绝对值
用途页-桌面通用
用途:X轴
用途:Y轴
逻辑最小值:-127
逻辑最大值:127,表示鼠标的左右、上下移动
报告数量:8
报告小组:2,X一个,Y一个,刚好两个
数据输入:可变、独立、相对值
关集合
关集合
有些报告描述符里还会在X、Y之后再加一个用途,就是滚轮。
总之,报告描述符虽然复杂一些,但结合这些实例以及相关文档,理解起来不是特别难。先说到这里,如果后面找到更简单的方法,再来更新!
我是单片机爱好者,MCU起航!
你好,我想咨询您一些问题。
有两台pc设备A、B,使用串口线连接AB。在B上写个程序,发送鼠标指令,使A误以为B是一个鼠标。
这个需求确实很奇怪,但是我非常需要实现这个功能。然而我不熟悉鼠标相关的知识,希望你能解答,谢谢!
这个需求确实有点奇怪,直接实现也比较困难。我可以换个思路,A和B中间加一个设备C,用C来模拟鼠标连接A,同时C和B用串口线连接,B可以通过软件给C发指令,最终C传递给A。