第五篇 USB设备枚举过程(2)

二、复位总线

主机第一次正确接收到设备描述符,就会复位一次总线,接下来发送设置地址的标准请求。

1. 协议分析仪数据

第五篇 USB设备枚举过程(2)

这次复位是正常复位。

如果主机第一次获取设备描述符之后,一直复位(异常复位),很大情况下是因为获取设备描述符的过程有错(返回的数据或者数据长度都有错)!比如:在一次验证FPGA的过程中,设备栈刚移植结束,跑起Demo之后,发现获取设备描述符后,Host一直复位总线,如下:

第五篇 USB设备枚举过程(2)

将数据展开,仔细分析后发现有错:返回的长度和数据内容都有错。

第五篇 USB设备枚举过程(2)

应该是18字节的设备描述符,而这里返回的是64字节的错误数据。说明底层设备栈能解析指令,但是在回复设备描述符处理上有误。

三、设置地址阶段

主机在收到第一个数据包(设备描述符数据包),确认无误,下发一个0长度的确认数据包之后,复位总线,接下来就进入设置地址阶段。也就是说,接下来下发的是一个设置地址请求。

1. SET_ADDRESS请求

设置地址请求也是一个USB标准设备请求。这是一个分配地址的过程(主机给设备分配一个地址)。既然是标准请求,那么它也是有8个字节的数据。指定的地址包含在wValue字段中。主机从地址为0的设备获取设备描述符,一旦第一次成功获取到设备描述符之后,主机就会立刻发送设置地址的请求,减少设备使用公共地址0的时间(每个设备插入,都先被复位,默认的地址为0,也就是0地址是所有的USB设备的初始化地址,即可以理解为公共地址)。

(1) 设置地址请求的结构

设置地址的请求结构如下:

bmRequestType

bRequest

wValue(2Byte)

wIndex(2Byte)

wLength(2Byte)

数据过程

0x0000 0000

0x00

SET_ADDRESS

0x05

设备地址

0x0000

0x0000

没有

说明:

1. 设置地址的请求过程,是没有数据的,所以,数据长度就是0。

2. 索引也用不着(请求字符串描述符,索引才用得多),所以也为0。

(2) 实例

主机下发的数据包为    00          05         1D 00     00 00    00 00

数据方向:主机--->设备 设置地址请求  地址数据为0x001D=29

协议分析仪中的地址数据

第五篇 USB设备枚举过程(2)

分析:

wValue域的值是0x001D。转成十进制就是29,也就是主机分配给设备的地址是29。USB数据传输使用的是小端模式,低字节在前,所以,在协议分析中的数据就是:1D 00。

8个字节的标准请求数据是这样传输的:

左                                               右

<--------------------------------------------------------------------------------->

低字节                                        高字节

<--------------------------------------------------------------------------------->

前                                                后

<--------------------------------------------------------------------------------->

先发送                                         后发送

1)设备解析到地址数据之后,并没有能立刻使用。当设备成功读取到地址数据后,就进入状态过程(数据传输原本使用的是DATA0数据包,现在要切换到DATA1,这就是数据包的切换过程,这个过程也是芯片自动完成的),等待主机读取0长度的状态数据包(确认数据包)。书记成功读取到状态数据包之后,使用握手包ACK回应。至此,一次请求完毕,过程有点复杂,但是能确保地址数据已经传送给设备了(控制传输方式,过程很复杂,但是能保证数据的可靠性)。可结合协议分析仪里面,第2个事务来看。主机ACK响应设备之后,设备就启用新的地址了。接下来的一系列数据通信,使用的不在是0地址,而是新分配的地址。

2)这个过程有两个事务(两次数据传输)。经ACK回应的数据,对用户是可见的【程序能捕获到这部分数据,并进行分析】。

下面代码分析的过程仅供参考。

 

2. 代码分析

设置地址也是一个标准请求,代码流程和获得设备描述符是一样的。

2.1 端点有数据,则回调usb_handle_control_transfer()

打印:Transfer: ep=0x00, status=0x00

打印:Data Direction: Host---->Device

 

2.2 有标准请求,则回调usb_handle_standard_request()

打印: usb_handle_standard_request is called first

 

2.3 具体的请求,则调用usb_handle_std_device_req()

打印: REQ_SET_ADDRESS, addr=0x11

 

2.4 标准SETUP数据包,各个域的打印usb_print_setup()

打印:

SETUP_Packet_Dat: 

bmRequestType=0x00 

bRequest=0x05 

wValue=0x0011 

wIndex=0x0000 

wLength=0 Byte

 

2.5 返回0长度的状态数据包用到两个函数:usb_data_to_host() 和 usb_dc_ep_write()

打印:usb_data_to_host:  usb_data_to_host

打印:usb_dc_ep_write :  send_0_bytes_to_Host!

 

串口打印跟踪如下:

第五篇 USB设备枚举过程(2)

 

四、再次获取设备描述符

设备设置好地址之后,又收到了主机获取设备描述符的请求,并且这次的请求,使用的是新的地址,也就说明地址设置成功了(这次的获取设备描述符请求,是为了确认地址是否已经设置成功)。如果上一阶段,设备这边没有成功设置地址,也就是说,地址还是0,那么主机的这次请求肯定得不到回应,主机会检测到超时!进而复位总线,又回到第一阶段……,如此反复。

也就是说,某次请求,如果检测到超时(设备没给主机回应它想要的数据),那么主机就会进行复位,把复位信号作为依据,就可以知道某次请求是否异常(某次事务是否异常)。这里需要注意的是,第一次,也就是主机获得设备描述符后,复位总线,这次的复位是正常的复位,不属于异常复位。

1. 协议分析仪捕获到的数据:

第五篇 USB设备枚举过程(2)

这个过程,能说明两点:

(1)主机请求设备设置某个指定的地址,已经成功请求,并且设备已经启用了新的地址。

(2)接下来设备能使用新的地址和主机进行通信了。

下面代码分析的过程仅供参考。

 

2. 代码分析

和第一阶段的,请求设备描述符是一样的。设备返回18字节的设备描述符就可以了。下面是串口跟踪打印:

第五篇 USB设备枚举过程(2)