【FatFs】基于STM32 SD卡移植FatFs文件系统

本文详细介绍了如何将FatFs文件系统移植到STM32的SD卡驱动上,包括下载FatFs源码、理解其组织架构、配置文件系统参数、修改diskio.c文件以适配SD卡驱动,以及实现实时时钟RTC。通过这些步骤,可以在STM32工程中实现对SD卡的读写和文件操作。
摘要由CSDN通过智能技术生成

相关文章

《【SDIO】SDIO、SD卡、FatFs文件系统相关文章索引》

1.前言

FatFs是一个通用的FAT/exFAT文件系统模块,用于小型嵌入式系统。它完全是由 ANSI C 语言编写并且完全独立于底层的 I/O 介质。因此它是独立于平台的,可以集成到资源有限的小型微控制器中,如8051、PIC、AVR、ARM、Z80、RX等。FatFs 支持 FAT12、FAT16、FAT32、exFAT 等格式,所以我们利用前面写好的 SD卡驱动,把 FatFs 文件系统代码移植到工程。

2.如何下载FatFs文件系统

FatFs 文件系统的源码可以从 fatfs 官网下载:
http://elm-chan.org/fsw/ff/00index_e.html
在这里插入图片描述
下面红色框标记出来的就是FatFs文件系统源码FatFs样例工程,如下:
在这里插入图片描述
下载如下:
在这里插入图片描述

3.FatFs文件系统的组织架构

在移植之前,我们需要对FatFs文件系统有一个大致认识。这样我们才知道哪些是可以直接移植,哪些是需要我们自己来实现的。下面显示的依赖关系图是带有FatFs模块的嵌入式系统的典型配置。绿色部分(Low Level Disk I/O Layer)FatFs文件系统不关心具体的实现,所以需要我们自己来实现它。
在这里插入图片描述
下面展示了单驱动系统多驱动系统,我们在实现了SD卡驱动之后,还需要实现一个实时时钟(RTC),它会在创建或者修改文件时会用到。如果需要同时支持U盘、SD卡、Nand Flash等,需要分别实现对应的驱动,然后挂载在不同的节点,这样FatFs就可以同时管理多个设备。
在这里插入图片描述
下表显示了根据配置选项需要哪些功能,如下:

FunctionRequired whenNote
disk_status
disk_initialize
disk_read
Always Disk I/O functions.
Samples available in ffsample.zip.
There are many implementations on the web.
disk_write
get_fattime
disk_ioctl (CTRL_SYNC)
FF_FS_READONLY == 0
disk_ioctl (GET_SECTOR_COUNT)
disk_ioctl (GET_BLOCK_SIZE)
FF_USE_MKFS == 1
disk_ioctl (GET_SECTOR_SIZE)FF_MAX_SS != FF_MIN_SS
disk_ioctl (CTRL_TRIM)FF_USE_TRIM == 1
ff_uni2oem
ff_oem2uni
ff_wtoupper
FF_USE_LFN != 0 Unicode support functions.
Add optional module ffunicode.c to the project.
ff_cre_syncobj
ff_del_syncobj
ff_req_grant
ff_rel_grant
FF_FS_REENTRANT == 1 O/S dependent functions.
Sample code is available in ffsystem.c.
ff_mem_alloc
ff_mem_free
FF_USE_LFN == 3

4.FatFs文件系统移植步骤

前面我们有实现基于STM32 SDIO SD卡读写测试程序,为移植 FatFs 方便,我们在该工程基础上添加 FatFs 组件,并修改 main 函数的用户程序即可。

  1. 将 FatFs 组件文件添加到工程中,视图如下:
    在这里插入图片描述
    2.将 FatFs 组件相关的头文件添加到工程,如下:
    在这里插入图片描述
    3.修改diskio.c文件,主要是将SD卡的初始化读写操作实时时钟RTC,添加到对应的位置。(备注:实时时钟RTC驱动未真正的实现,只是使用了固定假的时钟。
#include "main.h"
#include "ff.h"			/* Obtains integer types */
#include "diskio.h"		/* Declarations of disk functions */
#include "bsp_sdio_sd.h"

/* Definitions of physical drive number for each drive */
#define DEV_RAM		0	/* Example: Map Ramdisk to physical drive 0 */
#define DEV_MMC		1	/* Example: Map MMC/SD card to physical drive 1 */
#define DEV_USB		2	/* Example: Map USB MSD to physical drive 2 */

#define SD_BLOCKSIZE     512 

static volatile DSTATUS currStat = STA_NOINIT;	/* Physical drive status */
extern SD_CardInfo SDCardInfo;
/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
	DSTATUS stat = STA_NOINIT;
	//int result;

	switch (pdrv) {
	case DEV_RAM :
		break;
	case DEV_MMC :
		stat = currStat;
		break;
	case DEV_USB :
		break;
	}
	return stat;
}

