Fatfs
1.Fatfs簡介
FatFs(File Allocation Table File System)是一個專為小型嵌入式系統設計的通用FAT檔案系統模組。它完全由ANSI C語言編寫,獨立於硬體平臺,因此具有很好的可移植性。FatFs支援FAT12、FAT16和FAT32檔案系統,可以用於各種嵌入式平臺,包括但不限於8051、PIC、AVR、SH、Z80、H8、ARM等系列微控制器。
FatFs的主要特點包括:
- 多平臺支援:由於使用ANSI C編寫,FatFs可以在多種作業系統上執行,如Linux、Android、MacOS和Windows。
- 配置靈活性:透過
ffconf.h
配置檔案,可以開啟或關閉各種功能,以適應不同的應用需求。 - 支援多種檔案系統:支援FAT12、FAT16和FAT32,以及exFAT格式。
- 支援多卷:可以支援多個儲存媒介,最多可達10個卷。
- 支援長檔名:支援ANSI/OEM或Unicode編碼的長檔名。
- 支援RTOS:適合在實時作業系統(RTOS)中使用。
- 多種扇區大小支援:支援多種扇區大小,適應不同的儲存裝置。
- 只讀和最小化API:提供只讀模式、最小化的API和I/O緩衝區配置,以滿足不同應用的需求。
FatFs模組層次結構包括底層介面(包括儲存媒介讀寫介面disk I/O
和實時時鐘),中間層FATFS模組(實現FAT檔案讀寫協議),以及最頂層的應用層(提供一系列應用介面函式,如f_open
、f_read
、f_write
和f_close
等)。
FatFs廣泛應用於嵌入式系統和物聯網裝置中,如智慧家居、工業控制、消費電子等領域,特別是在需要訪問可移動儲存介質(如SD卡、USB快閃記憶體驅動器等)的應用中。
2.FatFs特性
- 相容 DOS/Windows 的 FAT/exFAT 檔案系統。
- 獨立於平臺。易於移植。
- 程式程式碼和工作區佔用空間極小。
- 支援多種配置選項:
- ANSI/OEM 或 Unicode 長檔名。
- exFAT 檔案系統、64 位 LBA 和 GPT,適用於大型儲存裝置。
- RTOS 的執行緒安全
- 多卷 (物理驅動器和分割槽)
- 可變扇區大小
- 多個內碼表,包括 DBCS。
- 只讀、可選 API、I/O 緩衝區等。
3.FatFs介面
3.1 FatFs應用介面
如下圖所示,FatFs 為應用程式提供各種檔案系統功能。
- 檔案訪問
- 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 透過下圖所示的簡單媒體訪問介面控制儲存裝置。 一些平臺的實現示例也可從下載中獲取。
- 儲存裝置控制
- 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目錄