《USB技术应用与开发》第五讲:USB主机操作HID设备

一、USB标准请求及描述符解析

1.1USB主机传输

(1)连接检测

在连接检测阶段,主机会识别设备的连接与拔除,USB 设备的通讯速度,并进行一些功能的初始化。

(2)主机枚举USB设备

主机会获取设备的所有的信息,并加载相应的驱动,这个阶段是非常重要的。

(3)有效数据传输、功能控制等

在枚举阶段,主机识别到设备后根据想要启用的功能,将相应的驱动程序,也就是相应的一些传输命令都准备好,然后在有效数据传输这个阶段进行运行。

1.2标准请求

控制传输的数据阶段是由主机发出或者设备回应的,但是前面的标准请求是固定的 8 个字节的。

标准请求是所有的 USB通用的,USB 设备都需要支持,一共有 11 个如下图所示。

11个标准请求

相关的知识点回顾如下图所示。

通过第一个字节的位6 、位5 决定当前的 Type (类型)是不是标准请求,当是 0  的时候表示是标准请求。
当是标准请求时,就会对应后面第二个字节显示出的表格,表格中列出了标准请求码,显示虽为 13 个,但是其中的第 2 和第 4 个用作保留,方便以后扩展使用,总共仍是上图中的 11 个。

1.2.1 CLEAR_FEATURE 清除特征

bmRequestTypebRequestwValuewindexwLengthData

00000000B
00000001B

00000010B

CLEAR_FEATUREFeature
Selector

Zero
Interface

Endpoint

ZeroNone

clear_future 作用是用来清除设备接口或者端点的某种特征或性能。

设备:对于设备,它可以用来清楚其远程唤醒功能。

接口:目前还没有对应接口类执行这个操作。

端点:对于端点,可以用来清除其停止工作状态。
端点之前讲过:端点是设备和主机进行传输的真实存在的物理通道。
当某一个端点出现问题的时候,会告知主机 store 的状态,告知主机“我这里出现了问题”。当主机若想重新启动这个设备的时候,或者准确来说应该是重新启用设备的这个端点的时候就会发出 clear_future 的命令,会在相应的 index 环节填充相应的要清除某个端点号。

 1.2.2 GET_CONFIGURATION 获取配置值

GET_CONFIGURATION:获取设备当前的配置值。这条命令一般是在主机给设备进行配置以后就会生效,它的作用是来查询当前设备是否配置好,是否配置生效。

1.2.3 GET_DESCRIPTOR 获取描述符

这个是在枚举过程中用的最多的一个请求,主机通过发出这个获取描述符的请求来读取设备的各种描述符,从而就可以获知设备类型,端点的一些信息。

标准的描述符包含了设备描述符、配置描述符、接口描述符以及端点描述符,还有字符串描述符等。在这些描述符中,接口描述符和端点描述符是跟随着配置描述符一并返回的,一般是没有单独请求可以获取到。
因为如果单独请求的话,主机是没有办法去确认它属于哪个配置下的。

在 value 字段就可以填充主机想获取的描述符类型。

1.2.4 GET_INTERFACE 获取接口 

获取设备的接口是根据接口当前工作的选择设置值。

每一个接口都可以有多个选择设置值,每一个值所代表的功能完全不同,并且每时每刻都只会有一个处于活动状态,也就是说接口的配置值都是互斥的工作状态。

1.2.5 GET_STATUS 获取状态

GET_STATUS是获取设备接口或者端点的某个状态。

这条命令如果是针对设备发出的,设备将会返回两种状态:远程唤醒和自供电的支持情况。

如果命令是针对接口去获取状态的话,一般返回值是两个字节的全 0 。

如果命令的对象是针对端点时,端点将会返回它是否处于停止工作状态,也就是刚才说的store,返回的数据长度固定是两个字节。

1.2.6 SET_ADDRESS 设置地址

主机为设备分配唯一地址使用的命令,这个命令一般在对设备进行配置的时候使用。主机通过设备的地址来区分不同的设备,它可以相当于走线上设备的一种标识,地址范围是 0 ~ 127。

