最近在工作中,要求学习USB的通讯协议来解决一个USB与测试版连接不成功的小问题。之前未接触过任何关于USB的知识,相当于现在什么都是重新学习,现打算记录一下自己的学习经验与过程。
在刚接触USB协议前,我是先阅读了圈圈大神的《圈圈教你玩USB》前两章作为入门的铺垫。圈圈大神写得很通俗易懂,并且在第一章里介绍了很多关于USB协议的基本知识,所以整个阅读下来很有收获的。后面的章节因为硬件上的限制,所以也就没有继续再阅读下去,而是选择了手上现有的51增强型芯片与附带的例程。
我的学习习惯是从知识的架构入手,手上有两本关于USB协议的纸质书,与一份51开发板厂家提供的USB例程,就是这份例程无法识别我所接入的USB设备,但是这份例程拿来作为入门却是很不错的。
经过一周多的摸索,我个人认为的USB协议的学习架构应该是从USB设备接入到主机进行数据传输以及到拔出的过程,通过整理USB协议在这一过程中所执行的每一个步骤,来描绘属于我的学习框架。整理材料如下:
1) 插入USB设备
2) 主机的集线器识别到有USB连接,然后通过中断的方式提醒主机,有USB设备接入
3) 主机像集线器发送GetPortStatus请求,读取下行端口的状态及状态变化
4) 主机等待100ms左右,消除拔插抖动
5) 主机向集线器发出SetPortStatus请求,复位这个USB设备,在设备复位的过程中,集线器读取USB设备传输速度的类型
6) 10ms复位等待,复位成功后设备会进入缺省状态,主机与设备通过端点0进行通讯(主要是枚举过程的通讯)
7) 主机通过端点0发出Get_Descriptor(Device)请求,这个请求只读取设备描述符的前8个字节(用来确定端点0能传输的最大数据包长度,其实也可以通过直接告诉主机长度值来取消这一步的执行)
8) 主机通过端点0发出Set_Address请求,为设备分配一个唯一的设备地址。之后设备将通过这个地址与主机进行数据的传递,除非USB拔除或者主机断电这个地址才会被擦除。
9) 主机向新的设备地址发出Get_Descriptor(Device)请求,读取设备描述符的全部信息。
10)主机循环发送Get_Descriptor(Configuration)请求,读取USB设备的全部配置信息。一般主机第一次发送这个Get_Descriptor(Configuration)请求只读取前9个字节,主要是为了获取该配置描述符的全部长度。
11) 主机根据usb设备的信息选择一个合适的USB设备驱动
12) 加载驱动后,主机发送Set_Configuration(x)请求,为USB设备选择一个合适的配置,并要求设备按这个配置进行设置自己。
(以上步骤完成了USB主机与设备直接连接的过程,接下来则可以正常进行数据的传输)
13) 主机与设备通过七种(IN事务、OUT事务、PING事务、SETUP事务、SOF事务、SPLIT事务、PRE事务)不同类型的传输事务来与不同传输速度的USB设备进行数据的交换。其中每一次完整的数据传输都可以看做是一次事务。
14) 如果设备与主机太长时间不进行数据传输,主机发送SetPortFeature挂起命令让设备将进入挂起状态。
15) USB设备拔出。首先集线器会禁止USB使用的端口,然后通过中断告诉主机端口状态有变化,然后主机发送GetPortStatus请求来了解情况,如果是拔出则进行断开操作,并释放USB设备驱动以及其所占用的资源。
以上15个点就是我整理出来的关于USB协议学习的框架(我知道这其实也就是对USB枚举过程的一个扩展,枚举过程在USB协议的学习中也占据了很重要的一块),在这个框架的基础上,去解读那一份例程里的程序框架。USB协议自然不止这么一点,但是框架完成了,剩下的就是通过理论与实践的结合来一点点补齐其他细分的知识以达到对整个协议的理解。这一篇文章也是作为我学习USB协议的一个记录,如果日后发现有什么问题我会一点点修改的。因为是新手,我知道自己整理的这份材料自然还有很多不足的地方,我会一直记录在工作中关于USB协议的学习过程,若有不足,望大家告诉我一声。