logo资料库

单片机读写U盘FAT32或FAT16文件系统.doc

第1页 / 共47页
第2页 / 共47页
第3页 / 共47页
第4页 / 共47页
第5页 / 共47页
第6页 / 共47页
第7页 / 共47页
第8页 / 共47页
资料共47页,剩余部分请下载后查看
第1章U盘的逻辑结构
1.1U盘的逻辑结构
第2章USB通信协议
2.1USB设备开工的机理
2.2USB描述符
2.3USB设备的枚举过程(开工过程)
2.4USB1.1协议
2.4.1重新认识枚举过程
2.4.2基于SL811的USB底层传输函数实现要点
2.4.3usbXfer()函数
2.4.4 ep0Xfer()函数
2.5块传输(Bulk)
2.6SCSI命令
2.6.1跟U盘初始化有关的SCSI命令
2.6.2Read和Write U盘的命令
2.7U盘兼容性问题的探讨
第3章微软的文件系统
3.1FAT16文件系统简介
3.1.1保留区
3.1.2FAT区
3.1.3根文件夹
3.1.4数据区
3.2FAT32文件系统简介
3.3FAT文件系统的局限性
第4章编码实例分析
4.1需求简述
4.2文件结构
4.3Main.c
4.4USB.c
4.5timer.c
4.6filesys.c
4.6.1变量说明
4.6.2扇区读写函数
4.6.3询问下一簇号函数
4.6.4FAT初始化函数(BPB信息分析)
4.6.5Open Files
4.6.6创建文件
4.6.7写入文件
第5章使用CH375的解决方案
5.1只作简单介绍
第 1 章 U 盘的逻辑结构 1.1 U 盘的逻辑结构 U 盘可以看成是以扇区(1 扇区=512Bytes)为单位线性排列的实体,即 0 号扇区,1 号扇区,2 号扇区,……这样按顺序地排列下去。U 盘是 flash,对 flash 的操作总是以块为 单位的,因此单片机对 U 盘的操作是以扇区为单位,整个扇区地读取,或整个扇区地写入。
第 2 章 USB 通信协议 2.1 USB 设备开工的机理 USB 是即插即用的,涵盖海量存储器(如 U 盘、移动硬盘)、人机交互设备(如鼠标键 盘游戏杆)、扫描仪、打印机等等各种各样功能的设备,那么 USB 主机是如何判断目前接入 的设备到底是怎么样的呢?答案是 USB 描述符,以及 USB 的枚举。 2.2 USB 描述符 这个概念很简单,就是对各种纷繁芜杂的 USB 外设按功能划分大类(class),大类下又 再细分小类(subclass),每个类别给予一串特定的符号(Descriptor)供主机辨识。 每个 USB 设备只能有一个 DEVICE 描述符,它指明了该设备属于哪一大类,是海量存 储器类,还是人机交互设备类,还是打印机或者扫描仪类,等等。 每个 DEVICE 下可以有 1 个或多个配置描述符(configuration),以说明该设备含有哪些 功能。如一个 USB 接口的 CDROM 可以同时具有读写光盘的功能和播放 CD 的功能。有几 个功能,就有几个配置描述符。 每种配置对应若干个接口描述符(Interface),以描述该配置使用哪些接口与主机进行通 信。 每个 Interface 又都是端点(End Point)的集合,端点就是设备与 USB 主机交换数据的 最 原 子 单 位 了 。 每 个 Interface 用 到 的 端 点 可 以 是 一 个 或 多 个 。 下 图 摘 自 USB MASS STORAGE CBI Transport Specification 第 6 页,清楚说明各种描述符的组织情况。 2.3 USB 设备的枚举过程(开工过程) 有了完善的分类后,USB 设备上电即可通过枚举过程告诉 USB 主机自己的详细信息,
这很类似一个一问一答的过程,如下: 主机(下称 H):你是甚么设备? 设备(下称 D):我是 12 01 0100…… (这就是 DEVICE 描述符了) H:你有几种功能? D:我有 09 02 09 …… (配置描述符) H:每种功能有几个接口? D:09 04 00…… (接口描述符) H:每个接口用到哪些端点? D:07 05 81 …… (端点描述符) H:好了,我知道你是谁了,开始传数据吧! D:OK. READY GO! 具体而言,USB 枚举过程有以下步骤: (1) 集线器检测新设备 主机集线器监视着每个端口的信号电压,当有新设备接入时便可觉察。(集线器端口的两根 信号线的每一根都有 15kΩ的下拉电阻,而每一个设备在 D+都有一个 1.5kΩ的上拉电阻。 当用 USB 线将 PC 和设备接通后,设备的上拉电阻使信号线的电位升高,因此被主机集线 器检测到。) (2) 主机知道了新设备连接后 每个集线器用中断传输来报告在集线器上的事件。当主机知道了这个事件,它给集线器发送 一个 Get_Status 请求来了解更多的消息。返回的消息告诉主机一个设备是什么时候连接的。 (3) 集线器重新设置这个新设备 当主机知道有一个新的设备时,主机给集线器发送一个 Set_Feature 请求,请求集线器来重 新设置端口。集线器使得设备的 USB 数据线处于重启(RESET)状态至少 10ms。 (4) 集线器在设备和主机之间建立一个信号通路 主机发送一个 Get_Status 请求来验证设备是否激起重启状态。返回的数据有一位表示设备仍 然处于重启状态。当集线器释放了重启状态,设备就处于默认状态了,即设备已经准备好通 过 Endpoint 0 的默认流程响应控制传输。即设备现在使用默认地址 0x0 与主机通信。 (5) 集线器检测设备速度 集线器通过测定那根信号线(D+或 D-)在空闲时有更高的电压来检测设备是低速设备还是 全速设备。(全速和高速设备 D+有上拉电阻,低速设备 D-有上拉电阻)。 以下,需要 USB 的 firmware 进行干预 (6) 获取最大数据包长度 PC 向 address 0 发送 USB 协议规定的 Get_Device_Descriptor 命令,以取得缺省控制管道所 支持的最大数据包长度,并在有限的时间内等待 USB 设备的响应,该长度包含在设备描述 符的 bMaxPacketSize0 字段中,其地址偏移量为 7,所以这时主机只需读取该描述符的前 8 个字节。注意,主机一次只能列举一个 USB 设备,所以同一时刻只能有一个 USB 设备使用 缺省地址 0。 (7) 主机分配一个新的地址给设备 主机通过发送一个 Set_Address 请求来分配一个唯一的地址给设备。设备读取这个请求,返 回一个确认,并保存新的地址。从此开始所有通信都使用这个新地址。 (8) 主机向新地址重新发送 Get_Device_Descriptor 命令,此次读取其设备描述符的全部 字段,以了解该设备的总体信息,如 VID,PID。 (9) 主机向设备循环发送 Get_Device_Configuration 命令,要求 USB 设备回答,以读取 全部配置信息。
(10) 主机发送 Get_Device_String 命令,获得字符集描述(unicode),比如产商、产品描 述、型号等等。 (11) 如果主机是 PC 电脑,此时主机将会弹出窗口,展示发现新设备的信息,产商、产 品描述、型号等。 (12) 根据 Device_Descriptor 和 Device_Configuration 应答,PC 判断是否能够提供 USB 的 Driver,一般 win2k 能提供几大类的设备,如游戏操作杆、存储、打印机、扫描仪等,操 作就在后台运行。 (13) 加载了 USB 设备驱动以后,主机发送 Set_Configuration(x)命令请求为该设备选 择一个合适的配置(x 代表非 0 的配置值)。如果配置成功,USB 设备进入“配置”状态,并 可以和客户软件进行数据传输。此时,常规的 USB 完成了其必须进行的配置和连接工作。 查看注册表,能够发现相应的项目已经添加完毕,至此设备应当可以开始使用。 以上是 PC 电脑为主机的枚举过程,对于单片机作为主机的情形,过程要简单一些,以 枚举 U 盘为例: (1) 芯片 SL811 监视 USB 总线电平,当发现有 U 盘插入后,给单片机一个中断信号。 (2) 单片机给 SL811 发出端口复位命令,持续 100 毫秒以上。 (3) 单片机发出 Get_Device_descriptor 命令,从默认的端口 0 和地址 0 发出。该命令先 假设了包传送的大小是 64 字节,在获得命令返回时修正 MaxPacketSize。此步同 PC。 (4) 单片机发送 Set_Address 请求来分配一个唯一的地址给 U 盘,我们实际应用中固定 分配了地址 2。此步同 PC。 (5) 单片机向新地址 2 重新发送 Get_Device_Descriptor 命令,此次读取 U 盘设备描述符 的全部字段,以了解该设备的总体信息,如 VID,PID。此步同 PC。 (6) 单片机发送 Get_Configuration_Descriptor 命令获取配置描述符。 (7) 根据获取的配置信息,单片机发送 SetConfig 和 SetInterface 命令对 U 盘进行配置。 (8) 对获取的 Interface 描述符和 Endpoint 描述符进行分析,判断是否大容量存储设备、 是否支持 SCSI 命令集、是否 BULK_ONLY 传输、端口的最大包长等内容。 (9) 发送 Get_Max_LUN 命令获取 U 盘的进一步信息(根据协议看此步非必须,有些 U 盘此步会返回 STALL,即不支持,也没有关系)。但是建议在枚举过程中不省略此步,因为 不同品牌 U 盘其固件可能不一样,有些固件可能不允许省略此步。 (10) 完成上述步骤后,U 盘的枚举过程完成,接着需要发送几条 SCSI 命令来对 U 盘进 行初始化,这几条命令依次是 Inquiry、ReadFormatCapacity、ReadCapacity。完成后,U 盘 已经准备好接收单片机发出的任何读写命令(读写命令也是来自 SCSI 命令集)。 如果你有兴趣知道 USB 协议一些更细节的内容,请往下看。否则可以直接跳到第四章 的文件系统部分。 2.4 USB1.1 协议 本节内容主要涵盖 USB 1.1 Specification 的第 4、5、8、9 章。并且主要描述代码中无法 注释或者在代码中注释会太麻烦的内容。 USB 是一种主从的结构。所有传输由 Host 来发起。当主机发起一次传输时,这次传输 的包(Packets)通常包括三个阶段。主机首先是发送一个 Token Packet,内里包含本次传输 的命令类型(type)、方向(direction)、设备的地址(device address)以及端点号(Endpoint)。 紧接着是数据包(data packet),就是包含数据了。最后将由 device 返回握手信号包(handshake packet),表示是正确收到了(ACK)还是其他的失败原因。三个包如下图所示。
USB 的传输模型:Host 和设备的某个端点之间可以看成有一条逻辑管道(pipe)。Pipe 分两种:业务数据流和信令消息。业务流即指纯粹的数据,信令流指控制信息。其实通信协 议很多都如此,分业务流和信令流,例如电信网中的 7 号信令。 在信令管道中,有一条默认的管道,那就是零地址处的零号端点,这条管道在 USB 设 备上电复位或总线复位后就存在了,便于 Host 统一利用这个地址向 USB 设备进行配置。显 然对于 USB 集线器,即使同时插入几个设备,Host 也只能一次对一个设备进行配置。USB 设备只有配置(configured)后,才可使用。 USB 的传输类型有四种: 控制传输(control transfer),通常只用于在设备复位后 Host 通过端点 0 进行配置。 块传输(Bulk Transfer),譬如 U 盘的大量数据传输即用此方式。 中断传输(Interrupt Transfer),一般用于人机设备如 USB 鼠标键盘等。 等时传输(Isochronous Transfer),可以进行带宽控制的实时传输形式。 2.4.1 重新认识枚举过程 枚举过程事实上是 USB 设备复位后,恢复到 0 地址 0 号端点,然后主机通过一系列控 制传输命令对 USB 设备进行配置,同时也获取一些信息。 使用 BUS hound 这个工具可以把完整的 USB 设备枚举过程抓下来。网上很容易找到安 装包。BUS 软件的设置如下:可以确保抓下所有的数据包信息。
利用 BUS hound 的软件抓一下爱国者行业特供型 1G 的 U 盘,其在 PC 下的枚举过程完 全在下图中表现了出来。让我们逐一分析。
由设备 16.0 抓到的数据包属于 USB 集线器的行为,在无使用集线器的单片机系统中可 以无视之。设备 21.0 的含义是:usb 设备地址是 21,目前管道是跟它的端点 0 打交道。 数字 1 处是枚举过程的开始,主机用控制传输发送 Get Descriptor 获取设备描述符(具 体为何是设备描述符可以对照 USB1.1 技术规范的第九章来分析左边的那串 08 06 00 02…… 的二进制数据,下同),这条命令假设了未知设备的端点 0 的最大包长值 64 字节,然后在命 令中要求设备返回 0x12(十进制 18)个字节的 device 描述符,如图中圈起来的 12。值得一 提的是,这条命令无论假设设备的端点 0 的最大包长(Payload)是 8,16,32,64,都是可 以获得想要的数据的(图中的 40)。40H 指明该设备的端点 0 的最大包长是 64 字节,Host 此后的控制传输可以使用 64 字节的数据包跟设备通信了。64 字节数据包的细节后面会介绍。 数字 2 处表示主机发送 Get Descriptor 获取配置描述符。但是类似设备描述符的处理方 法,主机也先试探性的获取配置描述符的前 9 个字节(图中带圈的 09),以获悉整个配置描 述符有多长,因为长度信息就位于描述符的第 3 个字节,如图中带圈的 20。根据此 20h 的 长度信息,数字 3 处开始正式请求设备完整的配置描述符了,可以在图中看到两个 20 是对
应的。 整个配置描述符包含 32 个字节(一般的 U 盘都是这样)。这 32 个字节中,包含 3 部分 内容,包含设备的重要信息。数字 5 所代表的第一个框表示第一部分:配置描述符。数字 6 代表第二部分:接口描述符(Interface)。其中第 5 字节 02 表示该设备有 2 个端点(Bulk_IN 和 Bulk_OUT),第 6 字节 08 代表这是大容量存储设备(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 代表第三部分:端点描述符。第三字节都是代表端点地址,一般情况是地址 1 和地址 2。留意图中 8 框第三字节是 82H,这表示该端点地址就是地址 2,最高位被置 1 以表示这个端点是 Bulk_IN 端点,所以整个数值变成了 82H。但是并非地址 2 就一定是 Bulk_IN,不同的 U 盘不一样,所以在程序中要根据描述符的实际值,用变量记录下来的, 后面要用到。继续看 8 框,第五和第六字节组成一个 16 位的数值表示该端点的最大包长度 (payload)。对于只支持 1.1 协议的设备,第六字节是其高 8 位,都是 0,第五字节才是真 正的 payload 数值,1.1 协议规定只能是 8,16,32,64 之一,由厂家固定。至于我们在上 图看到第六字节是 02 第五字节是 00,组合成 200H=512,那是因为 U 盘控制器认出了这个 Host(PC 电脑)支持 usb2.0,所以就回应了 512,而不是 64。Payload 值非常重要,后面要 依据此值进行判断和计算。 下面接着的 4 个 get descriptor 都是获取设备的 string 描述符。实际的单片机系统也许不 需要获取这些描述符,而且有些 U 盘也不支持获取这个描述符(返回 STALL)。再往下的就 是 set_configuration、set_interface、get_MAX_LUN 等。有些 U 盘,在 set_interface 处会 stall; 有些 U 盘,如果 Host 不发送 set_interface 命令,往后的命令都不响应,所以这个牵涉到兼 容性问题,后面再解释。在上图中可以看到,这个爱国者 U 盘在遇到 get_MAX_LUN 时返 回了 stall,PC 的处理方法是 clear feature,然后重试,三次后仍然 stall 则跳过。这个牵涉到 如何进行差错处理,后面再详细分析。 2.4.2 基于 SL811 的 USB 底层传输函数实现要点 前面枚举过程介绍的各种命令,如截图中的数字 1 处的“80 06 00 01 00 00 12 00”到底 是如何发送出去的,这也许是大家比较感兴趣的问题。 有必要先简单认识一下 SL811 的功能,虽然这跟 USB1.1 协议几乎无关。 SL811 提供了 15 个寄存器供使用,实际在进行 USB 传输时最少只需要用到其中 6 个, 另外还需要用到 SL811 内建的 240 字节 RAM 作为数据缓冲。 启动 SL811 发送/接收一次数据(注意不是一帧数据,一帧数据=一个数据包)的步骤 如下: → 目标 U 盘的端点地址和 pid 目标 U 盘的地址 811 内部 RAM 中数据缓冲的地址 → 该次数据的长度(不是该帧数据的长度) → → 0xff → SL811 寄存器地址 0x03 地址 0x04 地址 0x01 地址 0x02 地址 0x0d → 启动发送的命令字 当这一次数据成功了后,如果 pid 是“发送”,则 SL811 内部 RAM 中缓冲处的数据都 被发出去了;如果 pid 是“接收”,则 SL811 内部 RAM 中缓冲处会填满来自 U 盘的数据, 长度等于上面黑体字第四行之设定。单片机应该在此时及时把 SL811 的内建 RAM 中这些数 据读出来,放进单片机自己开辟的内存区域。 地址 0x05 看的出来 SL811 对 USB 的物理层已经完全封装了,但是设计者还是需要关心很多细节。
分享到:
收藏