什么是FATFS?

FATFS(FAT File System)是一个开源的文件系统模块,它被设计用于嵌入式系统中,支持FAT(File Allocation Table)文件系统。

FATFS支持哪些存储设备?

  1. SD卡、TF卡
  2. MMC卡
  3. USB存储设备
  4. NOR和NAND闪存

FATFS中常用的文件操作函数有哪些?

以下函数全部在ff.c文件中定义。

/*-----------------------------------------------------------------------*/
/* Mount/Unmount a Logical Drive                                         */
/*-----------------------------------------------------------------------*/
FRESULT f_mount (
	FATFS* fs,			/* Pointer to the filesystem object (NULL:unmount)*/
	const TCHAR* path,	/* Logical drive number to be mounted/unmounted */
	BYTE opt			/* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */
);

/*-----------------------------------------------------------------------*/
/* Open or Create a File                                                 */
/*-----------------------------------------------------------------------*/
FRESULT f_open (
	FIL* fp,			/* Pointer to the blank file object */
	const TCHAR* path,	/* Pointer to the file name */
	BYTE mode			/* Access mode and file open mode flags */
);

FRESULT f_close (
	FIL* fp		/* Pointer to the file object to be closed */
);

/*-----------------------------------------------------------------------*/
/* Read File                                                             */
/*-----------------------------------------------------------------------*/
FRESULT f_read (
	FIL* fp, 	/* Pointer to the file object */
	void* buff,	/* Pointer to data buffer */
	UINT btr,	/* Number of bytes to read */
	UINT* br	/* Pointer to number of bytes read */
);

FRESULT f_write (
	FIL* fp,			/* Pointer to the file object */
	const void* buff,	/* Pointer to the data to be written */
	UINT btw,			/* Number of bytes to write */
	UINT* bw			/* Pointer to number of bytes written */
);

/*-----------------------------------------------------------------------*/
/* Delete a File/Directory                                               */
/*-----------------------------------------------------------------------*/
FRESULT f_unlink (
	const TCHAR* path		/* Pointer to the file or directory path */
);

/*-----------------------------------------------------------------------*/
/* Rename a File/Directory                                               */
/*-----------------------------------------------------------------------*/
FRESULT f_rename (
	const TCHAR* path_old,	/* Pointer to the object name to be renamed */
	const TCHAR* path_new	/* Pointer to the new name */
);

/*-----------------------------------------------------------------------*/
/* Get File Status                                                       */
/*-----------------------------------------------------------------------*/
FRESULT f_stat (
	const TCHAR* path,	/* Pointer to the file path */
	FILINFO* fno		/* Pointer to file information to return */
);

/*-----------------------------------------------------------------------*/
/* Create a Directory Object                                             */
/*-----------------------------------------------------------------------*/
FRESULT f_opendir (
	DIR* dp,			/* Pointer to directory object to create */
	const TCHAR* path	/* Pointer to the directory path */
);

/*-----------------------------------------------------------------------*/
/* Read Directory Entries in Sequence                                    */
/*-----------------------------------------------------------------------*/
FRESULT f_readdir (
	DIR* dp,			/* Pointer to the open directory object */
	FILINFO* fno		/* Pointer to file information to return */
);

/*-----------------------------------------------------------------------*/
/* Close Directory                                                       */
/*-----------------------------------------------------------------------*/
FRESULT f_closedir (
	DIR *dp		/* Pointer to the directory object to be closed */
);

/*-----------------------------------------------------------------------*/
/* Synchronize the File                                                  */
/*-----------------------------------------------------------------------*/
FRESULT f_sync (
	FIL* fp		/* Pointer to the file object */
);

什么是工作目录(Working Directory)?如何设置工作目录?

工作目录(Working Directory)是指当前程序或进程访问和操作文件时的默认目录。它是文件系统操作的基准目录,如果在程序中不指定文件的绝对路径,操作系统会默认从工作目录中寻找文件。

