STM32CubeMX系列|FATFS文件系统

FATFS文件系统

1.FATFS文件系统简介

文件系统是操作系统用于明确存储设备或分区上的文件的方法和数据结构(即在存储设备上组织文件的方法)。操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统;不带文件系统的SD卡仅能实现简单的读写扇区操作,要真正应用SD卡必须要使用文件系统

FATFS文件存储原理(以FAT32为例),下图为FAT32文件系统布局:

在这里插入图片描述
簇是文件存储的最小单元,FAT32分区大小与对应簇空间大小关系如下表示:

分区空间大小簇空间大小每个簇包含的扇区数
< 8GB4KB8
[ 8GB, 16GB )8KB16
[ 16GB, 32GB )16KB32
>= 32GB32KB64

例如:创建一个50字节的test.txt文件,文件大小是50字节,但是占用磁盘空间为4096字节(一个簇)

在这里插入图片描述

FATFS是一个完全免费开源,专为小型嵌入式系统设计的FAT(File Allocation Table)文件系统模块。FATFS的编写遵循ANSI C,并且完全与磁盘I/O层分开。支持FAT12/FAT16/FAT32,支持多个存储媒介,有独立的缓冲区,可对多个文件进行读写。兼容Windows文件系统。

FATFS模块的层次结构如下图示:

在这里插入图片描述

  • 最顶层是应用层:使用者只需要调用FATFS模块提供给用户的一系列应用接口函数(如f_open, f_read, f_write和f_close等),就可以像在PC上读写文件那样简单
  • 中间层FATFS模块:实现了FAT文件读写协议;它提供了ff.cff.h文件,一般情况下不用修改,使用时将头文件包含进去即可
  • 最底层是FATFS模块的底层接口:包括存储媒介读写接口和供给文件创建修改时间的实时时钟,需要在移植时编写对应的代码

FATFS源码相关文件介绍如下表示;移植FATFS模块时,一般只需要修改2个文件(即ffconf.hdiskio.c

与平台无关
ffconf.hFATFS模块配置文件
ff.hFATFS和应用模块公用的包含文件
ff.cFATFS模块
diskio.hFATFS和disk I/O模块公用的包含文件
interger.h数据类型定义
option可选的外部功能(比如支持中文)
与平台相关
diskio.cFATFS和disk I/O模块接口层文件

FATFS的移植主要分为三步:

  • 数据类型:在integer.h里面去定义好数据的类型
  • 配置:通过ffconf.h配置FATFS相关功能
  • 函数编写:在diskio.c文件中进行底层驱动编写(6个接口函数disk_initialize、disk_status、disk_read、disk_write、disk_ioctl、get_fattime)

本例程使用的是STM32CubeMX图形化软件配置FATFS,只需要简单设置即可以完成以上三步的工作。下图是STM32CubeMX的FATFS底层实现框图:

在这里插入图片描述

STM32CubeMX生成的代码工程里,涉及底层驱动实现的文件主要有以下三个:

ff_gen_drv.c文件:FATFS提供的通用驱动文件的实现

//ff_gen_drv.h文件内容
/*Disk IO Driver structure definition */
typedef struct
{
  DSTATUS (*disk_initialize) (BYTE);	//通过指针指向diskio.c中的disk_initialize()
  DSTATUS (*disk_status)     (BYTE);    //通过指针指向diskio.c中的disk_status()
  DRESULT (*disk_read)       (BYTE, BYTE*, DWORD, UINT);	//指向disk_read()
#if _USE_WRITE == 1 
  DRESULT (*disk_write)      (BYTE, const BYTE*, DWORD, UINT); 	//指向disk_write()
#endif 
#if _USE_IOCTL == 1  
  DRESULT (*disk_ioctl)      (BYTE, BYTE, void*); 	//指向disk_ioctl() 
#endif
}Diskio_drvTypeDef;

/*brief  Global Disk IO Drivers structure definition*/ 
typedef struct{ 
  uint8_t                 is_initialized[_VOLUMES];	//磁盘是否初始化
  Diskio_drvTypeDef       *drv[_VOLUMES];	//磁盘的驱动
  uint8_t                 lun[_VOLUMES];	//磁盘的编号
  __IO uint8_t            nbr;
}Disk_drvTypeDef;
/* Exported functions ------------------------------------------------------- */
uint8_t FATFS_LinkDriverEx(Diskio_drvTypeDef *drv, char *path, uint8_t lun);
uint8_t FATFS_LinkDriver(Diskio_drvTypeDef *drv, char *path);
uint8_t FATFS_UnLinkDriver(char *path);
uint8_t FATFS_LinkDriverEx(Diskio_drvTypeDef *drv, char *path, BYTE lun);
uint8_t FATFS_UnLinkDriverEx(char *path, BYTE lun);
uint8_t FATFS_GetAttachedDriversNbr(void);

sd_diskio.c:针对SD底层驱动实现,封装成为通用的底层驱动API

//sd_diskio.c文件内容
const Diskio_drvTypeDef  SD_Driver = {
  SD_initialize,	//指向diskio.c中的disk_initialize()
  SD_status,	//指向diskio.c中的disk_status()
  SD_read,	//指向diskio.c中的disk_read()
#if  _USE_WRITE == 1
  SD_write,	//指向diskio.c中的disk_write()
#endif /* _USE_WRITE == 1 */  
#if  _USE_IOCTL == 1
  SD_ioctl,	//指向diskio.c中的disk_ioctl()
#endif /* _USE_IOCTL == 1 */
};	//定义Diskio_drvTypeDef类型的SD_Driver

/* -------------------以上各函数的实现---------------------------*/
DSTATUS SD_initialize(BYTE lun){
  Stat = STA_NOINIT;  
  if(BSP_SD_Init() == MSD_OK){
    Stat &= ~STA_NOINIT;
  }
  return Stat;
}

DSTATUS SD_status(BYTE lun){
  Stat = STA_NOINIT;
  if(BSP_SD_GetCardState() == MSD_OK){
    Stat &= ~STA_NOINIT;
  }
  return Stat;
}

DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count){
  DRESULT res = RES_ERROR;
  uint32_t timeout = 100000;
  if(BSP_SD_ReadBlocks((uint32_t*)buff,(uint32_t) (sector), count, SD_DATATIMEOUT) == MSD_OK){
    while(BSP_SD_GetCardState()!= MSD_OK){
      if (timeout-- == 0)
        return RES_ERROR;
    }
    res = RES_OK;
  } 
  return res;
}

