前一贴里利用的是SSP总线下进行扇区的读写,这一贴将在FAT文件系统下进行SD卡的读写。
本来想自己写个简单的FAT文件系统,但是,在前不就,本人写了个FAT在AVR ICC下能够正常的运行,移植到LPC2148中来时死活不运行,这让我对自己写FAT失去了信心。本身FAT就是个比较复杂的东东,没有那么都时间去写。
再说了自己写的总是兼容性上有一定的问题。所以,决定还是移植一个比较好的文件系统吧。
在网络上的文件系统常见的有四个:UC/FS;ZLG/FS;efsl;FatFS。
第一个:UC/FS。文件系统没得什么说的,UCOSII那个公司开发的,稳定性,兼容性应该都不会差。
第二个:ZLG/FS。周立功的很多的开发板上面都送了这个文件系统的源代码的,在网上找到一个现成的读写硬盘的,只是是基于LPC2200系列的处理器的。
第三个:efsl。是一个开源的项目,免费,只需要提供读扇区和写扇区2个函数。第四个是FatFs,跟efsl一样,也是一个开源的项目,移植的时候比efsl多几个简单的函数。
第四个:FatFS。开源,免费,高效!
通过综合考虑,决定移植第四个。
[ 本帖最后由 zhaojun_xf 于 2010-5-20 08:43 编辑 ]网上移植的文字很多,大家可以参考一下:http://www.cnblogs.com/ylshu/archive/2009/09/25/1574106.html
在FatFs R0.08中,文件结构稍有改变。如果需要可以到官方网进行下载:http://elm-chan.org/fsw/ff/00index_e.html
FatFs的FatFs有两个文件夹,一个是 doc ,FatFs的说明,包括特性,系统函数,以及可能的一些问题,另一个就是源代码文件夹src了,总共8个文件,diskio.c和diskio.h是硬件层,ff.c和ff.h是FatFs的文件系统层和文件系统的API层,integer.h是文件系统所用到的数据类型的定义,ffconf.h为文件系统配置文件,tff.c和tff.h是Tiny的文件系统层和文件系统的API层,还有一个00readme.txt简要的介绍了FatFSHE FatFs/Tiny,包括他们所支持的API,怎么配置等等。
移植的问题:
第一个数据类型:在integer.h里面去定义好数据的类型。这里需要了解你用的编译器的数据类型,并根据编译器定义好数据类型。
第二个就是配置:打开ffconf.h(我用的FatFs,不是Tiny,可以在此头文件中进行定义),文件系统的配置裁剪等均在此头文件中进行定义配置。
第三个函数编写:打开diskio.c,进行底层驱动编写。
disk_initialize : 如果不需要可以直接返回0;也可以调用SD初始化函数。
disk_status: 不需要直接返回。
disk_read: 注意参数,如果读取多个扇区时一定要编写一个读多个扇区的函数,而不能用循环来调用读单个扇区的函数否则效率就不搞了。
disk_write: 和上面的disk_read函数一样。
disk_ioctl: 仅仅在格式化的时候被使用,在调试读写的时候,这个函数直接让他返回0就OK 了。
get_fattime: 获取时间,此函数是通过RTC获取,如果没有此功能的可以用别的方式获取,不用的话,返回0就行。
移植就主要是更改这些,之后就可以运行试试了。
[ 本帖最后由 zhaojun_xf 于 2010-5-20 14:34 编辑 ]
/**************************************************************************************
* FunctionName : MMCReadMultipleBolck()
* Description : 读取一扇区数据到buffer缓冲中
* EntryParameter : NO
* ReturnValue : 返回操作状态:失败-1;成功-0
**************************************************************************************/
uint8_t MMCReadMultipleBolck(uint32_t addr,uint8_t *buf,uint8_t count)
{
uint16_t i;
if (MMCWriteCmd(CMD18,0xFF,addr) != 0x00) // 发送CMD18
{
return 1; // 读取失败
}
do
{
while (SSPTransceiver(0xFF) != 0xFE)
{
; // 等待数据接受开始,受到0xFE表示开始
}
for (i=0; i<512; i++) // 读取数据
{
*buf++ = SSPTransceiver(0xFF);
}
SSPTransceiver(0xFF); // 取走CRC字节
SSPTransceiver(0xFF);
}while (--count);
MMCWriteCmd(CMD12,0xFF,0x00); // CMD12发送停止命令
return 0;
}
添加的SD卡多扇区读写函数:
/**************************************************************************************
* FunctionName : MMCReadMultipleBolck()
* Description : 读取一扇区数据到buffer缓冲中
* EntryParameter : NO * ReturnValue :
返回操作状态:失败-1;成功-0
**************************************************************************************/
uint8_t MMCReadMultipleBolck(uint32_t addr,uint8_t *buf,uint8_t count)
{
uint16_t i;
if (MMCWriteCmd(CMD18,0xFF,addr) != 0x00) // 发送CMD18
{
return 1; // 读取失败
}
do
{
while (SSPTransceiver(0xFF) != 0xFE)
{
; // 等待数据接受开始,受到0xFE表示开始
}
for (i=0; i<512; i++) // 读取数据
{ *buf++ = SSPTransceiver(0xFF); }
SSPTransceiver(0xFF); // 取走CRC字节
SSPTransceiver(0xFF);
}while (--count);
MMCWriteCmd(CMD12,0xFF,0x00); // CMD12发送停止命令
return 0;
}
/**************************************************************************************
* FunctionName : MMCWriteMultipleBlock()
* Description : 把buffer中的数据写入一个扇区中
* EntryParameter : NO
* ReturnValue : NO
**************************************************************************************/
uint8_t MMCWriteMultipleBlock(uint32_t addr,uint8_t *buf,uint8_t count)
{
uint16_t i; uint8_t tmp;
if (MMCWriteCmd(CMD25,0xFF,addr) != 0x00) // 发送CMD25到SD卡中去
{
return 1; // 写入失败
}
do
{
SSPTransceiver(0xFC); // Send start block token 0xfc (0x11111100)
for (i=0; i<512; i++) // 写入数据
{
SSPTransceiver(*buf++);
}
SSPTransceiver(0xFF); // 写入CRC字节
SSPTransceiver(0xFF);
tmp = SSPTransceiver(0xFF); // 读取XXX0 0101字节
tmp &= 0x1F;
if (tmp != 0x05)
{
return 1; // 写入失败
}
while (SSPTransceiver(0xFF) == 0x00)
{
; // BUSY等待
}
}while (--count);
SSPTransceiver(0xFD); // send 'stop transmission token'
while (SSPTransceiver(0xFF) == 0x00)
{
; // BUSY等待
}
return 0;
}
[ 本帖最后由 zhaojun_xf 于 2010-5-22 08:33 编辑 ]移植代码:
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE drv /* Physical drive nmuber (0..) */
)
{
DSTATUS stat;
if (drv)
{
return STA_NOINIT; /* Supports only single drive */
}
if (!MMCInit()) /* Initialization succeded */
{
stat &= ~STA_NOINIT; /* Clear STA_NOINIT */
}
return stat;
}
/*-----------------------------------------------------------------------*/
/* Return Disk Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE drv /* Physical drive nmuber (0..) */
)
{
DSTATUS stat;
if (drv)
{
return STA_NOINIT; /* Supports only single drive */
}
else
{
stat = 0;
}
return stat;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE drv, /* Physical drive nmuber (0..) */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address (LBA) */
BYTE count /* Number of sectors to read (1..255) */
)
{
if (drv || (!count))
{
return RES_PARERR;
}
//if (Stat & STA_NOINIT) return RES_NOTRDY;
if (count == 1) /* Single block read */
{
if (!MMCReadSingleBolck(sector,buff))
{
count = 0;
}
}
else /* Multiple block read */
{
if(!MMCReadMultipleBolck(sector,buff,count))
{
count = 0;
}
}
return (count ? RES_ERROR : RES_OK);
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
/* The FatFs module will issue multiple sector transfer request
/ (count > 1) to the disk I/O layer. The disk function should process
/ the multiple sector transfer properly Do. not translate it into
/ multiple single sector transfers to the media, or the data read/write
/ performance may be drasticaly decreased. */
#if _READONLY == 0
DRESULT disk_write (
BYTE drv, /* Physical drive nmuber (0..) */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address (LBA) */
BYTE count /* Number of sectors to write (1..255) */
)
{
if (drv || (!count))
{
// translate the reslut code here
return RES_PARERR;
}
//if (Stat & STA_NOINIT) return RES_NOTRDY;
if (count == 1) /* Single block write */
{
if (!MMCWriteSingleBlock(sector,buff))
{
count = 0;
}
}
else /* Multiple block write */
{
if(!MMCWriteMultipleBlock(sector,buff,count))
{
count = 0;
}
}
return (count ? RES_ERROR : RES_OK);
}
#endif /* _READONLY */
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE drv, /* Physical drive nmuber (0..) */
BYTE ctrl, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
BYTE n, csd[16];
DWORD csize;
if (drv)
{
return RES_PARERR;
}
//if (stat & STA_NOINIT) return RES_NOTRDY;
res = RES_ERROR;
//if (Stat & STA_NOINIT) return RES_NOTRDY;
switch (ctrl)
{
case CTRL_SYNC : res = RES_OK; break;
case GET_SECTOR_COUNT: /* Get number of sectors on the disk (WORD) */
if((MMCWriteCmd(CMD9,0x95,0x00) == 0) && MMCCSD_CID(CMD9, csd))
{
if((csd[0] >> 6) == 1) /* SDC ver 2.00 */
{
csize = csd[9] + ((WORD)csd[8] << 8) + 1;
*(DWORD*)buff = (DWORD)csize << 10;
}
else /* MMC or SDC ver 1.XX */
{
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
*(DWORD*)buff = (DWORD)csize << (n - 9);
}
res = RES_OK;
}
break;
case GET_SECTOR_SIZE : /* Get sectors on the disk (WORD) */
*(WORD*)buff = 512;
res = RES_OK;
break;
case GET_BLOCK_SIZE : if ((MMCWriteCmd(CMD9,0x95,0x00) == 0) && MMCCSD_CID(CMD9, csd)) /* Read CSD */
{
*(DWORD*)buff = (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);
res = RES_OK;
}
break;
default : res = RES_PARERR; break;
}
return res;
}
编写好以上代码后就可以用API函数进行文件操作了。
FatFs API函数应用之一
// 文件读取函数
/**************************************************************************************
* FunctionName : AppFileRead()
* Description : 读取文件
* EntryParameter : fileName - 需要读取的文件名或路径
* ReturnValue : 成功返回-0;失败返回-1
**************************************************************************************/
uint8_t AppFileRead(const TCHAR *fileName)
{
FATFS fs; /*Work area (file system object) for logical drive*/
FIL file; /*file objects*/
UINT br; /*File R/W count*/
//FRESULT res; /* FatFs function common result code */
/*Register a work area for logical drive 0*/
f_mount(0, &fs);
/*Create file*/
if(f_open(&file, fileName, FA_READ))
{
return 1;
}
else
{
do
{
AppClearBuffer(0x00); // 缓冲清零
if(f_read(&file, Buffer, 512, &br))
{
return 1;
}
else
{
//UARTSend(Buffer, 512);
UARTSendString((const CHAR *)Buffer); // 发送读取文件
}
} while (br); // 判断是否读完(br == 0,表示读取完成)
/*Close all files*/
f_close(&file); // 关闭文件,必须和f_open函数成对出现
}
/*Unregister a work area before discard it*/
f_mount(0, 0);
return 0;
}
在编写这个函数时要注意顺序:
1. f_mount(0, &fs) // 申请空间
2. f_open(&file, fileName, FA_READ) // 打开文件
3. f_read(&file, Buffer, 512, &br) // 读文件
4. f_close(&file) // 关闭文件
5. f_mount(0, 0) // 释放空间
[ 本帖最后由 zhaojun_xf 于 2010-5-26 07:51 编辑 ]
FatFs API函数应用之二
文件扫描
/**************************************************************************************
* FunctionName : AppScanFiles()
* Description : 扫描文件
* EntryParameter : path - 路径
* ReturnValue : 成功返回-0;失败返回-1
**************************************************************************************/
FRESULT AppScanFiles(CHAR* path)
{
FATFS fs; /*Work area (file system object) for logical drive*/
FRESULT res;
FILINFO fno;
DIR dir;
int length; // 目录长度
CHAR *fn;
CHAR pathName[100]; // 存放目录
#if _USE_LFN
static char lfn[_MAX_LFN * (_DF1S ? 2 : 1) + 1];
fno.lfname = lfn;
fno.lfsize = sizeof(lfn);
#endif
f_mount(0, &fs); // 一定不能少
strcpy(pathName,path);
res = f_opendir(&dir, pathName);
res = f_opendir(&dir, path); // 打开目录
if (res == FR_OK)
{
length = strlen(path); // 获取目录长度
for (;;)
{
res = f_readdir(&dir, &fno); // 读目录
if (res != FR_OK || fno.fname[0] == 0)
{
break;
}
if (fno.fname[0] == '.') // 一个点代表当前目录,两个点代表上级目
{
continue;
}
#if _USE_LFN // 长文件名
fn = *fno.lfname ? fno.lfname : fno.fname;
#else // 短文件名
fn = fno.fname;
#endif
if (fno.fattrib & AM_DIR) // Directory
{
//sprintf(&path[length], "/%s", fn);
strcat(pathName,"/");
strcat(pathName,fn);
res = AppScanFiles(pathName);
//res = AppScanFiles(path);
if (res != FR_OK) break;
path[length] = 0;
}
else
{
UARTSendString(fn); // 串口输出文件名
}
}
}
f_mount(0, 0);
return res;
}
说明:此函数是通过FATFS的例程更改的。这个例程有几个问题:
1. f_mount();没有调用这个函数,所以总是会出错的。
2. sprintf(&path[length], "/%s", fn);这个库函数不能调用,否则会出错。
3. 用strcpy()和strcat()代替sprintf()的功能。
[ 本帖最后由 zhaojun_xf 于 2010-5-29 14:28 编辑 ]
FatFs API函数应用之三
写文件
/**************************************************************************************
* FunctionName : AppFileWrite()
* Description : 写一个文件
* EntryParameter : fileName - 需要读取的文件名或路径;buf - 写入的数据;modeFlags - 写入模式
* ReturnValue : 成功返回-0;失败返回-1
**************************************************************************************/
// modeFlags : FA_OPEN_ALWAYS / FA_CREATE_NEW / FA_CREATE_ALWAYS
uint8_t AppFileWrite(const TCHAR *fileName, uint8_t *buf,BYTE modeFlags)
{
FATFS fs; /*Work area (file system object) for logical drive*/
FIL file; /*file objects*/
UINT bw; /*File R/W count*/
/*Register a work area for logical drive 0*/
f_mount(0, &fs);
/*Create file*/
if(f_open(&file, fileName, modeFlags|FA_WRITE))
{
return 1;
}
else
{
do
{
if(f_write(&file, buf, 512, &bw))
{
return 1;
}
} while (bw < 512); // 判断是否读完(bw < 512,表示写入完成)
/*Close all files*/
f_close(&file); // 关闭文件,必须和f_open函数成对出现
}
/*Unregister a work area before discard it*/
f_mount(0, 0);
return 0;
}