【檔案系統】嵌入式檔案系統Fatfs簡介

Emma1111發表於2024-11-02

Fatfs

1.Fatfs簡介

FatFs(File Allocation Table File System)是一個專為小型嵌入式系統設計的通用FAT檔案系統模組。它完全由ANSI C語言編寫,獨立於硬體平臺,因此具有很好的可移植性。FatFs支援FAT12、FAT16和FAT32檔案系統,可以用於各種嵌入式平臺,包括但不限於8051、PIC、AVR、SH、Z80、H8、ARM等系列微控制器。

FatFs的主要特點包括:

  1. 多平臺支援:由於使用ANSI C編寫,FatFs可以在多種作業系統上執行,如Linux、Android、MacOS和Windows。
  2. 配置靈活性:透過ffconf.h配置檔案,可以開啟或關閉各種功能,以適應不同的應用需求。
  3. 支援多種檔案系統:支援FAT12、FAT16和FAT32,以及exFAT格式。
  4. 支援多卷:可以支援多個儲存媒介,最多可達10個卷。
  5. 支援長檔名:支援ANSI/OEM或Unicode編碼的長檔名。
  6. 支援RTOS:適合在實時作業系統(RTOS)中使用。
  7. 多種扇區大小支援:支援多種扇區大小,適應不同的儲存裝置。
  8. 只讀和最小化API:提供只讀模式、最小化的API和I/O緩衝區配置,以滿足不同應用的需求。

FatFs模組層次結構包括底層介面(包括儲存媒介讀寫介面disk I/O和實時時鐘),中間層FATFS模組(實現FAT檔案讀寫協議),以及最頂層的應用層(提供一系列應用介面函式,如f_openf_readf_writef_close等)。

FatFs廣泛應用於嵌入式系統和物聯網裝置中,如智慧家居、工業控制、消費電子等領域,特別是在需要訪問可移動儲存介質(如SD卡、USB快閃記憶體驅動器等)的應用中。

image

2.FatFs特性

  • 相容 DOS/Windows 的 FAT/exFAT 檔案系統。
  • 獨立於平臺。易於移植。
  • 程式程式碼和工作區佔用空間極小。
  • 支援多種配置選項:
    • ANSI/OEM 或 Unicode 長檔名。
    • exFAT 檔案系統、64 位 LBA 和 GPT,適用於大型儲存裝置。
    • RTOS 的執行緒安全
    • 多卷 (物理驅動器和分割槽)
    • 可變扇區大小
    • 多個內碼表,包括 DBCS。
    • 只讀、可選 API、I/O 緩衝區等。

3.FatFs介面

3.1 FatFs應用介面

如下圖所示,FatFs 為應用程式提供各種檔案系統功能。

image

  • 檔案訪問
    • f_open - 開啟/建立一個檔案
    • f_close - 關閉一個開啟的檔案
    • f_read - 從檔案中讀取資料
    • f_write - 寫入資料到檔案中
    • f_lseek - 移動讀/寫指標,擴充套件尺寸
    • f_truncate - 截斷檔案大小
    • f_sync - 重新整理快取資料
    • f_forward - 將資料轉發到資料流
    • f_expand - 為檔案分配一個連續塊
    • f_gets - 讀取字串
    • f_puts - 寫一個字元
    • f_printf - 寫入格式化字串
    • f_tell - 獲取當前讀/寫指標
    • f_eof - 檔案結束測試
    • f_size - 獲取尺寸
    • f_error - 測試錯誤
  • 路徑訪問
    • f_opendir - 開啟一個路徑
    • f_closedir - 關閉一個路徑
    • f_readdir - 讀取目錄項
    • f_findfirst - 開啟一個目錄,讀取匹配的第一個專案
    • f_findnext - 閱讀匹配的下一個專案
  • 檔案和路徑管理
    • f_stat - 檢查檔案或子目錄是否存在
    • f_unlink - 刪除檔案或子目錄
    • f_rename - 重新命名/移動檔案或子目錄
    • f_chmod - 更改檔案或子目錄的屬性
    • f_utime - 更改檔案或子目錄的時間戳
    • f_mkdir - 建立子目錄
    • f_chdir - 更改當前目錄
    • f_chdrive - 更改當前驅動器
    • f_getcwd - 讀取當前目錄和驅動器
  • 卷管理和系統配置
    • f_mount - 註冊/取消註冊卷的工作區
    • f_mkfs - 在邏輯驅動器上建立 FAT 卷
    • f_fdisk - 在物理驅動器上建立分割槽
    • f_getfree - 獲取捲上的可用空間
    • f_getlabel - 獲取卷標
    • f_setlabel - 設定卷標籤
    • f_getlabel - 獲取卷標籤
    • f_setcp - 設定活動內碼表