#if _USE_WRITE == 1
DRESULT SD_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count){
  DRESULT res = RES_ERROR;
  uint32_t timeout = 100000;
  if(BSP_SD_WriteBlocks((uint32_t*)buff,(uint32_t)(sector),count, SD_DATATIMEOUT) == MSD_OK){
    while(BSP_SD_GetCardState()!= MSD_OK){
      if (timeout-- == 0)
        return RES_ERROR;
    }    
    res = RES_OK;
  }
  return res;
}
#endif /* _USE_WRITE == 1 */

#if _USE_IOCTL == 1
DRESULT SD_ioctl(BYTE lun, BYTE cmd, void *buff){
  DRESULT res = RES_ERROR;
  BSP_SD_CardInfo CardInfo;  
  if (Stat & STA_NOINIT) return RES_NOTRDY;  
  switch (cmd){
  /* Make sure that no pending write process */
  case CTRL_SYNC :
    res = RES_OK;
    break;
  /* Get number of sectors on the disk (DWORD) */
  case GET_SECTOR_COUNT :
    BSP_SD_GetCardInfo(&CardInfo);
    *(DWORD*)buff = CardInfo.LogBlockNbr;
    res = RES_OK;
    break;
  /* Get R/W sector size (WORD) */
  case GET_SECTOR_SIZE :
    BSP_SD_GetCardInfo(&CardInfo);
    *(WORD*)buff = CardInfo.LogBlockSize;
    res = RES_OK;
    break;
  /* Get erase block size in unit of sector (DWORD) */
  case GET_BLOCK_SIZE :
    BSP_SD_GetCardInfo(&CardInfo);
    *(DWORD*)buff = CardInfo.LogBlockSize;
    res = RES_OK;
    break;
  default:
    res = RES_PARERR;
  } 
  return res;
}
#endif /* _USE_IOCTL == 1 */