/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
	BYTE pdrv				/* Physical drive nmuber to identify the drive */
)
{
	DSTATUS stat = STA_NOINIT;

	switch (pdrv) {
	case DEV_RAM :
		break;
	case DEV_MMC :
		if(SD_Init()==SD_OK)
		{
			stat &= ~STA_NOINIT;
		}
		else 
		{
			stat = STA_NOINIT;
		}
		currStat = stat;
		break;
	case DEV_USB :
		break;
	}
	return stat;
}

/*-----------------------------------------------------------------------*/
/* 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 */
)
{
	DRESULT res = RES_PARERR;
	SD_Error SD_state = SD_OK;

	if (!count) return RES_PARERR;					/* Check parameter */
	if (currStat & STA_NOINIT) return RES_NOTRDY;	/* Check if drive is ready */

	switch (pdrv) {
	case DEV_RAM :
		break;
		
	case DEV_MMC :
		if(count ==  1){
			SD_state = SD_ReadBlock(buff, sector*SD_BLOCKSIZE, SD_BLOCKSIZE);
		}else{
			SD_state=SD_ReadMultiBlocks(buff,sector*SD_BLOCKSIZE,SD_BLOCKSIZE,count);
		}

		if(SD_state==SD_OK)
		{
			/* Check if the Transfer is finished */
			SD_state=SD_WaitReadOperation();
			
			/* Wait until end of DMA transfer */
			while(SD_GetStatus() != SD_TRANSFER_OK);
		}

		if(SD_state!=SD_OK)
			res = RES_PARERR;
		else
			res = RES_OK;  
		break;

	case DEV_USB :
		break;
	}

	return res;
}

/*-----------------------------------------------------------------------*/
/* 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 */
)
{
	DRESULT res = RES_PARERR;
	SD_Error SD_state = SD_OK;

	if (!count) return RES_PARERR;					/* Check parameter */
	if (currStat & STA_NOINIT) return RES_NOTRDY;	/* Check if drive is ready */
	if (currStat & STA_PROTECT) return RES_WRPRT;	/* Check write protect */

	switch (pdrv) {
	case DEV_RAM :
		break;
		
	case DEV_MMC :
		if(count ==  1){
			SD_state = SD_WriteBlock((uint8_t *)buff, sector*SD_BLOCKSIZE, SD_BLOCKSIZE);
		}else{
			SD_state=SD_WriteMultiBlocks((uint8_t *)buff,sector*SD_BLOCKSIZE,SD_BLOCKSIZE,count);
		}

		if(SD_state==SD_OK)
		{
			/* Check if the Transfer is finished */
			SD_state=SD_WaitWriteOperation();
			
			/* Wait until end of DMA transfer */
			while(SD_GetStatus() != SD_TRANSFER_OK);
		}

		if(SD_state!=SD_OK)
			res = RES_PARERR;
		else
			res = RES_OK;  
			
		break;
		
	case DEV_USB :
		break;
	}

	return res;
}
#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 = RES_PARERR;
	//int result;

	switch (pdrv) {
	case DEV_RAM :
		break;

	case DEV_MMC :
			switch (cmd) 
			{
				// Get R/W sector size (WORD) 
				case GET_SECTOR_SIZE :    
					*(WORD * )buff = SDCardInfo.CardBlockSize;
				break;
				// Get erase block size in unit of sector (DWORD)
				case GET_BLOCK_SIZE :      
					*(DWORD * )buff = SDCardInfo.CardBlockSize;
				break;

				case GET_SECTOR_COUNT:
					*(DWORD * )buff = SDCardInfo.CardCapacity/SDCardInfo.CardBlockSize;
					break;
				case CTRL_SYNC :
				break;
			}
			res = RES_OK;
			break;

	case DEV_USB :
		break;
	}

	return res;
}