例如,在FATFS文件系统中,当你执行 f_open("myfile.txt", FA_READ); 这样的操作时,如果没有指定绝对路径,系统会假定文件位于当前的工作目录中。

ffconf.h中,存在下面这个宏定义,将这个宏定义置1,即可开启相对路径的支持,从而可以设置工作目录。

#define FF_FS_RPATH		0
/* This option configures support for relative path.
/
/   0: Disable relative path and remove related functions.
/   1: Enable relative path. f_chdir() and f_chdrive() are available.
/   2: f_getcwd() function is available in addition to 1.
*/

下面的f_chdir()f_chdrive()FF_FS_RPATH为1的时候就可以使用,但f_getcwd()必须在FF_FS_RPATH为2的时候才可以使用。

// 该函数用于切换工作目录
FRESULT f_chdir (
	const TCHAR* path	/* Pointer to the directory path */
)

/*-----------------------------------------------------------------------*/
/* Change Current Directory or Current Drive, Get Current Directory      */
/*-----------------------------------------------------------------------*/
FRESULT f_chdrive (
	const TCHAR* path		/* Drive number to set */
)

//该函数用于获取当前工作目录
FRESULT f_getcwd (
	TCHAR* buff,	/* Pointer to the directory path */
	UINT len		/* Size of buff in unit of TCHAR */
)

FATFS中文件名的限制有哪些?

ffconf.h中,有下面的宏定义:

#define FF_USE_LFN		3
#define FF_MAX_LFN		255
/* The FF_USE_LFN switches the support for LFN (long file name).
/
/   0: Disable LFN. FF_MAX_LFN has no effect.
/   1: Enable LFN with static  working buffer on the BSS. Always NOT thread-safe.
/   2: Enable LFN with dynamic working buffer on the STACK.
/   3: Enable LFN with dynamic working buffer on the HEAP.
/
/  To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
/  requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/  additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
/  The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
/  be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
/  specification.
/  When use stack for the working buffer, take care on stack overflow. When use heap
/  memory for the working buffer, memory management functions, ff_memalloc() and
/  ff_memfree() exemplified in ffsystem.c, need to be added to the project. */

如果将FF_USE_LFN定义为0,则只能使用短文件名,文件名最多8个字符,扩展名最多3个字符。

如果将FF_USE_LFN定义为1或2或3,则根据FF_MAX_LFN确定最大文件名。

如何挂载和卸载一个文件系统?

查看f_mount()函数。fs参数传入NULL则是代表卸载。

如何检查存储设备的剩余空间?

ff.c中,存在下面这个函数:

/*-----------------------------------------------------------------------*/
/* Get Number of Free Clusters                                           */
/*-----------------------------------------------------------------------*/

FRESULT f_getfree (
	const TCHAR* path,	/* Logical drive number */
	DWORD* nclst,		/* Pointer to a variable to return number of free clusters */
	FATFS** fatfs		/* Pointer to return pointer to corresponding filesystem object */
);

通过它可以计算设备剩余空间。

/**
 * @brief       获取磁盘剩余容量
 * @param       pdrv : 磁盘编号("0:"~"9:")
 * @param       total: 总容量 (KB)
 * @param       free : 剩余容量 (KB)
 * @retval      0, 正常; 其他, 错误代码
 */
