FatFS文件系统介绍
简要介绍
文件系统,它是为了存储和管理数据,而在存储介质建立的一种组 织结构,这些结构包括操作系统引导区、目录和文件。常见的 windows 下的文件系统格式 包括 FAT32、NTFS、exFAT。在使用文件系统前,要先对存储介质进行格式化。格式化先 擦除原来内容,在存储介质上新建一个文件分配表和目录。这样,文件系统就可以记录数 据存放的物理地址,剩余空间。
文件系统庞大而复杂,它 需要根据应用的文件系统格式而编写,而且一般与驱动层分离开来,很方便移植,所以工 程应用中一般是移植现成的文件系统源码。
FatFs 是面向小型嵌入式系统的一种通用的 FAT 文件系统。它完全是由 ANSI C 语言编 写并且完全独立于底层的 I/O 介质。因此它可以很容易地不加修改地移植到其他的处理器 当中,如 8051、PIC、AVR、SH、Z80、H8、ARM 等。FatFs 支持 FAT12、FAT16、 FAT32 等格式,所以我们利用前面写好的 SPI Flash 芯片驱动,把 FatFs 文件系统代码移植 到工程之中,就可以利用文件系统的各种函数,对 SPI Flash 芯片以“文件”格式进行读写 操作了。
FatFs 文件系统的源码可以从 fatfs 官网下载: http://elm-chan.org/fsw/ff/00index_e.html
源码分析
包括doc/ 和 src/ 两个文件夹,doc 是帮助文档,直接双击 00index_e.html 即可查看。
src 文件夹下的源码文件功能简介如下:
integer.h:文件中包含了一些数值类型定义。
diskio.c:包含底层存储介质的操作函数,这些函数需要用户自己实现,主要添加 底层驱动函数。
ff.c: FatFs 核心文件,文件管理的实现方法。该文件独立于底层介质操作文件的 函数,利用这些函数实现文件的读写。
cc936.c:本文件在 option 目录下,是简体中文支持所需要添加的文件,包含了简 体中文的 GBK 和 Unicode 相互转换功能函数。
ffconf.h:这个头文件包含了对 FatFs 功能配置的宏定义,通过修改这些宏定义就可 以裁剪 FatFs 的功能。
如需要支持简体中文,需要把 ffconf.h 中的_CODE_PAGE 的宏改成 936 并把上面的 cc936.c 文件加入到工程之中。
建议阅读这些源码的顺序为:integer.h --> diskio.c --> ff.c
。
阅读文件系统源码 ff.c 文件需要一定的功底,建议读者先阅读 FAT32 的文件格式(关于FAT文件系统,可以参考FAT32:https://baike.baidu.com/item/FAT32/827339?fr=aladdin),再 去分析 ff.c 文件。若仅为使用文件系统,则只需要理解 integer.h 及 diskio.c 文件并会调用 ff.c 文件中的函数就可以了。
FatFS文件系统移植
FatFs 程序框图
FatFs 组件是 FatFs 的主体,文件都在源码 src 文件夹中,其中 ff.c、ff.h、integer.h 以及 diskio.h 四个文件我们不需要改动,只需要修改 ffconf.h 和 diskio.c 两个文件。
底层设备输入输出要求实现存储设备的读写操作函数、存储设备信息获取函数等等。
FatFs 移植步骤
把FatFs的src目录包含进工程
将FatFs的src目录命名为FATFS,加入diskio.c 和 ff.c 编译,把一些暂时用不到的函数注释起来,确保整个工程编译通过。
移植diskio.c 文件
需要实现下面这些函数:
DSTATUS disk_initialize (BYTE pdrv);
DSTATUS disk_status (BYTE pdrv);
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
示例如下:
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat = STA_NOINIT;
//int result;
switch (pdrv) {
case SD_CARD :
// result = ATA_disk_status();
// translate the reslut code here
return stat;
case SPI_FLASH :
//result = MMC_disk_status();
// translate the reslut code here
if(sFLASH_ID == SPI_FLASH_ReadID())
{
stat &= ~STA_NOINIT;
}
else
{
stat = STA_NOINIT;
}
return stat;
}
return STA_NOINIT;
}
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat;
//int result;
int i;
switch (pdrv) {
case SD_CARD :
//result = ATA_disk_initialize();
// translate the reslut code here
return stat;
case SPI_FLASH :
//result = MMC_disk_initialize();
// translate the reslut code here
SPI_FLASH_Init();
i=500;
while (--i);
SPI_Flash_WAKEUP();
stat = disk_status(SPI_FLASH);
return stat;
}
return STA_NOINIT;
}
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to read */
)
{
DRESULT res;
//int result;
switch (pdrv) {
case SD_CARD :
// translate the arguments here
//result = ATA_disk_read(buff, sector, count);
// translate the reslut code here
return res;
case SPI_FLASH :
// translate the arguments here
//result = MMC_disk_read(buff, sector, count);
// translate the reslut code here
/* 扇区偏移 2MB,外部 Flash 文件系统空间放在 SPI Flash 后面 6MB 空间 */
sector+=512;
SPI_FLASH_BufferRead(buff, sector <<12, count<<12);
res = RES_OK;
return res;
}
return RES_PARERR;
}
#if _USE_WRITE
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to write */
)
{
DRESULT res;
//int result;
switch (pdrv) {
case SD_CARD :
// translate the arguments here
//result = ATA_disk_write(buff, sector, count);
// translate the reslut code here
return res;
case SPI_FLASH :
// translate the arguments here
//result = MMC_disk_write(buff, sector, count);
// translate the reslut code here
/* 扇区偏移 2MB,外部 Flash 文件系统空间放在 SPI Flash 后面 6MB 空间 */
sector+=512;
SPI_FLASH_SectorErase(sector << 12);
SPI_FLASH_BufferWrite((u8 *)buff,sector << 12,count<<12);
res = RES_OK;
return res;
}
return RES_PARERR;
}
#endif
#if _USE_IOCTL
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
//int result;
switch (pdrv) {
case SD_CARD :
// Process of the command for the ATA drive
return res;
case SPI_FLASH :
// Process of the command for the MMC/SD card
switch(cmd)
{
case GET_SECTOR_COUNT:
*((DWORD *) buff) = 1536;
break;
case GET_SECTOR_SIZE:
*(WORD *)buff = 4096;
break;
case GET_BLOCK_SIZE:
*(DWORD *)buff = 1;
break;
}
res = RES_OK;
return res;
}
return RES_PARERR;
}
DWORD get_fattime (void)
{
/* 返回当前时间戳 */
return ((DWORD)(2015 - 1980) << 25) /* Year 2015 */
| ((DWORD)1 << 21) /* Month 1 */
| ((DWORD)1 << 16) /* Mday 1 */
| ((DWORD)0 << 11) /* Hour 0 */
| ((DWORD)0 << 5) /* Min 0 */
| ((DWORD)0 >> 1); /* Sec 0 */
}
编写main 函数测试
int main(void)
{
FRESULT res;
LED_GPIO_Config();
LED_BLUE;
/* 配置串口为:115200 8-N-1 */
USART_Config();
printf("\r\n 这是一个 SPI FLASH 文件系统实验 \r\n");
//在外部 SPI Flash 挂载文件系统,文件系统挂载时会对 SPI 设备初始化
//初始化函数调用流程如下
//f_mount()->find_volume()->disk_initialize->SPI_FLASH_Init()
res = f_mount(&fs,"1:",1);
printf("\r\n f_mount res: %d", res);
if(res == FR_NO_FILESYSTEM)
{
res = f_mkfs("1:",0,0);
printf("\r\n f_mkfs res =%d",res);
//格式化后要取消挂载再重新挂载文件系统
res = f_mount(NULL,"1:",1);
res = f_mount(&fs,"1:",1);
printf("\r\n second f_mount res =%d",res);
}
res = f_open(&fp,"1:中文文件名abcdefgadfasd.txt",FA_OPEN_ALWAYS|FA_READ|FA_WRITE);
printf("\r\nf_open res =%d",res);
if(res == FR_OK)
{
res = f_write(&fp,wData,sizeof(wData),&bw);
printf ("\r\nbw= %d",bw);
if(res == FR_OK)
{
f_lseek(&fp,0);
res = f_read (&fp,rData,f_size(&fp),&br);
if(res == FR_OK)
printf ("\r\n文件内容:%s br= %d",rData,br);
}
f_close(&fp);
}
while(1);
}