bsp_driver_sd.c:HAL库的二次封装,将基于SD卡的操作都在该文件下实现

//bsp_driver_sd.h文件内容
uint8_t BSP_SD_Init(void);
uint8_t BSP_SD_ITConfig(void);
void    BSP_SD_DetectIT(void);
void    BSP_SD_DetectCallback(void);
uint8_t BSP_SD_ReadBlocks(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout);
uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout);
uint8_t BSP_SD_ReadBlocks_DMA(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks);
uint8_t BSP_SD_WriteBlocks_DMA(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks);
uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr);
void BSP_SD_IRQHandler(void);
void BSP_SD_DMA_Tx_IRQHandler(void);
void BSP_SD_DMA_Rx_IRQHandler(void);
uint8_t BSP_SD_GetCardState(void);
void    BSP_SD_GetCardInfo(HAL_SD_CardInfoTypeDef *CardInfo);
uint8_t BSP_SD_IsDetected(void);
void    BSP_SD_AbortCallback(void);
void    BSP_SD_WriteCpltCallback(void);
void    BSP_SD_ReadCpltCallback(void);

FATFS提供的应用接口如下图示:

在这里插入图片描述

下面简单介绍几个常用操作函数:

f_mount:在FATFS模块上注册/注销一个工作区(文件系统对象)

FRESULT f_mount(FATFS* fs, const TCHAR* path, BYTE opt);
参数--> fs:fs工作区(文件系统对象)指针,如果赋值为 NULL 可以取消物理设备挂载
		path:注册/注销工作区的逻辑设备编号,使用设备根路径表示
		opt:注册或注销选项(可选01),0表示不立即挂载,1表示立即挂载

f_mkfs:格式化物理设备

FRESULT f_mkfs(const TCHAR* path, BYTE sfd, UINT au);
参数--> path:逻辑设备编号,使用设备根路径表示
		sfd:01,0表示为硬盘设备;1表示为软盘设备
		au:指定扇区大小,若为0表示通过disk_ioctl函数获取

f_open:创建/打开一个文件对象

FRESULT f_open(FIL* fp, const TCHAR* path, BYTE mode);
参数--> fp:将创建或打开的文件对象指针
		path:文件名指针,指定将创建或打开的文件名(包含文件类型后缀名)
		mode:访问类型和打开方法
mode可选值:
FA_READ		指定读访问对象。可以从文件中读取数据。 与FA_WRITE结合可以进行读写访问。   
FA_WRITE	指定写访问对象。可以向文件中写入数据。与FA_READ结合可以进行读写访问。   
FA_OPEN_EXISTING	打开文件。如果文件不存在,则打开失败。(默认)  
FA_OPEN_ALWAYS		如果文件存在,则打开;否则,创建一个新文件。  
FA_CREATE_NEW   	创建一个新文件。如果文件已存在,则创建失败。   
FA_CREATE_ALWAYS  	创建一个新文件。如果文件已存在,则它将被截断并覆盖。

f_close:关闭一个打开的文件

FRESULT f_close (FIL *fp)
参数--> fp:将被关闭的已打开的文件对象结构的指针

f_write:写入数据到一个已打开的文件

FRESULT f_write (FIL* fp, const void *buff, UINT btw, UINT* bw)
参数--> fp:指向将被写入的已打开的文件对象结构的指针
		buff:指向存储写入数据的缓冲区的指针
		btw:要写入的字节数
		bw:指向返回已写入字节数的UINT变量的指针,返回为实际写入的字节数

f_read:从一个打开的文件中读取数据

FRESULT f_read (FIL* fp, const void *buff, UINT btr, UINT* br)
参数--> fp:指向将被读取的已打开的文件对象结构的指针
		buff:指向存储读取数据的缓冲区的指针
		btr:要读取的字节数
		br:指向返回已读取字节数的UINT变量的指针,返回为实际读取的字节数
2. 硬件设计

D1指示灯用来提示系统运行状态,串口1用来打印调试信息,使用FATFS文件系统对SD卡进行相关操作

  • D1指示灯
  • USART1
  • TF卡

在这里插入图片描述

