来自:http://blog.csdn.net/hgd_dingjun/archive/2007/08/17/1747864.aspx
本系统中所谓USB设备与主机是通过检测Vcc上拉电阻的变化来确定是否有设备连接的。在D12内部集成了1.5kΩ的上拉电阻,默认状态下不与Vcc相连,程序运行时可以向D12发送连接命令使1.5kΩ电阻连接到Vcc,这样主机便检测到有设备连接。
它的枚举过程分析如下:
1、综述
设备连接到总线后,设备从总线获得5V电源,程序首先初始化,端口,然后向D12发出USB连接命令。主机检测到设备连接。主机向设备发出第一个信 号:总线复位。总线复位产生一个中断,并且D12器件在默认地址0处使能,以便在接下来的枚举过程中使用地址0传输命令和数据,同时中断寄存器的总线复位 位被置为1。在程序中的表现是,D12向主循环请求中断,进入中断处理程序USB_int_handler(),读取中断寄存器,确定中断的类型,进行相应的处理。
主机使用默认地址0读取设备描述符。
2、具体步骤
【阶段1,从地址0发送标准请求包】
具体过程是:主机向D12发送第一个Setup包,每个Setup包都是8个字节,第一个包Get Descriptor的内容为:80 06 00 01 00 00 40 00 ,数据为16进制表示。其中的40表示返回的数据最大长度为40H字节。此Setup包存储在D12的端点0缓冲区中,并产生一个外部中断。(这时在 D12的中断寄存器中保存了中断的类型:端点0的OUT中断,即中断寄存器字节1的值应为0x01)进入中断服务程序后,由于D12端点0的缓冲区只有 16个字节,所以单片机就先发送16个字节的设备描述符。
当主机接收到这16个字节的字符后,就认为真正有设备连接了。
【阶段2,地址分配】
主机向D12发送第二个Setup包,这是一个含有指定地址的数据包,其内容一般为:00 05 02 00 00 00 00 00 ,其中的02就表示主机为设备分配的地址为0x02,在以后的通信里设备就只对0x02地址的信息作出应答。D12收到这个Setup包后同样产生一个中 断(端点0的OUT中断),需要注意的是单片机处理这个中断时需要向主机返回一个长度为0的空数据包。
【阶段3,主机从新的地址获取设备描述符】
主机收到设备发来的空的应答数据包后,确认地址分配成功。然后主机向D12发送第三个Setup包,再次要求获取设备描述符。这个Setup包的内 容一般是:80 06 00 01 00 00 12 00 。与上次不同的是,这次要求 实际的描述符长度,其中的12(十六进制数)表示要求得到全部18字节 的设备描述符。因为每次只能发送16字节,因此程序中要 分两次完成此要求。第一次16字节,第二次2字节。
【阶段4,读取配置描述符】
成功得到18字节的设备描述符后,主机向D12发送第四个Setup包,要求得到设备的配置描述符。这个Setup包的数据为:80 06 00 02 00 00 09 00 。其中的09指定设备返回9字节数据,这正是配置描述符的长度。
【阶段5,读取一系列的描述符集合】
这个阶段可以读取n多种描述符,如果知道了设备方,即我们的68013固件是怎么写的,这个步骤直接跳过了。进入传输阶段了即可:)
成功得到9字节的配置描述符后,主机向D12发送第五个Setup包,要求得到设备的配置描述符、接口描述符、端点描述符的集合(不包含字符串描述符等 )。这次Setup包的内容是:80 06 00 02 00 00 FF 00 。由于不知道描述符集合的真实长度,因此它要求得到256字节 。
注:原文是错的,真实长度是可知的,就是走完上一个阶段,即第四阶段后,得到的配置描述符结构体如下:
// 标准配置描述符
__packed typedef struct
{
BYTE bLength;
BYTE bDescriptorType;
WORD16 wTotalLength;
BYTE bNumInterfaces;
BYTE bConfigurationValue;
BYTE iConfiguration;
BYTE bmAttributes;
BYTE bMaxPower;
} CONFIG_DESCRIPTOR;
其中,wTotalLength成员即是阶段5需要获取的数据总长度。
到这一步,主机现在应该已经发现新硬件并为新设备安装好驱动程序。对于以上过程,主机是在总线驱动层处理,下面的一步,也是典型枚举过程的最后一步,就需要设备驱动程序来做了。
数值配置。主机得到各种描述符之后,认为设备的信息已经齐全,便对设备进行配置,使设备从地址状态进入配置状态。
【阶段6,配置】
主机向D12发送第六个Setup包,其数据为:00 09 01 00 00 00 00 00 。程序中需要调用Set Configuration()函数处理此事件,允许所有端点进入工作状态。
至此,USB枚举过程结束,设备可以正常使用了。在这个过程中D12指示灯根据通信的状况间歇闪烁。
USB 最主要的的是要理解 USB主机发送命令给设备,设备要对主机的命令进行响应, USB通讯的基本单位为 “包” 理解好“包”这个概念是学习USB的关键所在。
包有如下分类:
分别是令牌包、数据包、握手包和特殊包(其实是由PID决定的)
令牌包:可分为输入包、输出包、设置包和帧起始包(注意这里的输入包是用于设置输入命令的,输出包是用来设置输出命令的,而不是放据数的)其中输入包、输出包和设置包的格式都是一样的: SYNC+PID+ADDR+ENDP+CRC5(五位的校验码)
帧起始包: SYNC+PID+11位FRAM+CRC5(五位的校验码)
数 据包:分为DATA0包和DATA1包,当USB发送数据的时候,当一次发送的数据长度大于相应端点的容量时,就需要把数据包分为好几个包,分批发 送,DATA0包和DATA1包交替发送,即如果第一个数据包是DATA0,那第二个数据包就是DATA1。但也有例外情况,在同步传输中(四类传输类型 中之一),所有的数据包都是为DATA0,格式如下:
SYNC+PID+0~1023字节+CRC16
握手包:结构最为简单的包,格式如下
SYNC+PID
下面举几个例子来说明USB的通讯过程:
1:主机想要向设备传送一串数据。 过程如下:
(1) 主机向从机发送 “令牌包”,令牌包的类型为输出包,表示主机要向从机发送数据了。
(2) 主机向从机发送完令牌以后,USB处理器件根据发送的令牌,会将中断状态寄存器标志置位,从机CPU通过查询USB处理器件的中断状态寄存器,对主机的令牌包进行响应
(3) 从机判别出中断类型,于是,准备从主机接收数据。
(4) 从机准备好了,于是主机开始发送“数据包” 这时,USB处理器件会自动将从主发送过来的数据放如它的内部缓冲区内,接收完这个数据包后,从机向主机发送“应答包”
这就是一个完整的通讯过程。
由以上可以看出,USB若是想要传送数据,那么主机必须先发一个 IN 或OUT的令牌包,然后发送DATA0,或DATA1数据包。
简单的用现实生活中的事件进行描述: 老板想让员工去做一件事情,老板 先会发出命令,告诉要做什么事情,员工准备好以后呢,老板再把做这件事情的经费发放给员工,当员工把发放的经费清点以后,发现数目正确,他会给老板一个回 应信息,告诉老板,钱已经收到了,而且数目正确。
老板想让员工做的事: 对应USB通讯里的令牌包。
老板想要发放的经费: 对应USB通讯里的数据包。
员工给老板的回应: 对应USB通讯里的握手包。
这里尤其需要注意一个问题就是:
USB主机向设备发送令牌包的时候,接收令牌是有USB器件来完成的,而不是有从机CPU来完成的,如主机发送一个如下的令牌:
SYNC+PID+ADDR+ENDP+CRC5
USB 器件回根据PID的类型来判断是哪种类型的令牌 根据ADDR的值来判断是否是和自己通讯,根据ENDP的值来判断是和哪个端点进行通讯,根据校验来判断,数据传送是否无误。根据以上的令牌包信 息,USB器件会将其内部的中断状态寄存器相应的位置位,从机CPU可以查询这个中断状态寄存器来进行相应的操作。
另外补充:
在写HOST驱动时,控制传输分为三个阶段:①建立阶段。②数据阶段。③确认阶段。建立(setup)阶段都是由USB主机发起,它是一个setup数据包,里面包含一 些数据请求的命令以及一些数据。如果建立阶段是输入请求,那么数据阶段就要输入数据;如果建立阶段是输出请求,那么数据阶段就要输出数据。如果在数据阶 段,即便不需要传送数据,也要发一个0长度的数据包。数据阶段过后就是确认阶段。确认阶段刚好跟数据阶段相反,如果是输入请求,则它是一个输出数据包;如果是输出请求,则它是一个输入数据包。确认阶段用来确认数据的正确传输。
如果控制传输中,发送的数据包为0,那么数据包阶段必须跳过。