


版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第1章 U 盘的规律构造1.1 U 盘的规律构造U 盘可以看成是以扇区1 扇区512Bytes为单位线性排列的实体,即 0 号扇区,1 号扇区,2 号扇区,这样按挨次地排列下去。U 盘是 flash,对 flash 的操作总是以块为单位的,因此单片机对U 盘的操作是以扇区为单位,整个扇区地读取,或整个扇区地写入。第2章 USB 通信协议USB 设备开工的机理USB 是即插即用的,涵盖海量存储器如U 盘、移动硬盘、人机交互设备如鼠标键盘玩耍杆、扫描仪、打印机等等各种各样功能的设备,那么 USB 主机是如何推断目前接入的设备到底是怎么样的呢?答案是 USB 描述符,以及 USB 的枚举。USB 描
2、述符这个概念很简洁,就是对各种纷繁芜杂的 USB 外设按功能划分大类(class),大类下又再细分小类(subclass),每个类别赐予一串特定的符号Descriptor供主机辨识。每个 USB 设备只能有一个 DEVICE 描述符,它指明白该设备属于哪一大类,是海量存储器类,还是人机交互设备类,还是打印机或者扫描仪类,等等。每个 DEVICE 下可以有 1 个或多个配置描述符configuration,以说明该设备含有哪些功能。如一个 USB 接口的 CDROM 可以同时具有读写光盘的功能和播放 CD 的功能。有几个功能,就有几个配置描述符。每种配置对应假设干个接口描述符Interface
3、,以描述该配置使用哪些接口与主机进展通信。每个 Interface 又都是端点End Point的集合,端点就是设备与 USB 主机交换数据的最原子单位了。每个 Interface用到的端点可以是一个 或多个。以下图摘自 USB MASS STORAGE CBI Transport Specification 第 6 页,清楚说明各种描述符的组织状况。USB 设备的枚举过程开工过程有了完善的分类后,USB 设备上电即可通过枚举过程告知 USB 主机自己的具体信息,这很类似一个一问一答的过程,如下: 主机下称 H:你是甚么设备?设备下称 D:我是 12 01 0100 这就是 DEVICE 描述
4、符了 H:你有几种功能?D:我有 09 02 09 配置描述符H:每种功能有几个接口? D:09 04 00 接口描述符 H:每个接口用到哪些端点?D:07 05 81 端点描述符H:好了,我知道你是谁了,开头传数据吧! D:OK. READY GO!具体而言,USB 枚举过程有以下步骤:集线器检测设备主机集线器监视着每个端口的信号电压,当有设备接入时便可觉察。集线器端口的两根信号线的每一根都有 15k 的下拉电阻,而每一个设备在 D+都有一个 1.5k 的上拉电阻。当用 USB 线将 PC 和设备接通后,设备的上拉电阻使信号线的电位上升,因此被主机集线器检测到。主机知道了设备连接后每个集线器
5、用中断传输来报告在集线器上的大事。当主机知道了这个大事,它给集线器发送一个 Get_Status 恳求来了解更多的消息。返回的消息告知主机一个设备是什么时候连接的。集线器重设置这个设备当主机知道有一个的设备时,主机给集线器发送一个 Set_Feature 恳求,恳求集线器来重设置端口。集线器使得设备的 USB 数据线处于重启RESET状态至少 10ms。集线器在设备和主机之间建立一个信号通路主机发送一个 Get_Status 恳求来验证设备是否激起重启状态。返回的数据有一位表示设备仍旧处于重启状态。当集线器释放了重启状态,设备就处于默认状态了,即设备已经预备好通过 Endpoint 0 的默认
6、流程响应把握传输。即设备现在使用默认地址 0 x0 与主机通信。集线器检测设备速度集线器通过测定那根信号线D+或 D-在空闲时有更高的电压来检测设备是低速设备还是全速设备。全速和高速设备 D+有上拉电阻,低速设备 D-有上拉电阻。以下,需要 USB 的 firmware 进展干预猎取最大数据包长度PC 向 address 0 发送 USB 协议规定的 Get_Device_Descriptor 命令,以取得缺省把握管道所支持的最大数据包长度,并在有限的时间内等待 USB 设备的响应,该长度包含在设备描述符的 bMaxPacketSize0 字段中,其地址偏移量为 7,所以这时主机只需读取该描述
7、符的前 8 个字节。留意,主机一次只能列举一个 USB 设备,所以同一时刻只能有一个 USB 设备使用缺省地址 0。主机安排一个的地址给设备主机通过发送一个 Set_Address 恳求来安排一个唯一的地址给设备。设备读取这个恳求,返回一个确认,并保存的地址。从今开头全部通信都使用这个地址。主机向地址重发送 Get_Device_Descriptor 命令,此次读取其设备描述符的全部字段,以了解该设备的总体信息,如 VID,PID。主机向设备循环发送 Get_Device_Configuration 命令,要求 USB 设备答复,以读取全部配置信息。主机发送 Get_Device_String
8、 命令,获得字符集描述unicode,比方产商、产品描述、型号等等。假设主机是PC 电脑,此时主机将会弹出窗口,呈现觉察设备的信息,产商、产品描述、型号等。依据 Device_Descriptor 和 Device_Configuration 应答,PC 推断是否能够供给 USB 的 Driver,一般win2k 能供给几大类的设备,如玩耍操作杆、存储、打印机、扫描仪等,操作就在后台运行。加载了 USB 设备驱动以后,主机发送Set_Configurationx命令恳求为该设备选择一个适宜的配置(x 代表非 0 的配置值)。假设配置成功,USB 设备进入“配置”状态,并可以和客户软件进展数据传
9、输。此时,常规的 USB 完成了其必需进展的配置和连接工作。查看注册表,能够觉察相应的工程已经添加完毕,至此设备应当可以开头使用。以上是 PC 电脑为主机的枚举过程,对于单片机作为主机的情形,过程要简洁一些,以枚举 U 盘为例:芯片 SL811 监视 USB 总线电平,当觉察有 U 盘插入后,给单片机一个中断信号。单片机给 SL811 发出端口复位命令,持续 100 毫秒以上。单片机发出 Get_Device_descriptor 命令,从默认的端口 0 和地址 0 发出。该命令先假设了包传送的大小是 64 字节,在获得命令返回时修正 MaxPacketSize。此步同 PC。单片机发送 Se
10、t_Address 恳求来安排一个唯一的地址给 U 盘,我们实际应用中固定安排了地址 2。此步同 PC。单片机向地址 2 重发送 Get_Device_Descriptor 命令,此次读取 U 盘设备描述符的全部字段,以了解该设备的总体信息,如 VID,PID。此步同 PC。单片机发送 Get_Configuration_Descriptor 命令猎取配置描述符。依据猎取的配置信息,单片机发送 SetConfig 和 SetInterface 命令对U 盘进展配置。对猎取的 Interface 描述符和 Endpoint 描述符进展分析,推断是否大容量存储设备、是否支持 SCSI 命令集、是否
11、BULK_ONLY传输、端口的最大包长等内容。发送 Get_Max_LUN命令猎取 U 盘的进一步信息依据协议看此步非必需,有些U 盘此步会返回 STALL,即不支持,也没有关系。但是建议在枚举过程中不省略此步,由于不同品牌 U 盘其固件可能不一样,有些固件可能不允许省略此步。完成上述步骤后,U 盘的枚举过程完成,接着需要发送几条 SCSI 命令来对U 盘进行初始化,这几条命令依次是 Inquiry、ReadFormatCapacity、ReadCapacity。完成后,U 盘已经预备好接收单片机发出的任何读写命令读写命令也是来自 SCSI 命令集。假设你有兴趣知道 USB 协议一些更细节的内
12、容,请往下看。否则可以直接跳到第四章的文件系统局部。USB1.1 协议本节内容主要涵盖 USB 1.1 Specification 的第 4、5、8、9 章。并且主要描述代码中无法注释或者在代码中注释会太麻烦的内容。USB 是一种主从的构造。全部传输由 Host 来发起。当主机发起一次传输时,这次传输的包Packets 通常包括三个阶段。主机首先是发送一个Token Packet,内里包含本次传输的命令类型type、方向direction、设备的地址device address以及端点号Endpoint。紧接着是数据包data packet,就是包含数据了。最终将由 device 返回握手信号
13、包handshake packet,表示是正确收到了ACK还是其他的失败缘由。三个包如以下图所示。USB 的传输模型:Host 和设备的某个端点之间可以看成有一条规律管道pipe。Pipe分两种:业务数据流和信令消息。业务流即指纯粹的数据,信令流指把握信息。其实通信协议很多都如此,分业务流和信令流,例如电信网中的 7 号信令。在信令管道中,有一条默认的管道,那就是零地址处的零号端点,这条管道在USB 设备上电复位或总线复位后就存在了,便于 Host 统一利用这个地址向 USB 设备进展配置。明显对于 USB 集线器,即使同时插入几个设备, Host 也只能一次对一个设备进展配置。USB 设备只
14、有配置configured后,才可使用。USB 的传输类型有四种:把握传输control transfer,通常只用于在设备复位后 Host 通过端点 0 进展配置。块传输(Bulk Transfer),譬如U 盘的大量数据传输即用此方式。中断传输(Interrupt Transfer),一般用于人机设备如USB 鼠标键盘等。等时传输(Isochronous Transfer),可以进展带宽把握的实时传输形式。重生疏枚举过程枚举过程事实上是 USB 设备复位后,恢复到 0 地址 0 号端点,然后主机通过一系列把握传输命令对 USB 设备进展配置,同时也猎取一些信息。使用 BUS hound 这
15、个工具可以把完整的 USB 设备枚举过程抓下来。网上很简洁找到安装包。BUS 软件的设置如下:可以确保抓下全部的数据包信息。利用 BUS hound 的软件抓一下爱国者行业特供型 1G 的 U 盘,其在 PC 下的枚举过程完全在以下图中表现了出来。让我们逐一分析。由设备 16.0 抓到的数据包属于USB 集线器的行为,在无使用集线器的单片机系统中可以无视之。设备 21.0 的含义是:usb 设备地址是 21,目前管道是跟它的端点 0 打交道。数字 1 处是枚举过程的开头,主机用把握传输发送 Get Descriptor 猎取设备描述符具体为何是设备描述符可以比照USB1.1 技术标准的第九章来
16、分析左边的那串 08 06 00 02 的二进制数据,下同,这条命令假设了未知设备的端点 0 的最大包长值 64 字节,然后在命令中要求设备返回 0 x12十进制 18个字节的device 描述符,如图中圈起来的 12。值得一提的是,这条命令无论假设设备的端点 0 的最大包长Payload是 8,16,32,64,都是可以获得想要的数据的图中的 40。40H 指明该设备的端点 0 的最大包长是 64 字节,Host此后的把握传输可以使用 64 字节的数据包跟设备通信了。64 字节数据包的细节后面会介绍。数字 2 处表示主机发送 Get Descriptor 猎取配置描述符。但是类似设备描述符的
17、处理方法,主机也先摸干脆的猎取配置描述符的前 9 个字节图中带圈的 09,以得悉整个配置描述符有多长,由于长度信息就位于描述符的第 3 个字节,如图中带圈的 20。依据此 20h 的长度信息,数字 3 处开头正式恳求设备完整的配置描述符了,可以在图中看到两个 20 是对应的。整个配置描述符包含 32 个字节一般的U 盘都是这样。这 32 个字节中,包含3 局部内容,包含设备的重要信息。数字 5 所代表的第一个框表示第一局部:配置描述符。数字 6代表其次局部:接口描述符Interface 。其中第 5 字节 02 表示该设备有 2 个端点Bulk_IN和 Bulk_OUT,第 6 字节 08 代
18、表这是大容量存储设备Mass Storage Device。第 7 字节 06表示支持 SCSI 命令,不过我调试过MP3 播放器这个地方是 05,但也支持SCSI 命令。第 8 字节 50 表示数据只支持使用Bulk传输Bulk Only。更具体的内容可参考 USB Mass Storage Class Bulk-Only Transport Revision 1.0。数字 7 和 8 代表第三局部:端点描述符。第三字节都是代表端点地址,一般状况是地址和地址 2。留意图中 8 框第三字节是 82H,这表示该端点地址就是地址 2,最高位被置 1 以表示这个端点是 Bulk_IN 端点,所以整个
19、数值变成了 82H。但是并非地址 2 就肯定是Bulk_IN,不同的 U 盘不一样,所以在程序中要依据描述符的实际值,用变量记录下来的, 后面要用到。连续看 8 框,第五和第六字节组成一个 16 位的数值表示该端点的最大包长度payload。对于只支持 1.1 协议的设备,第六字节是其高 8 位,都是 0,第五字节才是真正的 payload 数值,1.1 协议规定只能是 8,16,32,64 之一,由厂家固定。至于我们在上图看到第六字节是 02 第五字节是 00,组合成 200H512,那是由于 U 盘把握器认出了这个HostPC 电脑支持usb2.0,所以就回应了512,而不是 64。Pay
20、load 值格外重要,后面要依据此值进展推断和计算。下面接着的 4 个 get descriptor 都是猎取设备的string 描述符。实际的单片机系统或许不需要猎取这些描述符,而且有些 U 盘也不支持猎取这个描述符返回 STALL。再往下的就是 set_configuration、set_interface、get_MAX_LUN 等。有些 U 盘,在 set_interface 处会stall; 有些 U 盘,假设 Host 不发送 set_interface 命令,往后的命令都不响应,所以这个牵涉到兼容性问题,后面再解释。在上图中可以看到,这个爱国者 U 盘在遇到 get_MAX_LU
21、N 时返回了 stall,PC 的处理方法是 clear feature,然后重试,三次后仍旧stall 则跳过。这个牵涉到如何进展过失处理,后面再具体分析。基于 SL811 的 USB 底层传输函数实现要点前面枚举过程介绍的各种命令,如截图中的数字 1 处的“80 06 00 01 00 00 12 00”到底是如何发送出去的,这或许是大家比较感兴趣的问题。有必要先简洁生疏一下 SL811 的功能,虽然这跟 USB1.1 协议几乎无关。SL811 供给了 15 个存放器供使用,实际在进展 USB 传输时最少只需要用到其中 6 个, 另外还需要用到 SL811 内建的 240 字节RAM 作为
22、数据缓冲。启动 SL811 发送/接收一次数据留意不是一帧数据,一帧数据一个数据包的步骤如下:目标 U 盘的端点地址和 pidSL811 存放器地址 0 x03目标 U 盘的地址地址 0 x04811 内部 RAM中数据缓冲的地址地址 0 x01该次数据的长度(不是该帧数据的长度)地址 0 x020 xff地址 0 x0d启动发送的命令字地址 0 x05当这一次数据成功了后,假设 pid 是“发送”,则 SL811 内部 RAM 中缓冲处的数据都被发出去了;假设 pid 是“接收”,则 SL811 内部 RAM 中缓冲处会填满来自 U 盘的数据,长度等于上面黑体字第四行之设定。单片机应当在此时
23、准时把 SL811 的内建 RAM 中这些数据读出来,放进单片机自己开拓的内存区域。看的出来SL811 对 USB 的物理层已经完全封装了,但是设计者还是需要关心很多细节。对 SL811 总线式的读写函数太简洁,这里不提了。那是我们这种解决方案下最底层的函数。关键是 usb.c 中的 usbXfer和ep0Xfer这两个函数。usbXfer函数借助分析这个函数的实现可以了解 USB 的传输模型及过失处理。分析一下它的输入参数。int usbXfer (BYTE usbaddr,/ USB 设备地址 0-127BYTE endpoint,/ 端点的地址 0-15,对于 U 盘无非就是 0,1,2
24、 BYTE pid,/ 数据包 token 类型,包括 setup,pid_IN,pid_OUT BYTE iso,/ 是否使用等时传输,对于 U 盘,该项恒否WORD wPayload,/本数据包的最大包长,又称净荷。WORD wLen,/ 待发送或接收的数据的实际长度BYTE *buffer/ 待发送的数据的缓冲首址,或者将要接收数据的缓冲首址)首先要了解pid。Setup 型的pid 只消灭在把握传输阶段,即usb 设备复位配置阶段。Pid_IN 和 pid_OUT 可能消灭在把握传输阶段和此后的 Bulk 传输阶段。顾名思义,pid_IN 表示 Host 打算通过这次 usbXfer,
25、从 U 盘读进来wLen 长度的数据,放入 buffer 中。Pid_OUT 表示 Host 打算向 U 盘把握器发送 wLen 长度的数据或者写入 U 盘wLen 长度的数据,这些数据已经在buffer 预备好。其次是要理清 wPayload 和 wLen 的关系。wPayload 传递过来的是该端点的最大包长, 在前面枚举中分析那 32 字节的配置描述符时应当已经记录了下来。在 USB1.1 的标准里只能是 8,16,32,64 这四个值其中之一,实际上我依据SL811 的打印只见过 8 字节和 64 字节 payload 的 U 盘,而且那些 8 字节 U 盘只是端点 0 是 8 字节,
26、BULK 端点也是 64 字节的。应当目前来说多数 U 盘都是 64 字节的1.1 的范围内。言归正传,由于 USB 设备的端点有最大包长的限制, SL811 启动一次数据传输时必需保证不超过这一限制,因此,在往 SL811 的 0 x02 地址发送数据长度前,应作一推断,取wPayload 和wLen 之中的较小者。C 代码为:xferLen = (BYTE)(wLen=wPayload) ? wPayload:wLen);当 wLenpayload 的时候,一帧数据就需要 SL811 启动屡次传输才能完成。接着让我们依据代码来分析 usbXfer函数的流程。建议比照着附件中 usb.c 的
27、代码来看。代码的图在前,分析在后,下同。简洁的函数说明,列出了返回值意义。函数开头的变量定义,有些可以顾名思义,有些等后面用到了再解释。红框处需要解释一下。EP0_Buf 值为 0 x10,它的意义是 SL811 内建 RAM 的起始地址。对于 SL811 的内建 RAM 只需要用到其中 2payload 个字节,而且是掰开两半来轮换使用。SL811 的应用笔记称之为乒乓缓冲。举个例子,假设手头遇到一个 U 盘其端点 0 是 8 字节 payload 的。在枚举 U 盘时 host 恳求 U 盘返回它的 32 字节配置描述符。此时属于 wLen 大于 payload 的情形,需要 SL811
28、启动 4 次传输才能完全把 32 字节数据收回来。首先如上图所示,我们初始化 data0 指向 SL811 的 RAM 起始地址,data1 初始化为指向data0 + payload 处,并初始化 SL811 的缓冲指针存放器为 data0。然后启动 SL811 发起第一次传输,让 SL811 把第一批 8 字节收到 data0 处,单片机马上读走这 8 字节;修改 SL811 的缓冲指针存放器为 data1,启动其次次传输,把其次批 8 字节内容收到 data1 处,单片机也马上读走此 8 个字节。依次轮换,直到 4 次传输过后,32 字节完全收了回来。平心而论这个作法有点多余,既然是单片
29、机马上读走数据的,始终用 data0 就可以了。不过上述做法是Cypress 公司供给的例程,很多人都照搬了大家知道是怎么回事就行了。这就是前面说的取 wLen 和 Payload 中的较小者作为一次传输的长度。等效于前面那行C 代码:xferLen = (BYTE)(wLen=wPayload) ? wPayload:wLen);Cmd 变量是等会要写入 SL811 的把握存放器 0 x00 地址的值。对于 Pid_IN 的 token 类型在这里进展预置。sData0_RD = 00100011B,其含义指:该次数据包的 sequence bit 定为DATA0,产生同步帧 SOF,数据方
30、向是 IN即读 U 盘,使能传输EnableARM。具体参考 SL811 的数据手册。解释一下其中的 Sequence bit,USB1.1 协议规定,每个数据包data packet都必需包含一个 sequence bit,用于纠错。收发双方的软件也要各自维护自己的 sequence bit。Sequence 位要么是 DATA0,用 0 表示,要么是 DATA1,用 1 表示留意此 DATA0 与前面的乒乓缓冲的 data0 没有半点关系,重名纯属巧合。USB1.1 协议第 185 页描述了何为一次成功的数据包收发。在第 i 个数据包发送前左图,TX 方的seq bit 是 DATA0,于
31、是它填充数据包的 seq bit为 DATA0。RX 方成功收到了这个数据包,于是将自己的 seq bit 切换到 1 即 DATA1,并返回一个 ACK 应答表示成功收到了。当 TX 方收到 ACK后,也切换自己的 seq bit 到 DATA1。右图的第 i1 个数据包就类似了。协议在第 186 页描述了一次重发数据包的情形。同样先看左图,第 i 个数据包由 TX 发出,但是由于各种缘由 RX 收到坏corrupted 了的包。于是RX 方拒绝切换自己的 seq bit,并返回 NAK 给 TX 方返回stall 或者 timeout 等情形类似 NAK。此时 TX 方由于收到的不是 AC
32、K,不能切换 seq bit,只能仍旧以 DATA0的 seq bit 组装数据包重发,假设如右图RX 方接收了这个包了,这才是一次成功的收发,RX 和 TX 各自的seq bit 发生切换。如以下图是一次把握传输涉及到的三个 packet,其中中间那个是data packet,红框处就是其 sequence bit。连续分析代码。对于PID_OUT,1 框处就是将待发送的命令填进SL811 内部 RAM 的data0 缓冲处。Cmd的预置同上面 PID_IN,只是方向变成了 OUT 而已。2 框处正是上面提到的切换发送方的 Seq Bit,并反映在 cmd 中。明显这跟协议规定的只有在收到
33、ACK 才允许切换 seq bit 相悖,但是 Cypress 公司的官方例程是这样处理的又被 Cypress 耍了一道,尽信 code 不如无code,有空我会把这局部代码改正来。对于 Setup Token 的处理。当使用等时传输的时候置 cmd 的某位。但对于玩 U 盘,这行其实可以删除。对于把握传输阶段endpoint = 0,IN 或者 OUT 的 pid 都使用 DATA1 的 seq bit。而对于 setup pid,都承受 DATA0 的 seq bit。这是由协议规定的。没什么好说的。最终可以依次填写 SL811 的存放器,启动一次传输。下面开头推断发送是否成功。开头进入
34、while 循环,循环读入 SL811 的状态存放器,推断假设是总线复位或者设备中途拔出则直接返回1,以示错误。但假设推断到DONE 了,表示传输完成,跳出while, 清中断标志,读入本次传输的结果 result,并读入本次传输的剩余字节数 remainder。下面将依据 result 的各种不同状况进展处理。开头涉及到USB 传输的过失处理。返回 ACK返回 ACK 是最正常的情形。针对不同的 pid,有不同的处理。对于 OUT 和 setup token, 假设返回了 ACK,函数可以直接返回了,返回 wLen。对于 IN token,稍为简单。但其实前面已经提到过了,由于wLenpay
35、load,需要连续启动 SL811 进展传输,并把数据放于乒乓缓冲中。请看代码。数字 1 处,修正剩余的wLen,切换seq bit,dataX 加 1。dataX 的作用就是用来计算接下来应当使用乒乓缓冲中的 data0 还是 data1。数字 2 处,推断之前的传输是否一个字节都没收到应当不会消灭这种状况。否则认为之前的传输是成功的,xferlen 长度的数据全部收到了SL811的RAM缓冲中,赋值给buflen,告知单片机从 SL811 的缓冲中读取 buflen 长度的内容。数字 3 处,仅当 wLen 还有剩余,且上次的传输已经全部完成 remainder 为 0 的状况下, 再次启
36、动 SL811 的传输。推断 dataX 的奇偶性就可以推断应当使用乒乓缓冲的 data1 还是data0。数字 1 处,单片机准时从 SL811 的 RAM 中读取刚收下来的内容到 buffer 中,并更buffer 的位置。数字 2 处,假设wlen 或者 remainder 等于 0,认为本次usbXfer 已经IN 了全部需要的数据,函数成功返回 wLen。假设仍未收完,则会返回到前面的 while,等待下一次的 DONE 完成,再重复对 result 的推断,直至函数从这里返回。返回 NAKNAK 意味着 USB 设备临时无法返回数据给 host。依据 USB 的协议,返回 NAK
37、的可能缘由有如下:设备端处于流量把握状态。目前 host 的数据发送太频密,为防止设备自己的缓冲溢出,设备向 host 发送 NAK,意为临时不要再发数据过来,等设备缓一口气。设备的端点正在忙Busy C端点没有数据可传输给 host。D端点进入了 halt 状态对于 A 和 B 情形,host 应当连续尝试向设备重发包。C 情形一般消灭在中断传输里。D 情形,host 在重试了足够次数后,应当尝试使用clear feature 命令去除端点的状态。程序段推断对 NAK 的重发次数是否已到达 1000 次,否则稍稍延时一下,然后重发送。重发超过 1000 次后,返回40,说明这是 NAK 重发
38、失败的返回。我之前调试的时候NAK 只设置为最大重发 20 次,每次延时 5 毫秒,结果有些U 盘很简洁返回 NAK错误。现在看来,NAK 的重试次数可以很多,重试间隔可以很短,但太多也不适宜,会让人感觉到U 盘的初始化时间很长。任何时候都不要无限重发,会造成死循环。返回 Timeout设备返回 timeout 的缘由一般是收到无法识别的包unrecognized或者坏包(corrupted) 。除了重试外没有什么特别的处理方法。重试的代码类似 NAK 的。如以下图。其他的返回,如 stall,overflow,EP0_ERROR这几种错误假设反响了回来,不是 usbXfer 函数能够处理的,
39、唯一能做的就是向上一级函数返回正确的错误代码,让上级调用函数来处理。至此,usbXfer 函数也完毕了。如以下图。ep0Xfer函数ep0Xfer 顾名思义是特地跟端点 0 打交道的函数,负责把握传输即枚举 U 盘的任务, get_descriptor、set_address、set_configuration、set_interface、get_MAX_LUN、clear_feature 等命令都是用这个函数组装发送的。把握传输具有前面提到的 USB 传输模型中典型的三个阶段:主机发送 setup token、主机发送数据包、主机接收来自设备的握手包又或者称是 status stage。圈
40、1 处是把输入构造 setup 中的成员作必要的字节交换处理,由于字类型的数据有高位字节和低位字节的大小端对齐问题我比较懒,直接弄了个数组来做这个事情,一目了然。圈 2 处通过调用 usbXfer 把 setup token 发去给设备。留意把握传输肯定是发给端点 0, setup token 的长度都是 8 字节,例如前面的get descriptor “80 06 00 01 00 00 12 00”。接着就是传输数据包的阶段。要推断 setup token 中的第一个字节bmRequest 的最高位, 假设是 1,表示该命令要求设备向主机返回数据,例如 get descriptor 命令
41、,此时传递给 usbXfer的 pid 必需是 pid_IN。反之,就是 pid_OUT 了。看得出假设 usbXfer 返回错误一般是stall,端点 0 错误,overflow 等,ep0Xfer 也不作处理,直接返回 FALSE 给上一级。事实上假设在 configuration 阶段都消灭这种错误,说明硬件上有问题,单片机也作不了什么事情,只能报警。最终就是接收设备返回来的握手包。三个阶段顺当完成后,ep0Xfer 函数成功返回。块传输Bulk前面提到 USB 有 4 种传输类型,块传输是其中之一,普遍用于U 盘这样的大容量存储设备。usbXfer() 函数写好以后,块传输的函数就很简
42、洁实现。需要实现的函数有两个: BulkSend( ) 和 BulkRcv( ) 。顾名思义这是 BULK发送的函数。待发送的数据存放于 pBuffer 起始的地方,总长度是buflen。函数主体的工作就是把 buflen 的内容一个 pkglen 一个 pkglen 地发出去。这是 BULK 接收函数。实现思路格外类似 BulkSend( ),不需多说了。SCSI 命令跟 U 盘初始化有关的 SCSI 命令就 U 盘而言,当枚举过程的最终一条命令 Get_Max_Lun 执行完,开头进入发送 SCSI 命令初始化 U 盘的阶段。具体的 SCSI 命令集请参考USB Mass Storage
43、Class UFI Command Specification 和 SCSI Block Command -2。使用 BUS hound 软件能完整的分析这一过程。连续以我手头的爱国者 U 盘的 bus hound 打印来分析。见以下图。从 U 盘上电枚举过程最终阶段说起,即图中数字 1 的位置。三次 Get_MAX_LUN尝试都患病 STALL 后,windows 打算无视 Get_MAX_LUN,开头发送 SCSI 命令了。第一条就是 Inquiry,如图中红色字。命令是用 Bulksend( )发送的,总长 31 字节,其中红框中的小圆圈处的 12H 标志着这串数据正是 Inquiry
44、命令。Inquiry 命令要求返回 36 字节的设备信息, 即图中的“aigo Miniking 8.07”那串东东了。这是使用 BulkRcv ( )接收的。接着看到数字 2 的那个红框,这是由 U 盘返回的 Inquiry 命令执行完后的状态字 CSWCommand Status Wrapper,也是使用 BulkRcv ( )接收的。顺便说说上面那31 字节的命令准确描述应当是CBWCommand Block Wrapper,CBW和 CSW的细节可参看USB Mass Storage Class Bulk-OnlyTransport Revision。数字 2 下面的那个红框就是一条比
45、较简洁出问题的命令:ReadFormatCapacity。小圈中的 23H 是该条命令的“身份证”。有些 U 盘在收到这条命令后返回 STALL,可以从上图看得出,windows 执行到这里也收到了STALL,由于它紧接着进展了一次 REST,如上图蓝色直线所示。关于这个 REST,我查阅了 BUS hound 软件的帮助、翻看了手头上全部跟 U 盘有关协议文本,勾上了 BUS hound 的其他捕获选项重试捕获,结论是:这是 windows USB 驱动层的一次 RESET,好似在硬件上并无产生什么信号或指令给 U 盘,也不是 Command Block Reset,更不是 USB 总线的
46、Port Reset。这个地方我始终期望搞清楚 USB 总线上发生了什么事情,但苦于没有 USB 分析仪,有了解的朋友期望能不吝指教。我在单片机的程序里对 ReadFormatCapacity 这里的 STALL是这样处理的。先执行一次Command Block Reset ,然后连发两条 Clear Feature 命令去除端点 1 和端点 2 的 Halt 状态。具体看 usbmsc.c 中的代码吧,很简洁。连续分析流程。看以下图。蓝 带 REST处 是接 着 上一 张 图 最终 部 分的 。 REST后, windows获 取 了ReadFormatCapacity 命令的 CSW,留意
47、最终一个字节是 01H用圈圈住的,表示 U 盘对 ReadFormatCapacity 的执行出了问题。依据协议,需要主机发送 RequestSense,从图中可见RequestSense 命令的“身份证”是 03H.RequestSense 收到 18 字节数据和 13 字节的 CSW, 一切正常。Windows 于是重试 ReadFormatCapacity。这次在收到 20 个数据后,估量在猎取CSW 的阶段,windows 再次被U 盘 STALL了。此时windows 又进展了一次RSET又到了我不懂的地方了.。RSET 后,就正常收到 CSW 了。然后执行的命令应当是 ReadCa
48、pacity,其“身份证”是 25H。这条命令正常执行完后,主机对 U 盘的初始化最终最终完成鸟。Read 和 Write U盘的命令读和写 U 盘只实现了“整个扇区地读”和“整个扇区地写”两个函数。两个函数几乎一样。以读扇区函数为例进展讲解。读扇区的命令原型也是来自 SCSI Primary Command -2 技术标准,乃是其中的 READ10 命令。如上图所示,SCSI_Read( )的输入参数之中,lba 是地址,等于将要读取扇区确实定扇区号,pBuffer 指向读入数据的缓冲区。程序开头先执行另外一条 SCSI 命令TestUnitReady,该命令功能如其名,测试 U 盘是否已经
49、预备好。假设连续执行超过 3 次都失败,则 read 函数失败返回。这一段是在构造总共 31 字节的 CBW。我比较懒,直接用数据一个字节一个字节地去填,便利省事,就是铺张了一点点程序 ROM。接下来的就是例行公事,依次发送CBW,接收 512 字节的扇区内容,接收CSW,然后返回。严格来说每次 CBW 发送后,收到 CSW 时都需要推断 CSW 的合法性,以及 CSW 所返回的命令执行状态。我的 demo 程序里都没有这样的处理除了 ReadFormatCapacity 和TestUnit。有兴趣的朋友很简洁自己加上去。U 盘兼容性问题的探讨到目前为止,我自己还没有彻底解决兼容性问题我 TC
50、L 了。不过假设有 USB分析仪,有 30 个以上不同牌子的 U 盘给我测试的话,想必会有更多的成果共享给大家。附件中有我通过 BUS hound 和串口抓下来的十个不同 U 盘的打印为了做这件事情,我把四周同事的 U 盘都借光了。U 盘兼容性问题,很大程度上是对协议的理解程度问题,我是这样觉得的。假设时间充分,最好把以下的协议通读理解:(找不到的话就问 computer00 要吧hiahia)USB Mass Storage Class UFI Command Specification Revision 1.0;USB Mass Storage Class Control/Bulk/Int
51、errupt(CBI) Transport Revision 1.0; USB Mass Storage Class Bulk-Only Transport Revision 1.0;USB Specification Revision 1.1;SCSI Block Commands -2 (SBC-2) Revision 8; USB Specification Revision 2.0;以下从 U 盘插入 host 开头,按大事发生的时间轴挨次介绍我处理兼容性问题的阅历。BUS hound 软件和串口打印是我唯一的武器。1检测到 U 盘插入后,总线复位时间建议持续 400 毫秒以上。我帖一
52、下 BUS hound 抓的windows 对 USB 总线的复位时间就很清楚的说明问题。爱国者 miniking 1G:金士顿 2G:读卡器带 512Msd 卡:爱国者行业特供型 1G:还有很多个 U 盘,见我的附件中,根本都需要 300 毫秒左右。保险点就 400ms。2严格依据第一条 Get Device Discriptor 命令的返回猎取端点 0 的 payload有些 U 盘的端点 0 是 8 字节 payload 的,大多数是 64 字节。对于 8 字节 payload 的 U 盘,要严格从第一条 Get Device Discriptor 命令返回的第八字节猎取 payload
53、,然后传递给ep0Xfer,否则尽管枚举过程仍旧能pass,但是后面在分析32 字节的配置描述符时候会出问题,没有收到完整的配置描述符是分析不出端点 1 和端点 2 的地址以及端点 payload 的。在枚举阶段猎取 string 描述符的时候,要推断一下。有些 U 盘不支持反响 string 描述符,此时要用if 语句推断一下,假设猎取不到string描述符就跳过,不要失败返回。枚举阶段,set configuration 后不要省略 set Interface,有些 U 盘的固件假设收不到set Interface 死活不让你过,这个问题不少网友的阅历帖中已经提及。对于 ReadForma
54、tCapacity 和 ReadCapacity 的处理帖一下发送 SCSI 命令初始化U 盘的那段代码:如框中所示 ,假设 ReadFormatCapacity 返回失败,不 要马上 失败返 回,应尝试RequestSense,前面已提到了。至于怎样才算 ReadFormatCapacity 失败,请看以下图:圈 1 处是经常发生stall 的地方了,假设返回了 stall,BulkRcv 的返回就是失败,然后使用两条 ClearFeature 去除端点 1 和端点 2 的 HALT状态,留意 Bulk_IN 端点地址要加上0 x80。如此操作后,接着去到圈 2 处,去猎取 CSW,但通常都
55、会猎取到最终一个字节非 0out12不等于 0,表示本条命令的执行有问题,这样需要返回 FALSE,让上级程序调用 RequestSense。ReadCapacity 的处理类似 ReadFormatCapacity。进展读和写 U 盘之前,适当做一些摸干脆动作这在某位网友的帖中也提到了,参考 BUS hound 的打印,看看windows 在读写 U 盘前做了什么,我们不妨学着做。自己抓一次 BUS hound 就知道了,U 盘初始化完成后,即使在空闲时,windows 也不停地对之发送 TestUnitReady 命令来检查它的状态。我认为在单片机系统中没必要负担这种开销,但是在 SCSI
56、_Read ( ) 和SCSI_Write ( ) 的开头我也学习windows 参加了 TestUnit 命令。7) 写 U 盘的稳定性问题这个问题的根因应当是不同 U 盘的速度不一样,flash 的搽除是相当耗时间的,而且有的快有的慢。这个状况下只有增加等待时间和重试命令两种途径比较可行。我选择了增加 等待时间,在 SCSI_Write ( )函数中发送完 CBW 后,插入一个 100ms 的延时汗,假设都这样搞,拷个电影要多长时间。我这样做是由于我们的应用中对写 U 盘的速度要求很低。网友有些是用重发命令的,应当更合理。第3章 微软的文件系统FAT16文件系统简介文件系统的作用是对文件在
57、介质上的存储进展治理,并为操作系统供给操作函数。我们的单片机没有内嵌操作系统,但是为了使我们在 U 盘上存取的信息在接入 PC 电脑后能被windows 正常访问,单片机必需满足windows 对 U 盘文件系统的操作标准。前面我们提到可以把 U 盘看成是以扇区为单位的规律盘1 扇区 512 字节。那么我们看看当一个U盘被格式化成FAT16 格式的分区后,它在物理上的全貌到底是怎么样的? 以下就是其构造形式图:必需说明:大多数情形下的 FAT16 格式 U 盘其构造就如上图,但也有例外。假设U 盘在格式化时加上了系统选项,带有 boot 功能,则在保存区前还将有 MBR 和假设干个隐含扇区。关
58、于上图的各局部简述:引导扇区也叫 DBR,它对我们最大的意义是存储了本分区的 BPB信息。稍后详解。FAT1 是文件安排表主表FAT = File Allocation Table,记录数据区中每个簇的使用状况。其大小依据 U 盘的实际大小而定。FAT2 是主表的备份。大小与主表全都。根文件夹存储文件名名目名等名目信息,固定占据 32 个扇区大小。剩下的数据区就是真正存储数据文件内容的区域我们在操作一个 U 盘的文件系统前,必需先猎取其文件系统信息,这时必需依靠引导扇区计算出 FAT1、FAT2、根文件夹以及数据区它们的起讫地址。以下逐一介绍这几个区域以及如何计算它们的起讫地址。注:对扇区的访
59、问依照其确定扇区号寻址,确定扇区号规律扇区号隐含扇区数, 规律扇区号是指不计算 MBR 等隐含扇区的号,DBR 的规律扇区号总是 0,即 DBR 总是规律分区的第一个扇区。保存区首先看看 U 盘被格式化成 boot 分区的状况,保存区前含有 MBR 和假设干个隐含扇区。MBR 是主引导记录的缩写Master Boot Record ,是物理上第一个扇区(假设它存在的话,因而确定扇区号是 0,它独立于任何一个分区或者称“卷”。MBR 的前 446 字节是系统引导程序,接着的 64 个字节就是大名鼎鼎的分区表 DPT(Disk Partition Table)。最终两字节是扇区有效标志 55 AA
60、。一个 MBR 的例图如下:DPT 以每分区 16 个字节的大小存放着最多四个主分区的信息。对于 U 盘来说由于一般只有一个分区,所以其分区信息就存放在 DPT 的第一个 16 字节中。其中我们最关心的是偏移地址为 1C6H 的信息,它指示着引导记录 DBR 相对于 MBR 的偏移地址。以上图为例, 偏移地址 1C6H 处的值为 0000003F留意是按双字大小存放的,3FH = 63,即十进制值为 63,表示 DBR 确实定扇区号 = MBR 的扇区号0+63。意即 DBR 前面有 63 个隐含扇区鉴于 MBR 有的状况下存在有的状况下不存在,我们在初始化 U 盘时可以这样处理:先尝试读入确
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年新能源汽车项目提案报告模板
- 居民通水工程合同协议
- 山林种树养殖合同协议
- 工业平车买卖合同协议
- 小区维修阀门合同协议
- 居民房屋修补合同协议
- 就转让租赁合同转让协议
- 小说版权授权合同协议
- 工厂包合同协议
- 工地人员用工合同协议
- GB/T 9119-2010板式平焊钢制管法兰
- GB/T 19466.4-2016塑料差示扫描量热法(DSC)第4部分:比热容的测定
- 2023年漳州龙海市广播电视台(融媒体中心)招聘笔试题库及答案解析
- 最新苏教版三年级数学下册:教材分析课件
- 地基基础规范8章
- 从敦煌壁画看中国古代山水画的发展演变
- DB12-537-2014企业事业单位内部治安保卫规范
- 建筑工地项目部人员职责划分表
- 工程量确认单表样
- 劳动争议调解仲裁法解析
- 数字化语音存储与回放系统(毕业设计)
评论
0/150
提交评论