__weak DWORD get_fattime(void) {
	/* 返回当前时间戳 */
	return	  ((DWORD)(2021 - 1980) << 25)	/* Year 2021 */
			| ((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 */
}
  1. 修改ffconf.h文件,配置FatFs可能使用到的属性,如下:
--- G:2.stm32f429_sdio_fatfs_test\User\fatfs\ffconf.h
+++ G:2.stm32f429_sdio_fatfs_test\User\fatfs\ffconf.h
@@ -35,13 +35,13 @@
 
 #define FF_USE_FIND		0
 /* This option switches filtered directory read functions, f_findfirst() and
 /  f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
 
 
-#define FF_USE_MKFS		0
+#define FF_USE_MKFS		1
 /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
 
 
 #define FF_USE_FASTSEEK	0
 /* This option switches fast seek function. (0:Disable or 1:Enable) */
 
@@ -65,13 +65,13 @@
 
 
 /*---------------------------------------------------------------------------/
 / Locale and Namespace Configurations
 /---------------------------------------------------------------------------*/
 
-#define FF_CODE_PAGE	932
+#define FF_CODE_PAGE	936
 /* This option specifies the OEM code page to be used on the target system.
 /  Incorrect code page setting can cause a file open failure.
 /
 /   437 - U.S.
 /   720 - Arabic
 /   737 - Greek
@@ -94,13 +94,13 @@
 /   949 - Korean (DBCS)
 /   950 - Traditional Chinese (DBCS)
 /     0 - Include all code pages above and configured by f_setcp()
 */
 
 
-#define FF_USE_LFN		0
+#define FF_USE_LFN		2
 #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.
@@ -160,13 +160,13 @@
 
 
 /*---------------------------------------------------------------------------/
 / Drive/Volume Configurations
 /---------------------------------------------------------------------------*/
 
-#define FF_VOLUMES		1
+#define FF_VOLUMES		2
 /* Number of volumes (logical drives) to be used. (1-10) */
 
 
 #define FF_STR_VOLUME_ID	0
 #define FF_VOLUME_STRS		"RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
 /* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.

  1. 修改main.c文件,对SD卡进行FatFs测试,如下:
static void put_rc (FRESULT rc)
{
	const char *str =
		"OK\0" "DISK_ERR\0" "INT_ERR\0" "NOT_READY\0" "NO_FILE\0" "NO_PATH\0"
		"INVALID_NAME\0" "DENIED\0" "EXIST\0" "INVALID_OBJECT\0" "WRITE_PROTECTED\0"
		"INVALID_DRIVE\0" "NOT_ENABLED\0" "NO_FILE_SYSTEM\0" "MKFS_ABORTED\0" "TIMEOUT\0"
		"LOCKED\0" "NOT_ENOUGH_CORE\0" "TOO_MANY_OPEN_FILES\0" "INVALID_PARAMETER\0";
	FRESULT i;

	for (i = FR_OK; i != rc && *str; i++) {
		while (*str++) ;
	}
	printf("rc=%u FR_%s\n", (UINT)rc, str);
}

static void fatfs_show_volume_status(char *ptr)
{
	FRESULT res;
	long p1;
	FATFS *fs;
	static const char *ft[] = {"", "FAT12", "FAT16", "FAT32", "exFAT"};

	if(ptr == NULL){
		return;
	}

	res = f_getfree("1:/", (DWORD*)&p1, &fs);
	if (res) { put_rc(res); return; }
	printf("FAT type = %s\r\n", ft[fs->fs_type]);
	printf("Bytes/Cluster = %lu\r\n", (DWORD)fs->csize * 512);
	printf("Number of FATs = %u\r\n", fs->n_fats);
	if (fs->fs_type < FS_FAT32) printf("Root DIR entries = %u\r\n", fs->n_rootdir);
	printf("Sectors/FAT = %lu\r\n", fs->fsize);
	printf("Number of clusters = %lu\r\n", (DWORD)fs->n_fatent - 2);
	printf("Volume start (lba) = %lu\r\n", fs->volbase);
	printf("FAT start (lba) = %lu\r\n", fs->fatbase);
	printf("DIR start (lba,clustor) = %lu\r\n", fs->dirbase);
	printf("Data start (lba) = %lu\r\n\r\n", fs->database);

}

static void fatfs_directory_listing(char *ptr)
{
	FRESULT res;
	UINT acc_files, acc_dirs;
	QWORD acc_size;
	DWORD dw;
	FATFS *fs;

	if(ptr == NULL){
		return;
	}

	while (*ptr == ' ') ptr++;
	res = f_opendir(&Dir, ptr);
	if (res) { put_rc(res); return; }
	acc_size = acc_dirs = acc_files = 0;
	for(;;) {
		res = f_readdir(&Dir, &Finfo);
		if ((res != FR_OK) || !Finfo.fname[0]) break;
		if (Finfo.fattrib & AM_DIR) {
			acc_dirs++;
		} else {
			acc_files++; acc_size += Finfo.fsize;
		}
		printf("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu  %s\r\n",
				(Finfo.fattrib & AM_DIR) ? 'D' : '-',
				(Finfo.fattrib & AM_RDO) ? 'R' : '-',
				(Finfo.fattrib & AM_HID) ? 'H' : '-',
				(Finfo.fattrib & AM_SYS) ? 'S' : '-',
				(Finfo.fattrib & AM_ARC) ? 'A' : '-',
				(Finfo.fdate >> 9) + 1980, (Finfo.fdate >> 5) & 15, Finfo.fdate & 31,
				(Finfo.ftime >> 11), (Finfo.ftime >> 5) & 63,
				Finfo.fsize, Finfo.fname);
	}
	printf("%4u File(s),%10llu bytes total\r\n%4u Dir(s)", acc_files, acc_size, acc_dirs);
	res = f_getfree(ptr, &dw, &fs);
	if (res == FR_OK) {
		printf(", %10llu bytes free\r\n", (QWORD)dw * fs->csize * 512);
	} else {
		put_rc(res);
	}
}

int main(void)
{
	/* 禁用WiFi模块 */
	BL8782_PDN_INIT();

	// init led
	LED_GPIO_Config();
	LED1(OFF);
	LED2(OFF);
	LED3(OFF);

	// init uart 
	Debug_USART1_Config();
	printf("starting up!!!\r\n");

	// init systick
	SysTick_Init();

	printf("\r\n****** 这是一个SD卡 文件系统实验 ******\r\n");

	//在外部SPI Flash挂载文件系统,文件系统挂载时会对SPI设备初始化
	res_sd = f_mount(&fs,"1:",1);

	/*----------------------- 格式化测试 ---------------------------*/  
	/* 如果没有文件系统就格式化创建创建文件系统 */
	if(res_sd == FR_NO_FILESYSTEM)
	{
		printf("》SD卡还没有文件系统,即将进行格式化...\r\n");
		/* 格式化 */
		res_sd=f_mkfs("1:",0,work, sizeof(work));							

		if(res_sd == FR_OK)
		{
			printf("》SD卡已成功格式化文件系统。\r\n");
			/* 格式化后,先取消挂载 */
			res_sd = f_mount(NULL,"1:",1);			
			/* 重新挂载	*/			
			res_sd = f_mount(&fs,"1:",1);
		}
		else
		{
			LED1(ON);
			printf("《《格式化失败。》》\r\n");
			while(1);
		}
	}
	else if(res_sd!=FR_OK)
	{
		printf("!!SD卡挂载文件系统失败。(%d)\r\n",res_sd);
		printf("!!可能原因:SD卡初始化不成功。\r\n");
		while(1);
	}
	else
	{
		printf("》文件系统挂载成功,可以进行读写测试\r\n");
	}

	fatfs_show_volume_status("1:/");
	fatfs_directory_listing("1:/");

	/*----------------------- 文件系统测试:写测试 -----------------------------*/
	/* 打开文件,如果文件不存在则创建它 */
	printf("\r\n****** 即将进行文件写入测试... ******\r\n");	
	res_sd = f_open(&fnew, "1:FatFs读写测试文件.txt",FA_CREATE_ALWAYS | FA_WRITE );
	if ( res_sd == FR_OK )
	{
		printf("》打开/创建FatFs读写测试文件.txt文件成功,向文件写入数据。\r\n");
		/* 将指定存储区内容写入到文件内 */
		res_sd=f_write(&fnew,WriteBuffer,sizeof(WriteBuffer),&fnum);
		if(res_sd==FR_OK)
		{
			printf("》文件写入成功,写入字节数据:%d\r\n",fnum);
			printf("》向文件写入的数据为:\r\n%s\r\n",WriteBuffer);
		}
		else
		{
			printf("!!文件写入失败:(%d)\n",res_sd);
		}	 
		/* 不再读写,关闭文件 */
		f_close(&fnew);
	}
	else
	{	
		LED1(ON);
		printf("!!打开/创建文件失败。\r\n");
	}

	/*------------------- 文件系统测试:读测试 ------------------------------------*/
	printf("****** 即将进行文件读取测试... ******\r\n");
	res_sd = f_open(&fnew, "1:FatFs读写测试文件.txt", FA_OPEN_EXISTING | FA_READ);	 
	if(res_sd == FR_OK)
	{
		LED2(ON);
		printf("》打开文件成功。\r\n");
		res_sd = f_read(&fnew, ReadBuffer, sizeof(ReadBuffer), &fnum); 
		if(res_sd==FR_OK)
		{
			printf("》文件读取成功,读到字节数据:%d\r\n",fnum);
			printf("》读取得的文件数据为:\r\n%s \r\n", ReadBuffer);	
		}
		else
		{
			printf("!!文件读取失败:(%d)\n",res_sd);
		}		
	}
	else
	{
		LED1(ON);
		printf("!!打开文件失败。\r\n");
	}
	/* 不再读写,关闭文件 */
	f_close(&fnew); 

	/* 不再使用文件系统,取消挂载文件系统 */
	f_mount(NULL,"1:",1);

	/* 操作完成,停机 */
	while(1)
	{
	}
	//return 0;
}

5.验证测试结果

验证测试结果成功:
在这里插入图片描述

6.参考资料

移植成功的完整代码下载地址如下:
https://download.csdn.net/download/ZHONGCAI0901/16531823

要在STM32F407上移植FATFS文件系统,首先需要了解FATFS是一个开源的文件系统,它可以在嵌入式设备上实现存储器的文件读写操作。 在移植FATFS之前,需要准备一些基本的材料,如软件开发环境、相关的文档和资料。可以从FATFS的官方网站下载最新版本的库文件和相关的示例代码。 首先,创建一个新的工程,并在工程中添加所需的库文件和头文件。将fatfs.c和diskio.c两个源文件添加到工程,并在工程设置中包含相关的头文件目录。 接下来,在工程中添加适配底层硬件的代码,在这里是针对STM32F407的。需要编写适配FATFS的底层I/O读写操作的代码,包括初始化SD卡、读取扇区数据和写入扇区数据。 然后,根据具体需求配置FATFS的参数。可以在fatfs_conf.h文件中修改相关的参数,如簇大小、扇区大小和最大文件数等。这些参数根据实际应用需求进行调整。 最后,编写应用程序代码。可以利用FATFS提供的API来实现文件的打开、读取和写入等操作。需要注意的是在使用FATFS API之前,需要先调用f_mount函数挂载文件系统。 完成以上步骤后,就可以进行编译和烧录操作,将程序下载到STM32F407上进行测试。可以通过读取和写入文件来验证FATFS的正常工作。 总结来说,STM32F407上移植FATFS文件系统需要准备相关的材料,包括库文件和文档。然后添加相关的源文件和头文件到工程中,并编写适配底层硬件的代码。之后根据需求配置FATFS参数,并编写应用程序代码。最后进行编译和烧录操作,进行测试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值