3.2儲存媒介訪問介面

由於 FatFs 模組是獨立於平臺和儲存介質的檔案系統層,因此它與儲存卡、硬碟和任何型別的儲存裝置等物理裝置完全分離。 儲存裝置控制模組不是 FatFs 模組的一部分,需要由實現者提供。 FatFs 透過下圖所示的簡單媒體訪問介面控制儲存裝置。 一些平臺的實現示例也可從下載中獲取。

image

  • 儲存裝置控制
    • disk_status - 獲取裝置狀態
    • disk_initialize - 初始化裝置
    • disk_read - 讀取資料
    • disk_write - 寫入資料
    • disk-ioctl - 控制裝置相關功能
  • 實時時間時鐘
    • get_fattime - 獲取當前時間

4.FatFs移植

4.1 FatFs原始碼下載

R0.15下載地址:

FatFs - Generic FAT Filesystem Module (elm-chan.org)

4.2 FatFs移植

將FatFs原始碼新增到工程中,然後修改diskio.c,用於打通檔案系統的軟體部分與儲存硬體驅動:

/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs     (C)ChaN, 2019        */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be        */
/* attached to the FatFs via a glue function rather than modifying it.   */
/* This is an example of glue functions to attach various exsisting      */
/* storage control modules to the FatFs module with a defined API.       */
/*-----------------------------------------------------------------------*/

#include "ff.h"			/* Obtains integer types */
#include "diskio.h"		/* Declarations of disk functions */
#include "EN25Q128.h"
#include "malloc.h"	
#include "sdio_sdcard.h"
#include "system.h"
#include "usart.h"

/* Definitions of physical drive number for each drive */
#define DEV_FLASH   1	/* Example: Map MMC/Flash to physical drive 1 */
#define DEV_SD      0   /* Example: Map SD to physical drive 3 */


#define FLASH_SECTOR_SIZE 	512		
//對於EN25Q128
//前12M位元組給fatfs用,12M位元組後,用於存放字型檔,字型檔佔用3.09M. 剩餘部分給自己用	 			    
u16	    FLASH_SECTOR_COUNT=2048*12;	//EN25Q128,前12M位元組給FATFS佔用
#define FLASH_BLOCK_SIZE   	8     	//每個BLOCK有8個扇區

/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
	return STA_NOINIT;
}


/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (
	BYTE pdrv				/* Physical drive nmuber to identify the drive */
)
{
	u8 res=0;	 

	switch (pdrv) {
	case DEV_FLASH :
		while(EN25Q128 != EN25QXX_Init())				//初始化EN25Q128	
  		{
			printf("EN25Q128 Init Error!\r\n");
		}
		FLASH_SECTOR_COUNT=2048*12;//EN25Q1218,前12M位元組分配給FATFS
		break;
	case DEV_SD :
		while(res = SD_Init())//檢測不到SD卡
		{
			printf("SD Card Error!\r\n");		
		}
		break;
	default:
		res = 1;
		break;
	}
	if(res)return  STA_NOINIT;
	else return 0; //初始化成功 
}



/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/

DRESULT disk_read (
	BYTE pdrv,		/* Physical drive nmuber to identify the drive */
	BYTE *buff,		/* Data buffer to store read data */
	LBA_t sector,	/* Start sector in LBA */
	UINT count		/* Number of sectors to read */
)
{
	u8 res=0; 
    if (!count)return RES_PARERR;//count不能等於0,否則返回引數錯誤		 	 
	switch(pdrv)
	{
		case DEV_SD://SD卡
			res=SD_ReadDisk(buff,sector,count);	 
			while(res)//讀出錯
			{
				SD_Init();	//重新初始化SD卡
				res=SD_ReadDisk(buff,sector,count);	
				//printf("sd rd error:%d\r\n",res);
			}
			break;
		case DEV_FLASH://外部flash
			for(;count>0;count--)
			{
				EN25QXX_Read(buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_SIZE);
				sector++;
				buff+=FLASH_SECTOR_SIZE;
			}
			res=0;
			break;
		default:
			res=1; 
	}
   //處理返回值,將SPI_SD_driver.c的返回值轉成ff.c的返回值
    if(res==0x00)return RES_OK;	 
    else return RES_ERROR;	
}



/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/

#if FF_FS_READONLY == 0

DRESULT disk_write (
	BYTE pdrv,			/* Physical drive nmuber to identify the drive */
	const BYTE *buff,	/* Data to be written */
	LBA_t sector,		/* Start sector in LBA */
	UINT count			/* Number of sectors to write */
)
{
u8 res=0;  
    if (!count)return RES_PARERR;//count不能等於0,否則返回引數錯誤		 	 
	switch(pdrv)
	{
		case DEV_SD://SD卡
			res=SD_WriteDisk((u8*)buff,sector,count);
			while(res)//寫出錯
			{
				SD_Init();	//重新初始化SD卡
				res=SD_WriteDisk((u8*)buff,sector,count);	
				//printf("sd wr error:%d\r\n",res);
			}
			break;
		case DEV_FLASH://外部flash
			for(;count>0;count--)
			{										    
				EN25QXX_Write((u8*)buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_SIZE);
				sector++;
				buff+=FLASH_SECTOR_SIZE;
			}
			res=0;
			break;
		default:
			res=1; 
	}
    //處理返回值,將SPI_SD_driver.c的返回值轉成ff.c的返回值
    if(res == 0x00)return RES_OK;	 
    else return RES_ERROR;	
}

#endif


/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
	DRESULT res;						  			     
	if(pdrv==DEV_SD)//SD卡
	{
	    switch(cmd)
	    {
		    case CTRL_SYNC:
				res = RES_OK; 
		        break;	 
		    case GET_SECTOR_SIZE:
				*(DWORD*)buff = 512; 
		        res = RES_OK;
		        break;	 
		    case GET_BLOCK_SIZE:
				*(WORD*)buff = SDCardInfo.CardBlockSize;
		        res = RES_OK;
		        break;	 
		    case GET_SECTOR_COUNT:
		        *(DWORD*)buff = SDCardInfo.CardCapacity/512;
		        res = RES_OK;
		        break;
		    default:
		        res = RES_PARERR;
		        break;
	    }
	}else if(pdrv==DEV_FLASH)	//外部FLASH  
	{
	    switch(cmd)
	    {
		    case CTRL_SYNC:
				res = RES_OK; 
		        break;	 
		    case GET_SECTOR_SIZE:
		        *(WORD*)buff = FLASH_SECTOR_SIZE;
		        res = RES_OK;
		        break;	 
		    case GET_BLOCK_SIZE:
		        *(WORD*)buff = FLASH_BLOCK_SIZE;
		        res = RES_OK;
		        break;	 
		    case GET_SECTOR_COUNT:
		        *(DWORD*)buff = FLASH_SECTOR_COUNT;
		        res = RES_OK;
		        break;
		    default:
		        res = RES_PARERR;
		        break;
	    }
	}else res=RES_ERROR;//其他的不支援
    return res;
}

DWORD get_fattime(void)
{
	return 0;
}

// 動態分配記憶體
void *ff_memalloc(UINT size)
{
	return (void*)mymalloc(SRAM_IN,size);
}

void ff_memfree(void *mf)
{
	myfree(SRAM_IN,mf);
}

再增加一個fatfs_app.c檔案用於為檔案系統物件申請記憶體,建立物件

#ifndef _fatfs_app_H
#define _fatfs_app_H


#include "system.h"
#include "ff.h" 


typedef struct
{
	uint8_t type[6];		   //字尾6個位元組
	uint8_t name[100];		   //路徑和檔名字100個位元組(支援25個漢字大小名字)
} FileNameTypeDef;


extern FATFS *fs[FF_VOLUMES];  
extern FIL *file;	 
extern FIL *ftemp;	 
extern UINT br,bw;
extern FILINFO fileinfo;
extern DIR dir;
extern u8 *fatbuf;//SD卡資料快取區


//f_typetell返回的型別定義
//根據表FILE_TYPE_TBL獲得.在exfuns.c裡面定義
#define T_BIN		0X00	//bin檔案
#define T_LRC		0X10	//lrc檔案
#define T_NES		0X20	//nes檔案

#define T_TEXT		0X30	//.txt檔案
#define T_C			0X31	//.c檔案
#define T_H			0X32    //.h檔案

#define T_WAV		0X40	//WAV檔案
#define T_MP3		0X41	//MP3檔案 
#define T_APE		0X42	//APE檔案
#define T_FLAC		0X43	//FLAC檔案

#define T_BMP		0X50	//bmp檔案
#define T_JPG		0X51	//jpg檔案
#define T_JPEG		0X52	//jpeg檔案		 
#define T_GIF		0X53	//gif檔案   

#define T_AVI		0X60	//avi檔案  


#define	TYPE_BIN		(0X00)
#define	TYPE_LRC		(0X10)
#define	TYPE_GAME		(0X20)
#define	TYPE_TEXT		(0X30)
#define	TYPE_MUSIC		(0X40)
#define	TYPE_PICTURE	(0X50)

 
u8 FATFS_Init(void);							//申請記憶體
u8 FATFS_GetFree(u8 *drv,u32 *total,u32 *free);	//得到磁碟總容量和剩餘容量

#endif

#include "fatfs_app.h"
#include "malloc.h"
#include "string.h"

#define FILE_MAX_TYPE_NUM    7     // 最多FILE_MAX_TYPE_NUM個大類
#define FILE_MAX_SUBT_NUM    4     // 最多FILE_MAX_SUBT_NUM個小類

// 檔案型別列表
u8* const FILE_TYPE_TBL[FILE_MAX_TYPE_NUM][FILE_MAX_SUBT_NUM] = 
{
{"BIN"},
{"LRC"},
{"NES"},			//NES檔案
{"TXT","C","H"},	//文字檔案
{"WAV","MP3","APE","FLAC"},//支援的音樂檔案
{"BMP","JPG","JPEG","GIF"},//圖片檔案
{"AVI"},			//影片檔案
};

///////////////////////////////公共檔案區,使用malloc的時候////////////////////////////////////////////
// 公共檔案區,使用malloc的時候
FATFS *fs[FF_VOLUMES];     // 邏輯磁碟工作區.	
FIL *file;               // 檔案1
FIL *ftemp;	  		     // 檔案2.
UINT br,bw;			     // 讀寫變數
FILINFO fileinfo;	     // 檔案資訊
DIR dir;  			     // 目錄

u8 *fatbuf;			     // SD卡資料快取區

// SD卡資料快取區
///////////////////////////////////////////////////////////////////////////////////////
//為exfuns申請記憶體
//返回值:0,成功
//1,失敗
u8 FATFS_Init(void)
{
    u8 i;
    for(i = 0;i < FF_VOLUMES;i++)
    {
        fs[i] = (FATFS*)mymalloc(SRAM_IN,sizeof(FATFS));   // 為磁碟i工作區申請記憶體	
        if(!fs[i])
			break;
    }
    file = (FIL*)mymalloc(SRAM_IN,sizeof(FIL));            // 為file申請記憶體
    ftemp = (FIL*)mymalloc(SRAM_IN,sizeof(FIL));		   // 為ftemp申請記憶體
    fatbuf = (u8*)mymalloc(SRAM_IN,512);				   // 為fatbuf申請記憶體
    if(i == FF_VOLUMES&&file&&ftemp&&fatbuf) return 0;     // 申請有一個失敗,即失敗.
    else return 1;
}

//得到磁碟剩餘容量
//drv:磁碟編號("0:"/"1:")
//total:總容量	 (單位KB)
//free:剩餘容量	 (單位KB)
//返回值:0,正常.其他,錯誤程式碼
u8 FATFS_GetFree(u8 *drv,u32 *total,u32 *free)
{
    FATFS *fs1;
	u8 res;
    u32 fre_clust=0, fre_sect=0, tot_sect=0;
    //得到磁碟資訊及空閒簇數量
    res =(u32)f_getfree((const TCHAR*)drv, (DWORD*)&fre_clust, &fs1);
    if(res==0)
	{											   
	    tot_sect=(fs1->n_fatent-2)*fs1->csize;	//得到總扇區數
	    fre_sect=fre_clust*fs1->csize;			//得到空閒扇區數	   
#if FF_MAX_SS!=512				  				//扇區大小不是512位元組,則轉換為512位元組
		tot_sect*=fs1->ssize/512;
		fre_sect*=fs1->ssize/512;
#endif	  
		*total=tot_sect>>1;	//單位為KB
		*free=fre_sect>>1;	//單位為KB 
 	}
	return res;
}

編寫測試程式:

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "tftlcd.h" 
#include "malloc.h" 
#include "sdio_sdcard.h" 
#include "EN25Q128.h"
#include "ff.h" 
#include "fatfs_app.h"



int main()
{	
	u8 i=0;
	u32 free_sd,total_sd,free_mmc,total_mmc;
	u8 res=0;
	
	SysTick_Init(168);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //中斷優先順序分組 分2組
	LED_Init();
	USART1_Init(9600);
	TFTLCD_Init();			    //LCD初始化
	my_mem_init(SRAM_IN);		//初始化內部記憶體池
    FATFS_Init();				//為fatfs相關變數申請記憶體				 

	FRONT_COLOR=RED;//設定字型為紅色 
	LCD_ShowString(10,10,tftlcd_data.width,tftlcd_data.height,16,"PRECHIN STM32F4");	
	LCD_ShowString(10,30,tftlcd_data.width,tftlcd_data.height,16,"Fatfs Test");	
	LCD_ShowString(10,50,tftlcd_data.width,tftlcd_data.height,16,"www.prechin.net");
	

    res = f_mount(fs[0],"0:",1); 		//掛載SD卡
    if(res!=FR_OK)
	{
		//LCD_ShowString(10,80,tftlcd_data.width,tftlcd_data.height,16,"Flash Disk Format Error ");	//格式化失敗
        printf(" fs[0] res = %d\n",res);
        delay_ms(1000);
        return res;
	}

    res = f_mount(fs[1],"1:",1); 	//掛載FLASH.
	if(res!=FR_OK)
	{
		//LCD_ShowString(10,80,tftlcd_data.width,tftlcd_data.height,16,"Flash Disk Format Error ");	//格式化失敗
        printf("fs[1] res = %d\n",res);
        delay_ms(1000);
        return res;
	}	

	
 	FRONT_COLOR=BLUE;	//設定字型為藍色 
	//檢測SD卡成功 			
	printf("SD Card & EN25Q128 OK!\r\n");	
	LCD_ShowString(10,100,tftlcd_data.width,tftlcd_data.height,16,"SD Card & EN25Q128 OK    ");
	
	LCD_Fill(10,80,tftlcd_data.width,80+16,WHITE);		//清除顯示
	while(FATFS_GetFree("0:",&total_sd,&free_sd))	//得到SD卡的總容量和剩餘容量
	{
		LCD_ShowString(10,80,tftlcd_data.width,tftlcd_data.height,16,"SD Card Fatfs Error!");
		delay_ms(200);
		led2=!led2;
	}			
    while(FATFS_GetFree("1:",&total_mmc,&free_mmc))	//得到SD卡的總容量和剩餘容量
	{
		LCD_ShowString(10,80,tftlcd_data.width,tftlcd_data.height,16,"flash Fatfs Error!");
		delay_ms(200);
		led2=!led2;
	}		
	LCD_ShowString(10,80,tftlcd_data.width,tftlcd_data.height,16,"FATFS OK!");	 
	LCD_ShowString(10,100,tftlcd_data.width,tftlcd_data.height,16,"SD Total Size:     MB");	 
	LCD_ShowString(10,120,tftlcd_data.width,tftlcd_data.height,16,"SD  Free Size:     MB"); 	  
    LCD_ShowString(10,140,tftlcd_data.width,tftlcd_data.height,16,"FLASH Total Size:     MB");	 
	LCD_ShowString(10,160,tftlcd_data.width,tftlcd_data.height,16,"FLASH  Free Size:     MB");   
 	LCD_ShowNum(10+8*14,100,total_sd>>10,5,16);	//顯示SD卡總容量 MB
 	LCD_ShowNum(10+8*14,120,free_sd>>10,5,16);     //顯示SD卡剩餘容量 MB
    LCD_ShowNum(10+8*14,140,total_mmc>>10,5,16);	//顯示MMC卡總容量 MB
 	LCD_ShowNum(10+8*14,160,free_mmc>>10,5,16);     //顯示MMC卡剩餘容量 MB
	
	while(1)
	{
		i++;
		if(i%10==0)
		{
			led1=!led1;
		}
		delay_ms(10);	
	}
}

參考資料:

Fatfs中的doc目錄

相關文章