uint8_t exfuns_get_free(uint8_t *pdrv, uint32_t *total, uint32_t *free)
{
    FATFS *fs1;
    uint8_t res;
    uint32_t fre_clust = 0, fre_sect = 0, tot_sect = 0;
    
    /* 得到磁盘信息及空闲簇数量 */
    res = (uint32_t)f_getfree((const TCHAR *)pdrv, (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;
}

如何使用FATFS创建、删除或重命名文件?

创建:

FIL file;
FRESULT res;

res = f_open(&file, "0:/newfile.bmp", FA_CREATE_ALWAYS | FA_WRITE);
if (res == FR_OK) {
    // 文件创建成功
    f_close(&file);
} else {
    // 处理错误
}

//写入数据,下面这行代码来自bmp文件头的写入。
f_write(&file, &bmpFileHeader, sizeof(BMPFileHeader), &bw);

f_close(&file);

删除:

f_unlink(file_path);

重命名:

FRESULT res;
res = f_rename("0:/oldname.txt", "0:/newdir/newname.txt");
if (res == FR_OK) {
    // 移动并重命名成功
} else {
    // 处理错误
}

重命名不仅仅可以在相同目录下进行,也可以起到移动文件的作用,但新旧文件必须位于同一逻辑驱动器上。

如何遍历FATFS中的目录和文件?

void scan_files(void)
{
    f.fr = f_opendir(&f.dir, f.cur_path);               /* 打开文件目录 */
    
    if(f.fr == FR_OK)
    {
        while(true) {
            f.fr = f_readdir(&f.dir, &f.SD_fno);
            if(f.fr != LV_FS_RES_OK || f.SD_fno.fname[0] == 0) break;
            list_btn_data *btn_data = (list_btn_data *) pvPortMalloc(sizeof(list_btn_data));
            if(f.SD_fno.fattrib & AM_DIR)
            {
                //是文件夹
            }
            else
            {
            	//是文件
            }
        }
        f_closedir(&f.dir); /* 关闭文件目录 */
    }
}

如何处理FATFS中的错误?如何解析错误代码?

ff.h中,有下列定义:

/* File function return code (FRESULT) */

typedef enum {
	FR_OK = 0,				/* (0) Succeeded */
	FR_DISK_ERR,			/* (1) A hard error occurred in the low level disk I/O layer */
	FR_INT_ERR,				/* (2) Assertion failed */
	FR_NOT_READY,			/* (3) The physical drive cannot work */
	FR_NO_FILE,				/* (4) Could not find the file */
	FR_NO_PATH,				/* (5) Could not find the path */
	FR_INVALID_NAME,		/* (6) The path name format is invalid */
	FR_DENIED,				/* (7) Access denied due to prohibited access or directory full */
	FR_EXIST,				/* (8) Access denied due to prohibited access */
	FR_INVALID_OBJECT,		/* (9) The file/directory object is invalid */
	FR_WRITE_PROTECTED,		/* (10) The physical drive is write protected */
	FR_INVALID_DRIVE,		/* (11) The logical drive number is invalid */
	FR_NOT_ENABLED,			/* (12) The volume has no work area */
	FR_NO_FILESYSTEM,		/* (13) There is no valid FAT volume */
	FR_MKFS_ABORTED,		/* (14) The f_mkfs() aborted due to any problem */
	FR_TIMEOUT,				/* (15) Could not get a grant to access the volume within defined period */
	FR_LOCKED,				/* (16) The operation is rejected according to the file sharing policy */
	FR_NOT_ENOUGH_CORE,		/* (17) LFN working buffer could not be allocated */
	FR_TOO_MANY_OPEN_FILES,	/* (18) Number of open files > FF_FS_LOCK */
	FR_INVALID_PARAMETER	/* (19) Given parameter is invalid */
} FRESULT;

如何通过FATFS实现文件的随机读写(Seek操作)?

/*-----------------------------------------------------------------------*/
/* Seek File Read/Write Pointer                                          */
/*-----------------------------------------------------------------------*/

FRESULT f_lseek (
	FIL* fp,		/* Pointer to the file object */
	FSIZE_t ofs		/* File pointer from top of file */
);

FATFS的f_sync函数的作用是什么?为什么需要它?

在FATFS文件系统中,f_sync函数的主要作用是将文件的缓存数据同步到存储介质,确保已写入的数据被实际保存。这对于长时间以写模式打开文件的应用(如数据记录器)尤为重要。

/*-----------------------------------------------------------------------*/
/* Synchronize the File                                                  */
/*-----------------------------------------------------------------------*/

FRESULT f_sync (
	FIL* fp		/* Pointer to the file object */
);
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