0 就是默认地址,所有的设备一上来跟主机通讯的时候,都是以默认地址开始。当主机为设备分配了一个非零地址后,主机和设备进行的传输都是通过这个非零地址作为唯一的标识来使用。一般主机和设备连接后,在主机获取了第一次描述符之后,就会被设备分配地址,也就是下发这条命令,这样可以尽量减少设备使用公共地址 0 的时间。

1.2.7 SET_CONFIGURATION 设置配置

下发该命令的作用是为了激活设备的某个配置,一般设备可以有多种配置,但只有一种配置处于活动状态。

手动选择设备的某个配置让其处于工作状态,这个配置值需要与设备的配置描述符中的配置值编号一致。当表示一致时表示主机选中了该配置。

这个值通常为一,因为大多数的 USB 设备只有一种配置,配置编号为一。如果该配置为0,则会让设备进入地址状态,就相当于还在设置地址这个地方。

设备一般只有收到了非 0 的配置值后才能启用它的非 0 端点进行数据传输。

1.2.8 SET_DESCRIPTOR 设置描述符

该命令是为设备新增一个描述符或者更新一个已经存在的描述符,但是目前老师还没有看到过哪个设备用到过这个命令。

翻阅了一些论坛,也有些人表示一般 USB 设备都不会支持此命令。我猜测原因可能是因为设备一般都是厂家做好了某个功能去定义好了描述符信息不会轻易地支持主机更改。

1.2.9 SET_FEATURE 启动特征

表示主机要求启动一个在设备接口或者端点上的一个特征,它和上面的这个 clear_future 是对应的.

1.2.9 SET_INTERFACE 设置接口

用于主机激活设备的某个接口。

上面有提到设备的每个接口都可以有几种选择设置,但只有一个会处于工作状态,主机就可以通过该条命令去激活需要的接口设置,如果不选择的话,设备使用默认的选项。

1.2.11 SYNCH_ERAME 同步帧

该命令在实时传输中使用。在实时传输中,设备的端点可以一个次序来传输不同大小的数据包。这条命令主要是用于要求主机和设备在某个帧开始传输此序列。当端点收到这条命令后,它会传回一个新系列的一个帧号,因此这条命令是用于隐含同步的实时传输。一般很少有设备会支持这条命令。

1.2.12总结

不同背,使用的时候多去翻书。


非标准命令不对大多数的设备有效,类命令只对特定的类设备有用,厂商命令对专有的厂商提供的设备有效。重点需要看到的是:获取描述符、设置地址以及设置配置,这是在所有的设备枚举过程都会看到这三个。

其他的像 clear future 、set future 、get status 一般当主机睡眠唤醒会下发相应的 set future 和 clear future 这些命令,还有一些状态的时候会有 get status。

其他的命令比较少,对于多接口的设备才会出现这种 SET INTERFACE,GET INTERFACE 。

1.3 HID设备类结构

上述是 USB 主机的请求,通讯是相互的,既然主机发了请求,设备也需要有回应。

比如在主机经常获取描述符,在对于一个设备来说,里面的结构如下图。

上图是以 HID 设备类结构来举例。对于 HID 设备中,它的结构是设备描述符,下面是配置描述符,接着是接口,然后跟端点同步的是类描述符。

类描述符包含了报表描述符、实体描述符。报表描述符描述的是平时我们用的键盘鼠标所上传的这些数据,顾名思义就是对它上传的有效数据报表进行一个描述解析,描述其含义。
实体描述符可能一般人见到的会比较少,它其实描述的是一个物理性的一个结构特征,不太常见,我猜测可能因为现在一些鼠标键盘的物理结构已经比较固化常见了,没有什么特别的,所以在计算机端是有一个固有的描述信息,除非遇到一些特别的键鼠,可能会有相关信息上传。

1.3.1知识点回忆

每一个阶段都会有相应的这个描述符进行匹配

1.4主机枚举设备

1.4.1主机发出请求,获取设备描述符

八字节控制传输的标准请求结构。

80:表示后面跟随的数据是由设备传输给主机,它是一个标准请求。

06:代表的是获取描述符。

00,01:应该是0100(两个字节),在这个高字节位置上的 01 表示的当前的描述符请求的是设备描述符。

00,00:对设备来说的话是固定为 0 。

12,00:表示想获取的是 18 个字节。

(2)对应设备需要上传的结构内容如下

1.4.2设置设备地址

主机为设备分配地址,控制传世已经完成以后,设备收到这条命令开始记录地址,它的地址范围只有 1 ~ 127,也就是说主机只能挂载总共 127 个设备。

当总线复位或者设备断电再上电,地址就会失效,重新将其记录地址恢复到默认地址0。

配置完地址生效以后,主机后面所有通讯就会以该地址作为通讯的标识。

1.4.3获取配置描述符

(1)上图中写着“集合”,之前讲过设备有自己的配置描述符,但是主机下发这条命令的时候,一般上传的是一个集合,并且在命令中有写“需要获取的配置描述符的大小是 9 个字节(倒数第二个字节)”,所以设备上传的只有左下角 9  个字节这么多的配置描述符。

(2)上图解析了配置描述符,右边的完整配置描述符集合就不再介绍了。在我详细讲解键盘的那篇博客中应该有详细的注释。

1.4.4激活设备配置

前面 3 个完成后,主机会下发 set configure 设置配置,下图中第三位的 01 设置的配置值。主机一旦下传该命令,设备就会启用这个配置,下面就可以进行有效数据传输。

1.4.5 HID设备类主机常见命令请求

(1)set idle请求

对于 HID 设备类,有几个常见的类请求,210A 就是set idle请求,用来设置 HID 上传速率,下图中的参数为 0 ,表示当设备有数据改变的时候再进行上传。

(2)set report

该请求主机用于下传报表或者数据,下图中参数为 0 ,所以就是没有下传,正常情况下,对于键盘来说,会下穿一个字节,主要目的是用来控制键盘的灯点亮或者不点亮。

HID 设备的上端端点是必要的,因为设备需要告知主机人机交互的动作状态灯,但是不一定有下传端点(鼠标就没有),键盘主要是主机下传一个状态来表示点灯。

一般兼容性 HID 设备也可以通过这种控制传输的 2109 下传包来进行数据传输。
当然也可以建立一个 HID 下传端点进行下传。

(3)报表描述符

报表描述符对于 HID 类设备非常重要,主机是通过报表描述符了解 HID 设备上传的数据含义,下图是获取报表描述符和键盘上传的报表描述符示例。

上图中键盘的报表描述符含义在“键盘的那一讲”已经附上了,若有遗漏,大家也可以去 AI 一下,这里简单讲解。

①第一个字节:功能键

前面的所有行全部是为了修饰最后一行 Input,为了说明这是一个怎么样的 Input,Input 表示当前这个字节是一个上传数据,是一个怎么的上传数据(输入)呢:

第一行:这是一个键盘型的;

第二、三行:(键盘上的按键有一个编码表)说明这个数据的范围在编码表上的范围实际上是 224~231 这 8 位。

第四、五行:它的逻辑值只会是 0 和 1 。

第六、七行:它的大小是一个比特位(字节),共有 8 位。

总结:输入的数据就是一个可变的变量,这就是上传的 8 个字节中的第一个字节,查阅相关的PDF 文件可知这是一个功能键。

②第二个字节:一个常量(Constant)固定为0

③第三个字节:下传字节

其实就是点灯,一共 3 个灯,每个灯占用一位。

补齐 5 位,凑够一个字节。

④共 6 个字节

第一行:一共 6 个字节。

第二行:每一个字节 8 位 。

第三、四行:逻辑最小~最大是 0 ~101 。

指的是另外 101 个按键,并且是一个阵列(Array)形式,阵列形式就是可以多个按键同时按下。

1.5 报表描述符结构

HID 设备通过报表来交换数据,不知道你们好不好奇主机是 怎么理解出每一行后面的注释的。

HID 中的每一条都称为一个项目,项目分为短项目和长项目(很少使用,不讲解),下图是短项目的结构。

0~7 就是一个字节,后面跟随这着数据。下图中的每一行就是一个项目,并且都是一条条的短项目。

1.5.1第一个字节

第一个字节分成下面 3 个字段。

(1)bSize

bSize 可以表示是 0、1、2、4字节,后面跟随的 data 的数量就取决于该 bSize,当然这个字节数是简易的。

(2)bType

定义的是项目的类型,由主项目、全局项目、局部项目,上面讲解的 input、 output 和 collection 就属于主项目。

下图中的Logical Minmum 等四个项目就是全局项目,全局生效,意思就是如果没有重新去改动的话,它在所有中都是生效的。

局部项目表示局部在一个主条目中才会生效。

(3)bTag

表示的是项目的具体功能。下图中项目就是根据 bTag 来区分是怎么分类的。

第一个字段相当于把所有的东西列好,后面的数据就是用来解析的,像下图。0x95 就是第一个字段,非常重要,然后后面的数据就是 8  。

(4)长条目(不解释,自行拿取)

 1.6报表描述符条目解析器(主机HID驱动)

对于主机来说,当设备上传了一个报表描述符之后,主机会有类似下图的报表描述符条目解析器,会根据上传的内容解析出当前报表描述符中一行行提取信息,存在一个一个条目的状态表中,如下图。

英文原版
中文翻译版

然后后续上传数据的时候,主机就可以继续出对应的含义。

1.7传输与事务

在之前提到的 传输 与 事务 ,这两个概念是针对USB接口的范围,如果再往上一层来到具体的 USB 产品类别,比如之前一直说的 HID 类设备,该类设备在本身的 USB 传输中出现了报表的概念,我们认为这已经是一种数据的协议了,每一次有效的传输我们就称之为一个报表。

但是之前在讲“端点描述符”时,有一个最大包的概念,我们认为这个包是实际的物理传输,其中的报表已经设计导有效数据的解析,一个报表可以很长,但是一个报表超过包的最大长度就会进行分包传输,但是主机必须收到一个完整的报表才会上报解析。

也就是说如果一个包只允许是 8 个自己额,但是报表有 50 个字节,那么会分为多个包,直到主机收到这完整的 50 个字节,主机认为是一个完整的数据后,再上传到相应的驱动层去解析其含义。

就像之前用串口通讯一样,串口会设置包头包尾的数据格式,当中断或者暂停后接着传,都必须收到完整的一串数据,才会认为这一帧结束。

二、软件实现及效果演示

(up主发现这个上课老师的代码不是现场带着敲,而是这讲一点,那里讲一点,up主整理笔记真的有点浪费时间和头大,之间的“键盘”和“鼠标”已经耗费太多时间了,所以从这里往后,老师同样方式的代码讲解,up主都简单书写思路了。等把这个系列更新完,up找到其他手把手带着做USB项目的课在逐行发布笔记呦~  <预计5月中旬>)

2.1硬件平台

仍然是CH549,它的 USB 端口除了有设备功能,还有主机功能。

主要看这款单片机作为主机时怎么操作HID类设备。

2.2软件框架

CH549作为主机,是主动发起方。

1、延时200ms后执行总线复位1ms:

延时之前第二讲主机枚举的时候讲过:为了给设备 100 ~ 200 毫秒的设备稳定时间,因为有的主机供电。
发起总线复位:作为一种传输的复位信号。

2、获取设备速度并记录,打开USB外设端口(自动发送SOF/EOP)

(1)获取设备速度:它会通过 D+ / D- 线上的上拉电阻 来判断设备的速度。

(2)并记录:主机会记录设备是 Low / Full / High 速,为后续传输配置合适参数。

(3)打开USB外设端口(自动发送SOF/EOP)

  • USB主机控制器准备好与设备通信;

  • 并开始主动发出时钟同步信号 —— 也就是 SOF 帧,为所有设备提供“节拍”和“时间管理”。

  • EPO 是主机或设备在每个 USB 包结尾时自动加上的标志,告诉另一端:“我这次传输结束啦!”

一般控制器都会自己产生sof,不用软件产生。因为这个作用是定时的,这样子不会占用 CPU 自己的时间。

来自第一讲

3、空闲 10 秒

相当于做一个通讯稳定。

4、枚举

在讲解完主函数后,需要说明的是:

这个代码不像设备需要去响应主机的请求,所以需要采用的中断的机制,有一个时间性的要求,但是作为主机,所有的通讯基本上都是主机发起的,所以本讲的代码并没有写成中断的形式

2.3效果演示

2.3.1按键

 

2.4讲解重要函数

下面讲解两个比较重要的函数:

1、执行基本事务传输函数USBHostTransact(X...)
2、执行控制传输函数1HostCtrlTransfer(X...)

多个事务组成传输,setup、in、out包都是一个一个的事务,是USB基本传输的基本单位。

控制传输的三个结构是很固定的,而且会经常用到,将其分成一个函数比较方便。

2.4执行基本事务传输函数

该函数有三个参数:

1、UINT8 endp_pid:传输事务的PID,对应事务是 setup 还是 in 还是 out 。

2、 UINT8 tog:target 只针对 in 事务和 out 事务,对应数据有 data1 有 data0。

3、UINT16 timeout:主机发 in 包,设备可能会回 NAK ,可以将 timeout 理解成 NAK  的次数。

在信息交互时,有以下的中断点。

但是在主机中没有开中断,是利用查询中断标志,下面的代码中有一个 for 等待中断传输完成,UIF_TRANSFFR 就是查中断标志,去停止传输,再下面的一点的代码都是在判断当前设备回应的东西。

等待中断标志,然后决定当前是 ACK 、NAK 以及插拔情况。

2.5执行控制传输函数

建立阶段、数据阶段、状态阶段

发 set up 包,判断是否成功 → 设置 PID  → 里面若有数据传输  → 状态阶段是以 out

结束还是 in 结束

三、知识梳理

不要小瞧下面的第一条:主机连接设备以后,首先是检测插入移除这个动作,然后它会发出总线复位的信号,很多情况下设备的兼容性做不好或者发出设备请求设备不回应,大部分的情况都是因为前期总线复位、空闲时间、发 soft 包这些东西没有把握好。这种情况有些控制器会帮你做,有些控制器会将控制权交给你,所以为了后续更好的兼容性,必须去看看协议规定好的时间。

第二条:当检测完以后会通过控制传输去获取一些描述符,这些描述的作用就是为了分析出这个设备的类别,可以决定后面发什么样的数据。
一般作为电脑的话,它可能会有好多的设备驱动。作为一些裸机或者单线程的东西,可能都是针对一些固定的设备,所以这个地方我们可以不用做的那么复杂,就是正常的枚举识别出,最重要的是识别出设备的一些接口号、端点号,可以分析出当前设备是不是你要的,它是不是复合设备,然后可以进行端点控制 clear future、 set future 的时候里头会加一些接口的索引,这时候都会用到。

第三条:当出现异常的情况下,一般会执行总线复位,比如 CPU 运行有问题,可能摁一下复位键让它重启。同样当作为一个主机,通过一个 USB 接口去控制一个设备,当出现问题的时候,你又不能接触到本身设备自己的,就通过这种类似总线复位,就相当于通过协议去告知设备去重启一下,让它恢复一下状态。
挂起:比如说休眠的情况可以让设备挂起,其实就是像设别反馈一个问题:你可以进入低功耗,你不愿意进入低功耗也行。

第四条:在计算机上会有一层一层的,它会有一些传输的队列、事务带宽,尤其到现在出现的 3.0 上,在这些带宽、队列分配上就是会更加的精准,为了提速。自己做产品的时候,做主机的时候,如果想为了提高更快的速度,要考虑你本身代码中除了 USB 是否还有一些其他的分配,还有一个作为USB,你如果下面挂了多个设备,通过 Hub 之类挂多个设备的时候,如何去分配这个传输的这个优先级等等。

最后最重要的是第五条:所有的事务令牌都是由主机发起的,设备在事务中给数据包回应答。


本专栏说明:本人是USB的初学者,该专栏是我CSDN上第一个学习USB技术的专栏,笔记会比较口语,不是很精炼,后续有点基础后,争取“字字珠玑”

本文参考:

《USB技术应用与开发》第五讲:USB主机操作HID设备_哔哩哔哩_bilibili

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值