刘人萍,汪涛
(重庆大学 物理学院,重庆 401331)
摘要:由于NIOS下自带的SPI控制器,一旦建立,SPI clock(SCLK)的频率在使用过程中是不允许被修改的,即SPI的读取速度是不可改变的,这在很大程度上限制了SD卡的使用性能,因为在卡初始化的时候,SCLK时钟最大不能超过400 kHz。使用软件模拟SPI,通过示波器调试时序,并且移植了FATFS文件系统实现SD的存储管理。最后通过存储文件测试在不同速度下的存储时间。
关键词: NIOS;软件SPI;FATFS文件系统;SD卡
中图分类号:TP274文献标识码:ADOI: 10.19358/j.issn.1674-7720.2017.08.027
引用格式:刘人萍,汪涛.NIOS下实现存储速度可调的SD卡FAT文件系统[J].微型机与应用,2017,36(8):85-87,91.
0引言
与传统处理器相比,NIOS II嵌入式系统在设计的时候可以根据不同的需求来增减外设的种类和数量,在FPGA上面快速地搭建硬软件平台。再加上可以根据需要很方便地把FPGA并行处理的IP核嵌入到NIOS II系统中,而只需要简单的Avalon接口总线,所以其灵活的构造方式越来越受到开发人员的青睐。
嵌入式系统都需要用到大容量存储设备,以备数据存储。目前常用的存储设备有U盘、Flash芯片、SD卡等,综合比较,最适合嵌入式系统的可移动存储设备莫过于SD卡了。SD卡不仅容量可以做到32 GB以上,而且支持SPI,更换方便,编程简单,最高通信速度可以达到18 Mb/s,可满足于一般的应用要求。
然而在NIOS中使用SPI方式与SD卡通信的时候,由于在软核的构造过程中,系统已经把SPI的时钟频率选定,在软件编程时不可以再去修改SPI的速率,再加上SD卡在初始化过程时钟最大不超过400 kHz[1],这样在很大程度上限制了SD卡的使用性能。解决以上问题的方法就是不使用NIOS II自带的SPI控制器,利用软件模拟的方式,可以随时调整SPI的时钟速率。
本文将从NIOS II软核的构建开始,利用软件模拟SPI,接着写SD的底层驱动,最后移植FATFS文件系统来管理SD卡,测量SD卡在不同的速率下写文件所需要的时间。
1NIOS II软核的构建
1.1SOPC系统结构简介[2]
为了尽可能简单地验证本文所述内容,在硬件方面只选择了所必需的硬件,其中包括处理器和支持处理工作的外围设备以及4个通用的IO,具体如下:
(1)NIOS II 处理器。
(2)EPCS:FPGA的配置芯片,相当于计算机的硬盘。由于FPGA掉电后,代码和配置信息都会丢失,因此EPCS的功能一方面是保存NIOS II软核的配置信息,另一方面是保存用户需要运行的程序代码。
(3)DDR2 SDRAM: 程序运行的地方。上电之后,系统会把EPCS中的代码搬移到SDRAM中运行。
(4)JTAG :第一是将编译好的程序下载到开发板中;第二是通过JTAG_UART来调试程序,打印程序的执行结构。
(5)4个通用IO : 由于用软件来模拟SPI,因此只要4个通用IO,其中3个是输出方向(SCLK,MOSI,CS),一个是输入方向(MISO)。
1.2NIOS II 软核构建流程[3]
(1)时钟。时钟是一个数字系统不可缺少的一部分,在嵌入式系统中尤为重要。时钟信号的质量也决定着嵌入式系统工作能否稳定。在本次NIOS II软核构建中使用PLL来得到所希望的时钟频率。本文中所使用的开发板的晶振频率是50 MHz,希望得到的时钟频率也是50 MHz,按理说是不需要PLL的,但是经过PLL之后输出的时钟在稳定性方面更好一些,所以就用PLL来生成一个频率仍为50 MHz的时钟。
(2)软核及控制器构建。这部分以NIOS II CPU为核心,包含了存储单元的控制器和通用的IO接口。在CPU选择过程中有三种类型可供选择,从资源消耗和工作速度的角度出发,本文选择了NIOSII/s。在选择SDRAM控制器的时候,需要根据自己实际的硬件来进行选择,一般的数据在所使用芯片的DATASHEET中可以查到。然后是添加Flash控制器,这里使用的是EPCS,最后建立system ID和JTAG_UART。
(3)添加外围设备。这里为了尽可能简单地验证本文功能,只添加了4个通用IO来模拟SPI时序。这里使用了三个输出IO(CS,SCLK,MOSI)和一个输入IO(MISO)。
2软件模拟SPI及SD卡底层驱动
2.1SPI简介
SPI(Serial Peripheral Interface)是Motorola首先在其处理器上面定义的。SPI主要应用在EEPROM、Flash、AD转换等。SPI是一种高速的、全双工、同步的通信总线,并且在芯片的引脚上只占用四跟线,既节约了芯片的引脚,也方便了PCB的布局布线。SPI接口一般使用四跳线通信:MISO:主设备输入,从设备输出;MOSI:主设备输出,从设备输入;SCLK:时钟信号,由主设备产生;CS:从设备片选信号,由主设备产生。
2.2SD卡相关介绍
SD卡(Secure Digital Memory Card)是一种基于开云棋牌官网在线客服快闪记忆器的新一代记忆设备,它被广泛地应用在便携式设备中。按容量可以把SD卡分为三类[1]:SDSC(0~2 GB),SDHC(2~32 GB),SDXC(32 GB~2 TB)。
SD卡和SDHC卡协议基本兼容,但是同SDXC卡的区别比较大,本文主要介绍目前比较常用的SDHC卡的操作。SD的操作方式一般支持2种方式:SD卡模式和SPI模式,本文使用SPI模式。
从DATASHEET中可以知道SD卡每个命令是由6个字节组成的[1],第一个字节的最高两位固定为01,然后紧接着是6个字节的命令号,其中第2~5字节为命令参数,如果有些命令没有命令参数就设为0,第6字节的高7位是CRC,最低位恒为1。SD卡的通信采用发送应答机制,每发送一个命令,SD卡都会给出一个应答,以告知主机该命令的执行情况,或者返回给主机需要获取的数据。后面从示波器中可以看到SD卡具体的发送命令和接收应答的相关时序。
2.3软件模拟速度可调的SPI
根据SD卡手册的Bus Timing可以看出,SD卡底层的读写时序是在时钟的上升沿接收数据,在时钟的下降沿发出数据。所以根据这个时序要求,就可以通过软件来模拟SD卡最底层的数据读写。图1右侧是写一个字节的函数,这个函数可以通过调节usleep(u32 nus)函数中的参数来调整SCLK的速度,从而达到了SPI写一个字节速度可调的目的。同理可以写出读取一个字节的函数,同样可以根据usleep(u32 nus)中延时的参数不同来控制SPI读取数据的速度。
2.4SD卡的初始化
有了SD卡底层的读写一个字节的函数之后,就可以与SD卡进行通信了。根据SD卡的数据手册可以总结出SD卡初始化的大致流程:首先需要给SD卡发送大于74个时钟,这是因为SD卡内部有个供电电压上升时间,大概为64个SCLK,剩下的10个SCLK用于SD卡同步,然后再发送CMD0使SD卡进入IDLE状态;接着发送CMD8,检查这个SD卡是否支持2.0协议;之后根据不同的应答值检查SD卡;最后初始化结束之后,需要多发8个SCLK使SD卡完成某些操作[4]。在完成初始化之后,就可以进入SD卡的读写数据了。在调试过程中利用示波器看到的波形图如图2所示。然后对比手册给出的发送命令和返回相应的时序图,可以验证以上模拟的SPI时序是否正确可行。
图2(a)是SD卡手册给出的部分复位时序图,正常情况下在主机发送CMD0之后,SD卡会返回主机0x01。图2(b)是实际在示波器上面看到的时序,根据SD卡命令形式可以看出,NIOS II主机发送的6个命令字节分别是:第1个字节:0x40(命令第一个字节最高两位必须为01),Command = 0;第2~5个字节:0x00,0x00,0x00,0x00为命令的参数;第6个字节:0x95命令CRC校验 + 最低位的“1”。
然后SD卡接收到NIOS II主机发送的CMD0命令之后,给出了一个0x01的应答,从而可以看出,上述软件模拟的SPI时序是正确的。
2.5SD卡读写功能测试
对SD卡初始化完成之后,就可以正常使用SD卡了。由于SD卡是存储数据的设备,对SD的使用无非是读数据和写数据。有了上面写命令和读数据的基础之后,就可以通过命令的形式来读写SD卡了。
SD卡手册规定,如果想读SD卡某个扇区数据,发送命令CMD17,参数为扇区数,就可以读出扇区内的数据。写扇区通过发送CMD24来实现。这里使用了两个函数,分别为读一个扇区和写一个扇区函数:
SD_ReadDisk(buf,0,1)//把扇区0的数据读到buf中
SD_WriteDisk(buf,0,1)//把buf中的数据写到扇区0中
由于SD卡是块设备,在操作的时候需要一个扇区一个扇区地操作。在测试SD卡读写功能的时候,先对SD卡初始化,接着读出原来0号扇区的数据,然后再写入数据,最后再次读出,经过JTAG_UART的打印,可以看出是符合预期目标的,所以读写函数功能正常。
3FATFS文件系统移植及SD卡速度测试
前面只是对SD卡一个扇区的读写,而且读写都是数字。要真正有效地利用SD卡保存文件或者音乐等,必须使用文件系统管理。本文将使用FATFS来管理SD卡,实现SD卡文件的读写。FATFS是一个完全免费并且开源的FAT文件系统模块,用标准的C语言编写,移植到各种嵌入式设备只需要进行少量的修改。FATFS支持多个存储媒介,有独立的缓冲区,可以对多个文件进行读写。使用者无需去关心FATFS内部复杂的协议,只需要像调用其提供的其他一系列应用接口函数那样就可以了。
在移植使用的时候,只需要编写底层FATFS提供的接口函数,它主要包括存储设备的读函数、写函数、初始化等,之后就可以轻松地使用。
3.1FATFS移植步骤
(1)在官网下载源码,解压。本文所使用的Eclipse编译环境中的数据类型和源码文件夹中integer.h里面的定义是一致的,所以不需要改动。如果使用其他编译器的数据类型不和源码中的相同,就需要根据编译器定义好数据类型。
(2)在ffconf.h头文件中修改相关配置。这里根据自己的要求,改变某些变量的值,就可以配置出适合自己需求的文件系统。
(3)由于文件系统模块完全与磁盘IO层分开,为了增加通用性,FATFS的开发者并不确定用户所用的存储设备是哪一类,所以需要用户自己提供相应的底层操作函数。在diskio.c文件里面,FATFS已经提供了函数接口,只需要根据自己的存储设备类型,把这些底层操作函数添加进入即可。这里FATFS留出了6个函数接口。仅仅是为了验证本文的功能,本文只添加了三个函数,其余函数都返回0。添加的函数为:disk_initialize,disk_read, disk_write。初始化函数和读写扇区函数在前面都已经使用和测试过,只需要把这三个函数交给文件系统使用就可以了。
3.2文件系统测试
本文对文件系统的测试,首先调用FATFS提供的接口函数f_getfree来测试SD卡的总容量和剩余容量,然后在SD卡中创建一个文件,最后显示SD卡的容量,判断FATFS文件系统测试的结果和实际的结果是否一致,再在PC上查看SD卡中是否有刚才创建的文件。本文所用SD卡容量为4 GB,调用FATFS文件系统提供的接口f_getfree函数之后,得到了SD卡的容量为3 716 MB,如图3所示,与实际的容量是相符合的。
3.3SD卡写文件速度测试
在移植文件系统之后,就可以轻松地使用SD卡来存储文件了。这里限于硬件条件比较简单,所以只对SD卡写文件的速度做一个大致的测试和估算。如前所述可以通过改变usleep(u32 nus)这个函数的参数来改变SD卡的读写速度。本文测试的方法是:向SD卡写入100 KB大小的文件,记录在不同参数下所用的时间。最后通过测试发现,使用usleep(10)函数写入100 KB的文件大致需要18 s;使用usleep(1)函数写入100 KB的文件大致需要3 s。通过初略的估算,usleep(10)的情况下,数据率约为50 kb/s=6.125 kB/s,写入100 KB内容需要16.3 s,再加上SD卡初始化阶段的一些通信命令和读写命令的开销时间,可以认为是在正常范围。
4结论
本文在NIOS下利用软件模拟SPI,对SD卡进行了准确的读写,然后移植FATFS文件系统来管理SD卡。提出了可以通过改变SPI SCLK的时钟频率来改变SD卡的读写速率,并且测试了在不同速率下SD卡写文件的速率,可以解决在NIOS II下使用SPI不可改变SCLK速率的问题。
参考文献
[1] SD Group. SD specifications part1: physical layer simplified specification version 2.0[Z].2006.
[2] ALTERA. NIOS II processor Reference Handbook[Z].2014.
[3] 蔡伟刚. NIOS II 软件架构解析[M].西安:西安电子科技大学出版社,2007.
[4] 陈续,邓中亮.基于NIOS II的SD卡驱动程序开发[J].电子设计工程,2010,18(5):107-110.