随着Flash Memory等非易失存储技术的发展,诞生了许多基于Flash存储技术的非易失大容量闪烁存储卡。其中SD卡以体积小、功耗低、口线少、操作方便等优势,被广泛应用于各种数码产品中,如数码相机、MP3等。SD卡同时也为低功耗、便携式动态心电监护仪器的采集、存储系统提供了理想的存储介质。本文详细介绍了SD卡的基本结构、技术特征和FAT16文件系统的原理、组成,开发了一种基于MSP430F449单片机和SD卡的FAT16文件存储系统,对接口电路进行了设计,同时给出了软件系统的设计流程。该系统成功的应用在电量测量仪中,写入的文件能被 Windows操作系统读写,在大容量的现场数据采集和存储方面有着广泛的应用前景。
一、SD卡接口与MSP430F449程序设计
1.1 SD卡的外形和接口如图1所示。根据SD卡与主控制器的通信协议不同,SD卡对外提供两种访问模式[1]:SD模式和SPI模式。所用通信模式不同,SD卡引脚的功能也不同,具体引脚功能如表1所示。在具体通信过程中,主机只能选择其中一种通信模式,而且通信模式一旦选定,系统在通电情况下不能改变。SD模式下,主控制器使用SD总线访问SD卡,而通常的单片机没有硬件SD总线,尽管可以借助通用口线用软件仿真,但访问速度较低,还要占用大量CPU时间,而单片机多具有SPI总线,本文就利用SPI总线实现对SD卡的访问。下面主要介绍SPI总线方式。
在SPI总线模式下,CS为主控制器向卡发送的片选信号,SCLK为主控制器向卡发送的时钟信号,DI(DataIn)为主控制器向卡发送的单向数据信号,DO(DataOut)为卡向主控制器发送的单向数据信号。SD卡的内部结构如图2所示,SD卡内部除了具有大量存储单元外,还具有卡接口控制器、寄存器以及SD和SPI两种模式的对外接口等。外部主控制器访问卡的接口控制器与存储器单元接口相连。这样主控制器并不直接访问存储器,而是通过卡接口控制器根据主控制器的命令自动处理完成,而主控制器无须知道卡内是如何操作、管理存储单元的,这将大大减轻主控制器对存储器操作的负担。SD卡内部有6个信息寄存器,用来设置和保存操作卡的关键信息,有2个状态寄存器,用来记录操作卡的当前状态。
1.2 本设计中的主控制器采用MSP430F149单片机。MSP430系列16位低功耗单片机是一个具有强大处理能力的混合微处理器[2],它具有强大的处理能力以及丰富的片内外设。MSP430F149除能够满足数据采集功能外,还能很容易地实现对SD卡的访问。与SD卡系统相关的特性如下[3]:
(1) 采用16位的RISC结构,有较高的处理速度,在8MHz晶体驱动下,指令周期为125ns,即指令速度可达8MIPS,能够满足高速操作SD卡的功能要求。
(2)具有2个串行通信接口,可用于异步和同步(UART/SPI),可以直接和SD卡的SPI总线连接。
(3)具有6个8位并行端口,且2个8位端口具有中断能力,能够满足口线的需求以及SPI中断的要求。
(4) 片内存储器包括60KB的Flash和2KB的RAM,不需要外扩存储器,就可以满足对SD卡的访问。
(5) 3.3V的工作电压,与SD卡工作电压兼容。
(6) 时钟系统更加完善,可以使用内部时钟,也可以使用外部时钟,通过编程可以切换。
(7) 可实现通过JTAG接口的在线系统调试,方便系统的开发。
MSP430系列单片机的SPI串行接口的主要特点如下:支持3线或4线SPI操作;可选主模式与从模式;接收和发送有各自的寄存器,且接收和发送为双缓存;移位时钟的极性和相位可编程;主模式的时钟频率可控,SPI位传输速率可通过编程选择;7位或8位字符长度;有写冲突保护和总线竞争保护。
对于MSP430的SPI系统,首先通过UxCTL控制寄存器配置USART模块工作于SPI模式,然后再通过UxBR0和UxBR1波特率选择控制寄存器来设置传输速率。SPI模式下,UBR最小的分频因子是2,所以主机最大数据传输速率(b/s)是系统时钟频率的1/2,因而最高可达4Mb/s。
主控制器MSP430F149与SD卡座的电路连接图如图3所示。除了SPI接口的连接外,还有一根控制线P5.0,用于检测SD卡当前是否设置写保护,写保护时该引脚为高电平,否则为低电平。
1.3 SD卡的工作模式
系统的设计采用串行外设协议(SPI)的连接方式。串行外设协议消息由指令、回应和数据块组成,所有的操作均由主设备控制。主设备每次开始传送任务时,都先将片选端置低电平,以激活SD进入工作状态。SD卡由指令控制,支持特定的指令格式,且每一条指令发送后,SD卡都会有一个应答,以表明卡的状态。
1.4SD卡的初始化
SD卡的初始化流程如图2所示。SD卡上电后的默认模式是SD模式,必须通过初始化命令进入串行外设协议模式。CMD0命令被成功接收后,SD卡会向单片机返回0x01,进入idle_state模式。然后发送CMD1命令,发送成功,SD卡就会返回0x00的八位二进制数,通知主控SD卡初始化完成。
需要注意的是,SD卡在进入SPI模式前,空闲状态只接受SD命令,命令的CRC(循环冗余校验)域必须有效,进入后,缺省为内容无效;在选择进入SPI模式后,重新上电前不能返回SD模式;SPI方式中,选中的SD卡总响应命令,而不是超时。
图2 SD卡初始化流程图
SD卡的部分初始化代码及子程序如下:
while (status != 0) // 如果返回非空,则有错误发生或者SD卡将被再次初始化
{
status = initSD(); //SD卡初始化
timeout++;
if (timeout == 50) // 如果有错误,则重试50次
{
printf ("No SD-card found!! %x\n", status); //无SD卡发现
break;
}
}
while ((SD_ping() != SD_SUCCESS)); //等待,直到卡插入
char initSD (void)
{
int i;
initSPI(); //初始化SPI接口及功能
CS_HIGH();
for(i=0;i<=9;i++)
spiSendByte(0xff);
return (SD_GoIdle()); //返回SD卡的状态信息
}
char SD_GoIdle()
{
char response=0x01;
CS_LOW(); //CS置低 SDSendCmd(SD_GO_IDLE_STATE,0,0x95); //发送命令0使SD卡处于SPI模式
if(SDGetResponse()!=0x01) //等待准备好响应
return SD_INIT_ERROR; //如果响应不正确,则返回SD卡错误
while(response==0x01)
{
CS_HIGH(); //CS置高
spiSendByte(0xff); //SPI发送一个字节
CS_LOW(); //CS置低
SDSendCmd(SD_SEND_OP_COND,0x00,0xff); //发送CMD1命令
response=SDGetResponse(); //获取SD卡的响应
}
CS_HIGH(); //CS置高
spiSendByte(0xff); //SPI发送一个字节
return (SD_SUCCESS); //返回SD卡初始化成功信息
}
二、FAT16文件系统结构分析
要使写入SD卡的数据在Windows下访问,需要在SD卡上创建Windows支持的FAT16文件系统,SD卡上的FAT16文件系统的结构包含分区引导记录、文件分配表、文件目录表及数据区4个部分。
分区引导记录通常包含4块内容:BIOS参数记录块BPB、磁盘标志记录簿、分区引导记录代码区及结束标志55AAH。
BPB表从扇区字节地址0BH开始,共占25个字节,表2是从SD卡的首扇区读出的BPB表的内容示例。
在分区引导记录之后是文件分配表FAT(File Allocation Table)区。FAT16的文件系统中有两份完全相同的文件分配表FAT1和FAT2,每份FAT占用空间的大小可从BPB表中查得。文件在磁盘上以簇为单位存储,但是同一个文件的数据并不一定完整地存放在磁盘的一个连续的区域内,往往会分成若干簇,FAT表就是记录文件存储中簇与簇之间连接信息的,这就是文件的链式存储。FAT16以2个字节(即16位)表示1个簇,起始2个字节为F8FFH、FFFFH,FFFFH表示终止,0000H时表示未使用。
紧跟在FAT表之后的是文件目录表FDT(File Directory Table),它固定占32个扇区,每个扇区可以容纳16个登记项,每个登记项的长度为32B。登记项的内容包括文件名、文件属性、文件修改时间和文件长度等。
文件目录表之后就是数据区,用来存放文件数据,占用大部分磁盘空间。
三、FAT16文件系统的实现
单片机对SD卡底层的读写,按照FAT16的格式对SD卡上数据进行操作,就可在SD卡上创建文件、读写文件和删除文件,从而实现文件的管理。
为了程序的可移植性和易用性,将整个文件系统分为3层,第1层是SD卡驱动层,第2层是FAT16文件系统层,第3层是应用层,提供给用户的接口函数。
SD卡驱动层完成SD卡控制器中相应寄存器的设置和向SD卡发送命令,实现SD卡初始化、读、写等操作,目的是为FAT16文件系统层提供相应的功能函数,屏蔽直接对硬件的具体操作。
FAT16文件系统层向应用层提供对文件和目录进行操作的API接口函数,使写入SD卡的数据能够在被装有Windows操作系统的上位机中得到正确访问。
应用层直接使用文件系统层的接口函数完成对数据的操作,而不需要考虑使用函数的细节问题以及底层硬件的相关问题。
FAT16文件系统向应用层提供的接口函数有:CreateFile(),创建文件;OpenFile(),打开文件;WriteFile(),写文件;ReadFile(),读文件;DelFile(),删除文件等。其中创建、读写、删除文件是应用程序使用的主要函数接口,下面对其作详细介绍。
(1) 文件的创建
在SD卡上创建文件或目录的过程就是在文件目录表FDT中申请登记项的过程,流程如图4所示。登记项包括文件名、文件长度、起始簇号等内容。
(2) 文件的读/写
SD卡上文件都是以簇为单位存取的。读SD卡上的文件,首先要根据文件名查找到该文件的目录登记项,根据目录登记项中的起始簇号既可找到文件在数据区中第1簇的内容,又可在FAT表中找到第2个簇号,根据第2个簇号又能找到第2簇的内容和FAT表中第3个簇号,依此类推,可以读取全部文件数据。向SD卡写文件,要保证FAT1和FAT2中内容的一致性,即对两块都要进行同样的写操作。
(3)文件的删除
删除文件时,不涉及数据区的操作,只须在文件目录登记项上作一个删除标记,并把文件在FAT表中所占有的簇标记为“空簇”。
通过对SPI模式下SD卡读写和文件系统的研究,实现了单片机对SD卡FAT16文件的管理,包括文件的创建、读写、删除等操作。该方法为数据采集系统提供了一种非易失性存储器的解决方案,采集到的数据会以标准文件的格式记录到SD卡上,数据可在Windows下用读卡器读取,在保证高性价比的同时,又方便了数据的进一步分析、处理。通过长时间在心电图监测系统中的实际应用,证明了该方法的可行性。该文件系统的分层结构使得只需对底层驱动进行简单的修改就可以移植到闪存卡等其他存储介质上。该方案也可以应用到如MP3等与Windows有交互的移动存储设备中,便于文件的统一管理。
下面给出关于FAT16文件系统操作的部分子函数程序代码,如下所示:
cardSize = SD_ReadCardSize(); //判断SD卡的容量
ReadBPB(); //读取SD卡的BPB
CreateFile("TEST0001TXT"); //创建测试文件
ReadBlock(FATStartSec()); //读FAT表
ReadBlock(DirStartSec()); //读根目录
ReadBlock(DataStartSec()); //读数据开始区
SD_GoIdle(); //设置SD卡处于空闲状态
void ReadBPB(void) //读取BPB数据结构子程序
{
FAT_BPB* BPB = (FAT_BPB*)BUFFER;
ReadBlock(0); //获取参数
BPB_BytesPerSec = BPB->BPB_BytesPerSec;
BPB_SecPerClus = BPB->BPB_SecPerClus;
BPB_RsvdSecCnt = BPB->BPB_RsvdSecCnt;
BPB_NumFATs = BPB->BPB_NumFATs;
BPB_RootEntCnt = BPB->BPB_RootEntCnt;
BPB_TotSec16 = BPB->BPB_TotSec16;
BPB_FATSz16 = BPB->BPB_FATSz16;
BPB_HiddSec = BPB->BPB_HiddSec;
}
void CreateFile(uint8 FileName[11]) //创建文件子程序
{
uint16 ClusID;
DIR FileDir;
EmptyBytes(&FileDir, sizeof(DIR));
CopyBytes(FileName, &FileDir.FileName, 11);
FileDir.FileAttrib = 0x20;
FileDir.FilePosit.Start = GetNextFAT();
ClusID = FileDir.FilePosit.Start;
WriteFAT(ClusID, 0xffff);
WriteFAT2(ClusID, 0xffff);
WriteDIR(GetEmptyDIR(), &FileDir);
}
void ReadBlock(uint32 LBA) //读一个扇区子程序
{
SDReadSector(LBA,BUFFER);
return;
}
void WriteBlock(uint32 LBA) //写一个扇区子程序
{
SDWriteSector(LBA,BUFFER);
return;
}
数据的读写以扇区为单位,一个簇所包含的扇区数由引导区中基本输入输出系统参数的分配表参数来决定,通过根目录找到对应的文件名,格式化完成或进行写操作时,就要新建对应文件名的文件分配表区和根目录区,通过文件分配表区中的保存的簇号,完成对应的数据读写,完成一个簇的操作后,根据文件分配表的链式结构,找到文件的待操作的下一个簇的簇号,进行相应的操作,直到文件结束。
四、结束语
本文介绍的系统可以很方便的进行存储容量的扩展,而且功耗低,满足了长期大量数据存储的要求。可以很方便的应用于小型便携式嵌入式系统中,在数据采集存储方面更加灵活、稳定,摆脱了操作系统的限制。