FatFS文件系统的使用(STM32)

本文介绍了STM32使用FatFS文件系统进行文件操作的实践,包括FatFS相关文件、驱动测试、重试机制的实现以及复杂文件操作如获取磁盘容量、遍历目录等。针对驱动层的SDMMC问题,通过添加重试机制解决了高速读写时的故障。此外,还展示了如何获取文件大小、数量以及进行文件权限和错误码的理解。
摘要由CSDN通过智能技术生成

目录

1、Fatfs相关文件

2、驱动测试

3、重试机制

4、实现较为复杂的文件操作

        4.1 获取磁盘容量

        4.2 遍历目录,获取文件大小和数量,以及查找\删除等操作

        4.3 认识文件权限和错误码


1、Fatfs相关文件

        fatfs的介绍这里就不过多赘述了,总之单片机上面用FatFS的比较多,已经趋于标准化了。

        另外,本篇文章是基本是基于应用层的。对于ST的HAL库SDMMC部分代码,没有深入研究过(也是目前项目中SD卡的难点所在:在进行高速频繁的文件读写操作时,总是会出现失败现象,但是原因难以排查,只好在应用层加入了重试机制)。

        如下图,分3部分:一是fatFs源码,二是STM32的sdio驱动层代码,三是STM32的HAL库文件。

        

        常见的接口函数:

#define f_size(fp) ((fp)->obj.objsize)
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt);			/* Mount/Unmount a logical drive */
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode);				/* Open or create a file */
FRESULT f_close (FIL* fp);											/* Close an open file object */
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br);			/* Read data from the file */
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw);	/* Write data to the file */
FRESULT f_lseek (FIL* fp, FSIZE_t ofs);								/* Move file pointer of the file object */
FRESULT f_opendir (DIR* dp, const TCHAR* path);						/* Open a directory */
FRESULT f_closedir (DIR* dp);										/* Close an open directory */
FRESULT f_readdir (DIR* dp, FILINFO* fno);							/* Read a directory item */
FRESULT f_mkdir (const TCHAR* path);								/* Create a sub directory */
FRESULT f_unlink (const TCHAR* path);								/* Delete an existing file or directory */
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs);	/* Get number of free clusters on the drive */

        文件系统的常用配置选型:

        如下修改宏定义,(1)编码为437格式(通用英文格式编码),(2)修改长文件名支持方式3和最大长度,即堆内存方式。

2、驱动测试

        如下做了一个简单的驱动测试函数,验证驱动有没有问题,思路很简单:

        挂载文件系统->打开一个新文件->写入数据->关闭文件->卸载文件系统 。

uint8_t ToolingFatfsCommTest(void)
{
	//测试fatfs文件读写功能
	uint8_t byRev = 0;
	uint32_t ulWriteNum;
	FRESULT res;
	FIL fd;
	char test_file[] = "0:test.hex";
	uint8_t test_data[256] = {0x00};
	
	//1、挂载文件系统
	if(f_mount(&SDFatFS,"0:",1) == FR_OK)
	{
		byRev++;//1-挂载成功
		//log_output(LOG_TAG_POWER,LOG_LEVEL_NORMAL,"f_mount \r\n");
		
		res = f_open(&fd,test_file,FA_OPEN_ALWAYS | FA_WRITE | FA_READ);
		if(res == FR_OK)
		{
			byRev++;//2-打开成功
			//log_output(LOG_TAG_POWER,LOG_LEVEL_NORMAL,"f_open \r\n");
			
			memset(test_data,0x7E,256);
			res = f_write(&fd,test_data,256,&ulWriteNum);
			if(res == FR_OK && ulWriteNum == 256)
			{
				byRev++;//3-写入成功
				//log_output(LOG_TAG_POWER,LOG_LEVEL_NORMAL,"f_write \r\n");
			}
			
			f_close(&fd);
			//log_output(LOG_TAG_POWER,LOG_LEVEL_NORMAL,"f_close \r\n");
		}
		
		f_mount(&SDFatFS,"0:",0);
	}
	
	if(byRev >= 3)
	{
		//log_output(LOG_TAG_POWER,LOG_LEVEL_NORMAL,"\r\nTooling FatFS Test OK\r\n");
		return 1;
	}
	
	return 0;
}

3、重试机制

修改sd_diskio.c文件中的读、写函数,添加重试机制,解决偶发的读写错误时卡死的问题。

DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count)
{
  DRESULT res = RES_ERROR;
  uint8_t i;
    
  for(i=0;i<3;i++)
  {
    if(BSP_SD_ReadBlocks((uint32_t*)buff,
		(uint32_t) (sector),
		count, SD_TIMEOUT) == MSD_OK)
	{
		/* wait until the read operation is finished */
		while(BSP_SD_GetCardState()!= MSD_OK)
		{
		}
		res = RES_OK;
		return res;
	}
	else
	{
		BSP_SD_Init();
	}        
  }
 
  return res;
}
DRESULT SD_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
{
  DRESULT res = RES_ERROR;
  uint8_t i;
  
  for(i=0;i<3;i++)
  {
  	res = (DRESULT)BSP_SD_WriteBlocks((uint32_t*)buff, (uint32_t)(sector), count, SD_TIMEOUT);
  	if(res == MSD_OK)
  	{
  		/* wait until the Write operation is finished */
  		while(BSP_SD_GetCardState() != MSD_OK)
  		{
  		}
  		res = RES_OK;
  		return res;
  	}
  	else
  	{
  		BSP_SD_Init();
  	}    
  }
  return res;
}

4、实现较为复杂的文件操作

        4.1 获取磁盘容量

        利用f_getfree函数,获取盘符的相关信息;

        FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs);

        其中nclst返回空闲集群的数量信息(空闲容量),fatfs返回文件系统的相关信息(使用容量)。

void GetSDCardFreeNum(uint32_t* freeSize,uint32_t* totalSize)
{
	FRESULT res;
	FATFS *fs = &SDFatFS;
	DWORD freeClust = 0,freeNum = 0,totalNum = 0;
	
	res = f_getfree("0:",&freeClust,&fs);
	if(res == FR_OK)
	{
		totalNum = (fs->n_fatent-2)*fs->csize;//得到使用容量
		freeNum = freeClust*fs->csize;//获取空闲容量
		
		*freeSize = freeNum>>1;//单位是KB
		*totalSize = totalNum>>1;//单位是KB
	}
	
	return ;
}

        4.2 遍历目录,获取文件大小和数量,以及查找\删除等操作

//封装了一个小函数,获取完整的文件路径名
void GetFilePath(char *path,char *dirPath,char *fileName)
{
	uint8_t byDirPathLen = 0,byFileNameLen = 0;
	
	byDirPathLen = strlen(dirPath);
	byFileNameLen = strlen(fileName);
	
	memcpy(path,dirPath,byDirPathLen);
	memcpy(path+byDirPathLen,"/",1);
	memcpy(path+byDirPathLen+1,fileName,byFileNameLen);
	
	return;
}

void GetDirectorySizeNum(char *dirPath,uint32_t* fileNum,uint32_t* totalSize)
{
	DIR dir;
	FRESULT res;
	FILINFO FileInfo;
	uint32_t ulFileNum = 0,ulTotalSize = 0;
	
    char filepath[64] = {0};

	res = f_opendir(&dir,dirPath);
	if(res == FR_OK)
	{
		for(;;)
		{
			res = f_readdir(&dir,&FileInfo);
			if(res != FR_OK || FileInfo.fname[0] == 0)
				break;
			else
			{
            //此处实现统计功能
				ulFileNum++;//统计目录下面的文件个数
				ulTotalSize += FileInfo.fsize;//统计文件的总大小

            //此处实现删除指定文件功能
                if(if(strncmp(FileInfo.fname,"0:test.hex",10) == 0))
                {
                    GetFilePath(filepath,dirPath,FileInfo.fname);
                    res = f_unlink(filepath);	
                }
			}
		}
		f_closedir(&dir);
	}
	
	*totalSize = ulTotalSize/1024;
	*fileNum = ulFileNum;
}

        4.3 认识文件权限和错误码

        根据不同的权限,一般可以封装出来几种不同的函数用法:

        1、以只读方式打开文件,读取数据(1、读取指定位置和长度的数据,2、读取全部数据);

        2、创建一个新的文件或清除文件数据;

        3、向文件中追加数据;

        4、将数据写入文件中(1、持续写入,2、写入指定位置,3、整个写入);

        5、对文件的数据计算校验值;

        等等,根据自己的需要封装函数,实现起来都很简单。

        错误码,如果出现FR_DISK_ERR磁盘错误,一般是因为驱动原因,需要定位底层驱动函数,或者检查是否存在冲突问题(比如高频率的使用SDIO和其它通讯接口时,往往会出现SDIO读写错误,简单的方法就是SDIO底层读写函数添加重试机制,同时调用Fatfs函数时屏蔽中断或者添加互斥机制),FR_OK就是无错误,其它错误码就是顾名思义的,按照含义来查找自己的程序为什么会出现这个错误即可。

/* File access mode and open method flags (3rd argument of f_open) */
#define	FA_READ				0x01    //只读权限
#define	FA_WRITE			0x02    //只写权限
#define	FA_OPEN_EXISTING	0x00    //打开已存在的文件
#define	FA_CREATE_NEW		0x04    //创建新文件
#define	FA_CREATE_ALWAYS	0x08    //创建新文件,若已存在则覆盖
#define	FA_OPEN_ALWAYS		0x10    //打开一个新文件
#define	FA_OPEN_APPEND		0x30    //打开一个文件,并将文件指针定位到末尾 即追加
/* 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 > _FS_LOCK */
	FR_INVALID_PARAMETER	/* (19) Given parameter is invalid */
} FRESULT;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猪熊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值