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);  
}