cisen / blog

Time waits for no one.
135 stars 20 forks source link

通用串口总线(USB) 协议相关 #969

Open cisen opened 4 years ago

cisen commented 4 years ago

总结

为什么

中断模式(对接键盘,手柄之类的硬件)

中断传输是一种轮询的传输方式,是一种单向的传输,HOST 通过固定的间隔对中断端点进行查询,若有数据传输或可以接收数据则返回数据或发送数据,否则返回NAK,表示尚未准备好。中断传输的延迟有保证,但并非实时传输,它是一种延迟有限的可靠传输,支持错误重传。

中断端点的轮询间隔由在端点描述符中定义,全速端点的轮询间隔可以是1~255mS,低速端点为10~255mS,高速端点为(2^interval-1)*125uS,其中interval 取1到16 之间的值。

他们之间的区别也仅在于事务传输发生的端点不一样、支持的最大包长度不一样、优先级不一样等这样一些对用户来说透明的东西。

批量传输和中断传输在数据处理上是完全一样的,不同的地方就在于事务的调度上,以及端点最大包长以及是否支持批量端点等。

http://blog.chinaunix.net/uid-28320320-id-3415354.html

USB是共享总线,如果在USB HUB上有多个设备同时进行大数据量传输。例如设备A在传输4MB的数据,那么设备B在设备A数据传输期间有可能得不到响应。如何保证设备B在其它设备传输数据时也能及时得到响应?设备B可以设立中断传输端口,这样Host Controller会每隔一定的时间间隔(例如1ms),保证向设备B发出IN令牌,使得B有机会发出数据。 所以中断传输的数据量不能太大,在USB 1.1中最多只能16 bytes. USB 2.0的协议作了一些修改,中断传输与Bulk传输的区别不大,数据量的限制也没有了。不过,中断传输的上述机制还是在的。 如果USB的host controller总是被一个设备独占,那也就没有中断传输与Bulk传输的区别了。

cisen commented 4 years ago

USB HID 协议

https://www.cnblogs.com/xiaolei-kaiyuan/p/6118302.html

cisen commented 4 years ago

USB HID报告描述符教程

原文地址:Tutorial about USB HID Report Descriptors

什么是USB HID报告描述符? HID协议使得设备的实现变得简单,设备会定义数据包为HID描述符发送给主机。HID描述符是描述设备数据包的固定代码字节数组,包括设备支持多少个包,包有多大,以及包中每个字节和比特的含义。比如,带有计算程序按键的键盘告诉主机按键是按下还是松开状态,该信息放在数据包4的第6个字节的第2个比特,注意这个位置是设备指定说明的。设备通常将HID描述符存放在ROM里,不必深入理解或分析HID描述符。今天市场上的一些鼠标和键盘硬件实现仅仅使用一个8比特的CPU。--以上来自维基百科

简单一点开始,做一个标准的鼠标,三个按键,在X轴和Y轴上移动。因此要发送关于按键和移动的数据。每个按键用1个比特表示,每个字节表示移动一个轴上的有符号整型值,数据结构表示如下:

C语言格式如下:

因此在描述符中,第一个项目必须描述按键,包含三个字段:

每个按键的状态用1个比特表示,0或1:

有3个比特来表示:

发送这些变量数据到电脑:

最终表述按键的表述如下:

5个没有用的填充比特:

X轴的移动:

用1个字节的有符号整型,范围设成-127~127(实际上是-127~128,这样做是为了对称),来表示移动距离:

使用整个字节来发送:

当作坐标变量发送给电脑:

因此最终X轴移动的表述如下:

那么Y轴移动的呢?可以写成这样:

上述表述虽然没有问题,为了节省内存,可以表述如下:

综上,整个的表述是:

但是,这并未结束,为了让电脑知道这个是鼠标设备,必须这样:

因此在最后,下面是标准的鼠标USB HID报告描述符:

这实际上是USB HID文档上的例子,同样也是HID工具提供的例子。

现在有了一些概念,继续研究: Usage Pages(用例页):文档中好像解释的不够好,概念包含有USAGE、USAGE_PAGE、USAGE_MINIMUM、USAGE_MAXIMUM。在描述符中,首先设置一个USAGE_PAGE,某些可用的USAGEs。在鼠标的例子中,在USAGE_PAGE (Generic Desktop)允许你使用USAGE (Mouse),当用例页改为USAGE_PAGE (Button),允许在USAGE(X) 和USAGE(Y)之前给按键指定USAGE_MINIMUM和USAGE_MAXIMUM,再然后用例页变回到USAGE_PAGE (Generic Desktop)。用例页就像是命名空间一般,改变用例页作用于“usages”是否可用。请阅读文档“HID Usage Tables”获得更多细节。 Collections(集合):通过阅读文档关于集合的官方使用方法,用自己的话讲,集合用来组织数据,比如键盘可能內建触摸板,因此键盘数据需要保存在一个应用集合里,触摸板数据则保存在另一个应用集合里。可以给每个集合指定“Report ID”,具体将在后面叙述。

可以将USAGE (Mouse)变为USAGE (Gamepad),让电脑知道这是一个带1个操作杆和3个按键的游戏手柄。将PS2控制器变为USB游戏手柄怎么样?控制器有16个按键和两个拇指棒,数据结构如下:

C语言的数据结构如下:

让电脑知道这是一个游戏手柄:

按键的描述如下:

拇指棒的四个轴:

注:Z表示右边拇指棒的X轴,Rx表示右边拇指棒的Y轴。这看起来不合理,但是是目前大多数游戏手柄的工作方式。在《战地:叛逆连队2》上测试是可以工作的。 注:用“absolute”表示类似操作杆的东西,“relative”表示类似鼠标的东西。 结果如下:

两个玩家呢?这就显示出集合的方便之处:

填充上数据区域,得到:

这样看起来,在数据结构上加上Report ID真的很重要:

C语言数据结构:

在发送数据到电脑上之前必须手动的设置Report ID,目的是为了让电脑知道数据来自哪个游戏手柄。

也可以用Collections和Report ID设成综合性设备。因此包含键盘、鼠标和游戏手柄(2)的描述符如下:

当然数据结构也要增加Report ID:

由于改变了数据结构,设备不再支持启动协议,因此不用定义协议。相应的改变usbconfig.h。想要看到这个效果,USnooBie导入工程示例,Windows的“设备和打印”上显示: