本文章主要来自《圈圈教你玩USB》的学习笔记
USB包的结构
USB是串行总线,所以数据是一位位的在数据总线上传输,采用LSB在前的方式。
USB数据需要经过位填充和NRZI编码。在这里讨论时,所用的数据都是原始数据,即没有经过位填充和NRZI编码的原始数据。
USB总线上传输数据是以包为单位的,一个包分为不同的域。根据不同类型的包,所包含的域是不一样的。
包格式:同步域+包标示符PID+...+包结束符EOP
同步域用于告诉USB的串行接口引擎,数据要开始传输了。同时还可以同步主机端和设备端的数据时钟,因为同步域是一串0开始的,而0在USB总线上就是被编码为电平翻转。全速设备和低速设备同步使用的是00000001;对于高速设备使用的是31个0,后面跟着1个1。
包结束符EOP,对于高速设备和全速/低速设备也是不一样的。全速/低速设备的EOP是一个大约为2个数据位宽度的单端0(SE0)信号,SE0的意思就是,D+,D-同时保持为低电平。由于USB使用差分数据线,通常是一高一低。SE0是一种都为低的特殊状态。SE0用来表示一些特殊含义,例如包结束,复位信号等,前面提到USB集线器对USB设备进行复位的操作,就是通过将总线设置为SE0状态大约10ms来实现的。对于高速设备的EOP使用故意的位填充来表示。
包标示符PID用来标示一个包的类型,它总共8位,其中USB协议使用的只有4位(PID0~PID3),另外4位((PID4~PID7))是(PID0~PID3)的取反,用来校验PID。
USB协议规定了4类包:
- 令牌包
- 数据包
- 特殊包
- 握手包
不同类的包又分成几种具体的包。
令牌包
所有的数据都是主机发起的,发送一个令牌包来通知哪个进行响应如何响应。
令牌包有4种:
> 输出令牌包用来通知设备要输出一个数据包
> 输入令牌包用来通知设备返回一个数据包
> 建立令牌包只用在控制传输中,它跟输出令牌包的作用一样,也是通知设备将要输出一个数据包,两者的区别在于:SETUP令牌包后只使用DATA0数据包。且只能发到设备的控制端点,并且设备必须要接收,而OUT令牌包没有这些限制。
> 帧起始包在每帧(或微帧)开始时发送,它已广播的形式发送,所有USB全速设备和高速设备都可以接收SOF包。USB全速设备每毫秒产生一个帧,而高速设备每125us产生一个微帧。USB主机会对当前帧号进行计数,在每帧开始时(或者微帧开始时,每毫秒有8个微帧,这8个微帧帧号是一样的,即相同的SOF)通过SOF包发送帧号。在4个令牌包之后不跟随数据传输。
SOF包的作用和使用场景?可以参考下面的文章
USB协议详解第20讲(USB包-帧首包SOF) - yooooooo - 博客园
SOF包的发送时间:
USB总线包组成:
数据包
数据包用来传输数据
在USB1.1协议中,只有DATA0包和DATA1包。在USB2.0中又增加了DATA2和MDATA包,主要用在高速分裂事务和高速高带宽同步传输中。
数据包结构:
之所以有不同的数据包,是用在握手包出错时纠错。
主机和设备都会维护自己的一个数据包切换机制:当数据包成功发送或者接收时,数据包类型切换。
当检测到对方使用的数据包类型不对时,USB系统认为这发生了一个错误,并试图从错误中恢复。
数据包类型不匹配主要发生在握手包被损坏的情形。当一端已经正确接收到数据并返回确认信号时,确认信号却在传输过程中被损坏。这时另一端无法知道刚刚发生的数据是否已经成功,这时候它只好保持自己的数据包类型不变。
如果对方下一次使用的数据包类型跟自己的不一致,则说明它刚刚已经成功接收到数据包了(只有正确接收才会如此);如果对方下一次使用的数据包类型跟自己的一致,则说明对方没有切换数据包类型,也就是说刚刚的数据没有发送成功,这是上一次的重试操作。
握手包
用来表示一个传输是否被对方确认
握手包只有同步域,PID和EOP。格式如下:
握手包类型有:ACK、NAK、STALL和NYET
> ACK表示正确接收数据,并且有足够的空间来容纳数据。主机和设备都可以用ACK来确认。(NAK、STALL、NYET只有设备能够返回)
> NAK表示没有数据需要返回,或者数据接收正确但没有足够的空间来容纳它们。当主机接收到NAK时,知道设备还未准备好,主机会在以后合适的时机进行重试传输。
> 表示设备无法执行这个请求或者端点已经被挂起,它表示一种错误的状态。
> NYET只在USB2.0的高速设备输出事务中使用,它表示设备本次数据成功接收,但没有足够的空间来接收下一次数据。主机在下一次输出数据时,将先使用PING令牌包来试探设备是否有空间接收数据,以避免不必要的带宽浪费。
当USB主机或者设备检测到数据出错时(如CRC检验错,PID检验错,位填充错等),将什么都不返回。这是等待握手包的一方就会收不到握手包从而等待超时。
特殊包
在一些在特殊场合使用的包,有四种类型:PRE、ERR、SPLIT、PING。其中PRE、SPLIT、PING是令牌包,ERR是握手包。ERR、SPLIT、PING三个是在USB2.0中新增的。
> PRE通知集线器打开其低速端口的一种前导包。PRE只使用在全速模式中。平时为了防止全速信号使低速设备误动作;集线器没有将全速信号传递给低速设备的。只有当收到PRE令牌包时,才打开其低速端口。PRE令牌包与握手包的结构一样,只有同步域、PID、EOP。当需要传递低速事务时,主机首先发送一个PRE令牌包(以全速模式发送)。对于全速设备,将会忽略这个令牌包。集线器收到这个令牌包后,打开其连接了低速设备的端口。接着主机就会以低速模式给低速设备发送令牌包、数据包。
> PING令牌包与OUT令牌包具有一样的结构,但是PING令牌包后并不发送数据,而是等待设备返回ACK或着NAK,以判断设备是否能够传送数据。USB1.1没有PING令牌包。只有在USB2.0高速环境中才会使用PING令牌包,它只被使用在批量传输和控制传输的输出事务中。
> SPLIT是高速事务分裂令牌包,通知集线器将高速数据包转化为全速或者低速数据包发送给其下面的端口。
> ERR握手包是在分裂事务中表示错误使用。
如何处理数据包
一般的USB接口芯片会完成如CRC校验、位填充、PID识别、数据包切换、握手等协议的处理。
当USB接口芯片正确接收到数据时,如果有空间保存,则它将数据保存并返回ACK,同时,设置一个标志表示已正确接收到数据;如果没有空间保存数据,则自动返回NAK。
收到输入请求时,如果有数据需要发送,则发送数据,并等待接收ACK。只有当数据成功发送出去(即接收到应答信号ACK)之后,它才设置标志,表示数据已成功发送;如果无数据需要发送,则它自动返回NAK。
通常只需要根据芯片提供的一些标志,准备要发送的数据到端点,或者从端点读取接收到的数据即可。所要发送和接收的数据是指数据包中的数据,至于同步域、包标示、地址、端点、CRC等是看不到的。在
BUS Hound中抓到的数据也是如此,仅是数据包;并且,BUS Hound中只能看到成功传输的数据,即只有ACK确认过的数据包。
在USB接口芯片中,通过一些标志可以知道哪个端点接收或者成功发送了数据。另外,由于控制传输比较特殊,SETUP包也会有相应的标志提供我们使用。
例如D12有如下寄存器,提供标志,表示当前包是否为setup包: