何 谐
(江阴职业技术学院,江阴214400)
何谐(讲师),研究方向为单片机与嵌入式应用。
FAT类文件系统是Windows操作系统在磁盘文件管理上最常用的一种文件系统。而FAT32文件系统是微软FAT类文件系统中的最高版本,微软从Windows95版本开始,后续的操作系统均支持FAT32文件系统,是现今Windows下最常用的硬盘文件系统。当人们使用SD卡等存储装置从计算机上拷取文件时,存储器中的文件管理即符合FAT32文件系统的管理原则,因此,使用嵌入式芯片设计音乐播放器时,为了能自动识别SD卡上的音乐文件,关键是FAT32文件系统在嵌入式芯片上的实现,本文研究了FAT32文件系统在Cortex-M3内核的音乐播放器上的应用,并设计了基于FAT32的音乐播放器。
Cortex-M3内核是ARM公司推出的基于ARMv7体系架构的处理器核,具有高性能、低成本、低功耗的特点,而意法半导体公司的STM32系列芯片STM32F103RB则是在Cortex-M3内核的基础上扩展了高性能的外围设备。该芯片工作频率为72MHz,内置高速存储器(128KB的闪存和20KB的SRAM)。由于其丰富的外设和优异的性价比,音乐播放器选择其作为主控制器芯片,利用其自带的2组SPI接口读取SD卡音频文件以及缓存,并将读取的音频数据流送至音频解码器VS1003进行解码;同时主控制器还负责人机交互,连接TFT屏幕显示歌曲名称以及键盘用以选择曲目。
本音乐播放器选择SD卡作为存储装置。SD卡使用前应通过读卡器连接至计算机,格式化为FAT32文件格式,同时将*.mp3、*.wav、*.wma格式的音乐文件复制到SD卡中。一般来说,SD卡支持两种工作模式:SD模式和SPI模式。由于STM32F103RB本身具有2个SPI接口,因此SD卡以SPI模式连接STM32F103RB的SPI1口。
VS1003是荷兰VLSI公司出品的一款单芯片MP3/WMA/MIDI/WAV音频解码和ADPCM编码芯片,通过SPI控制。该系统中,VS1003作为主控芯片的从机使用,STM32F103RB通过它的SPI2口向VS1003不断输出音频数据流,VS1003自动解码,并连接外部功放和喇叭,就可以听到所播放的音乐了。
STM32F103RB与SD卡和VS1003的连接图如图1所示。
图1 STM32F103RB与SD卡和VS1003的连接图
FAT类文件系统对文件的存储空间是按簇进行划分和管理的,即一个文件总是占用若干个整簇,即文件最后一簇剩余的空间将不再使用。不同的簇采用簇号来区分,FAT32文件系统使用32位二进制来表示簇号,适用于空间大于512MB的磁盘。每簇对应的扇区数将决定磁盘的容量,扇区是仅小于簇的存储单位,一般每扇区大小为512字节。为了提高对整个磁盘空间的利用率,“簇”不宜大,也不宜小。对于小容量的磁盘,FAT32系统实际每个簇为4KB,也就是8个扇区;当磁盘容量超过8GB时,每个簇为8KB,32GB以上每簇为32KB。
当SD卡被格式化为FAT32文件格式时,SD卡中的逻辑盘空间就被划分为三大部分:保留区、FAT表区(文件分配表区)、DATA区。其中保留区和FAT表区又合称为系统区,文件的真正存储从DATA区开始。
保留区包含了FAT32的重要数据结构——MBR(主引导记录)和DBR(系统引导扇区)。MBR如果存在,则绝对0扇区就是MBR;如不存在,此扇区就是DBR。MBR的前面446字节为引导程序,随后的64字节是4条记录,称为DPT(磁盘分区表),每条记录均包含了分区的开始磁头、开始柱面与分区类型、分区的结束头、结束柱面与扇区、分区的第一个扇区、总扇区数等信息。由于SD卡容量不是很大,通常不作分区,后面三条分区记录均为0。
通过DPT可以找到DBR扇区,在DBR中记录了分区的很多重要的信息,这些重要信息都保存在BPB(BIOS参数块)这个区域。这些重要信息包含了FAT32的大部分全局参数,例如:每扇区的字节数、每簇扇区数、保留扇区数、FAT表数、FAT区前隐扇区数、FAT表所占扇区数、第一个目录的簇号等。
DBR扇区之后是保留扇区,再接着便是FAT表区了,FAT32中有两个FAT表,第二个是第一个的备份,通过对BPB的解析可知FAT表的开始扇区和大小。FAT表是一个链式结构,每4个字节(一个32位二进制)为一个FAT表项,每个FAT表项对应一个簇,00簇和01簇被系统保留,因此前2个FAT表项为特殊字符。从02簇开始,每个簇都依次对应一个FAT表项内容。如果该簇未使用或已回收,相应FAT表项内容写零,坏簇以0FFFFFF7H标识;如果该簇是文件的最后一簇,FAT表项值为0FFFFFFFH;如果该簇不是文件的最后一簇,FAT表项值为该文件占用的下一个簇的簇号,文件占用的各簇构成一个簇链,保存在FAT表中。因此,只要知道文件的起始簇号,就可以根据该链式结构找到整个文件。当新建文件时,如果新建的文件只占用一个簇,为其分配的簇对应的FAT表项将会写入结束标记,如不只占用一个簇,则在FAT表项中记录下一簇簇号直至结束;当删除文件时,文件所对应的FAT表项将被设置为0,以表示其对应的簇处于未分配状态。
DATA区从02簇开始,该区根据存放的内容,可分为根目录区和文件数据区。根目录区存放根目录文件,通过BPB解析可知02簇的开始扇区,该扇区也称根目录簇开始扇区。从这个扇区开始依次存放文件目录项,每个文件目录项占用32字节,根目录文件的大小随文件目录项个数增加而增加。除了第一个32字节为文件卷标,其后的每个文件目录项均描述了和文件相关的大部分信息,如文件名、文件创建时间、访问时间、文件大小、文件起始簇号等等。解析出这些文件信息,特别是文件起始簇号,嵌入式芯片就可以根据FAT表访问任意文件内容了。根目录项中的内容也可以是子目录文件的信息用同样方法也可以找出子目录下的所有文件的信息。
FAT32在Cortex-M3内核的嵌入式芯片上的实现依赖于一些必要的结构体,这些结构体包括了FAT32文件系统的重要区域信息,通过对这些结构体的解析,可以方便计算出目标文件的重要信息。因此,FAT32文件系统首先定义这些结构体,包括MBR结构体struct PartSector、DPT结构体struct PartRecord、BPB结构体struct FAT32_BPB、文件目录项结构体struct direntry和文件信息结构体struct FileInfo。
FAT32文件系统本身就是用来管理和控制对扇区数据的读和写,因此,需要构建一个FAT32_ReadSector()函数,用来读取存储设备的扇区,与FAT32_WriteSector()一起构成其他FAT32函数的底层函数。由于系统的存储设备是SD卡,SD卡函数SD_ReadDisk()可从指定物理扇区号读取1个扇区的数据,放入全局变量数据缓冲区FAT32_Buffer[512]中,因此,使用SD_ReadDisk()来作为这个底层函数。
初始化FAT32文件系统包括以下几个函数。
寻找 DBR函数 FAT32_Find_DBR():此函数用FAT32_ReadSector()读入SD卡0扇区数据,强制类型转换为MBR结构体struct PartSector,解析出其中的DPT结构体struct PartRecord,返回DBR所在扇区号。
FAT32_Init()函数:将DBR扇区数据读入缓冲区,强制类型转化为BPB结构体,解析struct FAT32_BPB,计算出FAT32的重要信息:每扇区字节数、每簇扇区数、第一个FAT表扇区号、FAT表占用的扇区数、根目录簇号、根目录簇开始扇区、根目录占用扇区、第一个数据扇区,作为全局变量参数。
ClustToSector()函数:输入参数是任意簇号,返回该簇所对应的起始扇区号。
GetNextCluster()函数:根据文件当前簇号,在FAT表中找到文件下一簇号返回,如没有后继簇,则返回0x0ffffff8,表示文件结束。
Get_File_Info():该函数输入参数为根目录簇号dir_clust、全局参数文件信息结构体struct FileInfo、音乐文件类型、音乐文件序号count。该函数首先根据根目录簇号找到该簇起始扇区号,连续读取扇区,直到FAT表中簇链结束。对读取的每个扇区数据以32字节为单位强制转换为文件目录项结构体struct direntry,对每个合法的文件目录项结构体struct direntry进行解析。若音乐文件类型符合输入参数,文件索引号自增1;若该文件索引号即为音乐文件序号时,表示找到目标音乐文件。将该目标文件的struct direntry的32字节内容转化到全局变量文件信息结构体struct FileInfo,提取文件信息详情。struct FileInfo结构体如下:
音乐播放器可循环播放SD卡上的音乐格式文件,只要文件格式为*.mp3、*.wav、*.wma,就可顺序播放,TFT屏幕显示正在播放的音乐文件名称,通过对按键的操作可实现歌曲的暂停/播放、选择上一首和下一首。该音乐播放器软件系统设计除FAT32文件系统FAT32.c之外,还包括以下几大模块:歌曲播放模块music_play.c为软件的最高层应用层;SD卡模块sd.c、VS1003模块vs1003.c为music_play.c提供设备驱动;而SPI模块spi.c为最底层的物理数据交换程序,为sd.c和vs1003.c提供支持。软件系统结构如图2所示。
图2 软件系统结构
应用层程序music_play.c在初始化之后首先计算出根目录下音乐格式歌曲的数目,随后判断按键状态,当选择前一首、后一首或一首歌播放完毕时,改变播放歌曲的索引号,播放该索引号的歌曲。
播放某歌曲时,首先利用FAT32函数Get_File_Info(),根据歌曲索引号和文件类型来构造该文件的文件信息结构体struct FileInfo,并获取这首歌的文件起始簇号、歌曲名称等信息。根据文件开始簇号,调用FAT32函数FAT32_ReadSector()读取1扇区数据,存入STM32F103RB的512字节的数据缓冲区,缓冲区数据随后通过SPI函数SPI2_ReadWriteByte()发送给 VS1003播放。当该簇所有扇区播放完毕后,一簇结束,利用FAT32函数GetNextCluster()在FAT表中继续寻找下一簇簇号,循环上述过程,直到簇链结束,歌曲也播放完毕。
歌曲播放主流程如图3所示。
系统读取SD卡时使用STM32F103RB的SPI1口,设置为高速模式;发送数据到VS1003时使用SPI2口,设置为低速模式。这样在歌曲的播放过程中,数据的读取速度永远超过数据的发送速度,可以得到比较好的音质,不会出现声音的抖动。
本文结合FAT32文件系统提出嵌入式系统的音乐播放器设计方案,研究了FAT32文件系统在Cortex-M3内核的音乐播放器中的实现方法,对便携式多媒体播放器的研发具有一定的指导意义。
图3 歌曲播放流程图
[1]何立民.单片机高级教程[M].北京:北京航空航天大学出版社,2007.
[2]戴士剑,涂彦辉.数据恢复技术[M].2版.北京:电子工业出版社,2007.
[3]刘伟.数据恢复技术深度揭秘[M].北京:电子工业出版社,2010.
[4]唐继贤.51单片机应用系统开发实例精解[M].上海:科学技术出版社,2012.
[5]刘军.例说STM32[M].北京:北京航空航天大学出版社,2011.