3. 软件设计
3.1 STM32CubeMX设置
  • RCC设置外接HSE,时钟设置为72M
  • PC0设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平
  • 激活SDIO,详细请参考SD卡例程
  • 打开FATFS文件系统,按下图配置,其余默认值

在这里插入图片描述
在这里插入图片描述

  • 输入工程名,选择工程路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码
3.2 MDK-ARM编程
int main(void){
	HAL_Init();
    SystemClock_Config();
  /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_CRC_Init();
    MX_FSMC_Init();
    MX_SDIO_SD_Init();
    MX_USART1_UART_Init();
    MX_FATFS_Init();
  /* USER CODE BEGIN 2 */
	uint32_t byteswritten;                /* File write counts */
	uint32_t bytesread;                   /* File read counts */
	uint8_t wtext[] = "This is STM32 working with FatFs"; /* File write buffer */
	uint8_t rtext[100];                   /* File read buffers */
	char filename[] = "STM32cube.txt";
	char SensorBuff[100];
	printf("********* STM32CubeMX FatFs Example *********\r\n\r\n");	
	if(f_mount(&SDFatFS,SDPath,1) == FR_OK){
    	printf("f_mount sucess!!! \r\n");
		if(f_open(&SDFile,filename,FA_CREATE_ALWAYS|FA_WRITE) == FR_OK){
      		printf("f_open file sucess!!! \r\n");
			if(f_write(&SDFile,wtext,sizeof(wtext),&byteswritten) == FR_OK){
        		printf("f_write file sucess!!! \r\n");
        		printf("f_write Data : %s\r\n",wtext);
				if(f_close(&SDFile) == FR_OK)
					printf("f_close sucess!!! \r\n");
				else
					printf("f_close error : %d\r\n",retSD);
      		}
			else
				printf("f_write file error\r\n");    
    	}
		else
			printf("f_open file error\r\n");  
  	}else
		printf("f_mount error : %d \r\n",retSD);
		
	retSD = f_open(&SDFile, filename, FA_READ);
    if(retSD)
        printf("f_open file error : %d\r\n",retSD);
    else
        printf("f_open file sucess!!! \r\n");

    retSD = f_read(&SDFile, rtext, sizeof(rtext), (UINT*)&bytesread);
    if(retSD)
        printf("f_read error!!! %d\r\n",retSD);
    else{
        printf("f_read sucess!!! \r\n");
        printf("f_read Data : %s\r\n",rtext);
    }

    retSD = f_close(&SDFile);
    if(retSD)
        printf("f_close error!!! %d\r\n",retSD);
    else
        printf("f_close sucess!!! \r\n");

    if(bytesread == byteswritten)
        printf("FatFs is working well!!!\r\n");

	if(f_open(&SDFile,(const char*)"Sensor.csv",FA_CREATE_ALWAYS|FA_WRITE) == FR_OK){
		printf("Sensor.csv was opened/created!!!\r\n");
		sprintf(SensorBuff, "Item,Temp,Humi,Light\r\n");
		f_write(&SDFile,SensorBuff,strlen(SensorBuff),&byteswritten);
	
		for(int i = 0; i < 10; i++){
			sprintf(SensorBuff, "%d,%d,%d,%d\r\n",i + 1, i + 20, i + 30, i + 40);
			f_write(&SDFile,SensorBuff,strlen(SensorBuff),&byteswritten);
			f_sync(&SDFile);
		}
		f_close(&SDFile);
	}

	while (1){
		HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);
		HAL_Delay(500);
  	}
}
4. 下载验证

如果使用的是新SD卡,需要在电脑上进行格式化,建立FAT文件系统,FATFS才能识别并操作SD卡
在这里插入图片描述
编译无误下载到开发板后,可以看到D1指示灯不断闪烁,串口打印出如下信息:

在这里插入图片描述

将SD卡插入PC后,可以看到SD卡里建立了2个文件:Sensor.csv和STM32cube.txt,打开文件后可以看到写入的数据
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

关注我的公众号,在公众号里发如下消息,即可获取相应的工程源代码:

玩转STM32CubeMX | FATFS文件系统

在这里插入图片描述

  • 17
    点赞
  • 101
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

安迪西嵌入式

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

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

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

打赏作者

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

抵扣说明:

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

余额充值