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 文件

需要实现下面这些函数:

1
2
3
4
5
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);

示例如下:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
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 函数测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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);  
}