FatFs文件系统(只针对SPI-Flash)总结

作用

当我们利用SPI操作Flash时往往读写的都是一段连续的扇区,而FatFs文件系统可以将我们要写入的数据拆分成不连续的扇区见缝插针写入,类似与链表一块扇区指向下一块扇区,不需要物理逻辑地址连续也可以读取整个文件。

这是为啥嘞,举个例子。

各各颜色代表各自的文件,A文件占了flash的1个扇区,B占了2个,C文件占了4个扇区

当我对B文件进行删除擦除扇区时,就是以下图那样

 这时我们要写入一个占4扇区大小的新文件时,像SPI我们写的库那样写的都是连续的地址会把C文件的数据覆盖,造成数据损坏,而FatFs文件系统就解决了这个问题。

 它会把新文件拆开来插入见缝插针,每块扇区都有尾指针指向下一块扇区,解决了不连续的问题,把空间利用最大化。

系统生成

常见的windows下的文件系统格式包括FAT32、NTFS、exFAT。
在使用文件系统前,要先对存储介质进行格式化。格式化时会在存储介质上新建一个文件分配表和目录。这样,文件系统就可以记录数据存放的物理地址,剩余空间。

文件系统的结构与特性

使用文件系统时,数据都以文件的形式存储。写入新文件时,先在目录中创建一个文件索引,它指示了文件存放的物理地址,再把数据存储到该地址中。当需要读取数据时,可以从目录中找到该文件的索引,进而在相应的地址中读取出数据。具体还涉及到逻辑地址、簇大小、不连续存储等一系列辅助结构或处理过程。
文件系统的存在使存取数据时,不再是简单地向某物理地址直接读写,而是要遵循它的读写格式。如经过逻辑转换,一个完整的文件可能被分开成多段存储到不连续的物理地址,使用目录或链表的方式来获知下一段的位置。

文件系统的空间示意图

 目录示意图

 文件分配表

 下面一行相当于下个扇区的地址作为索引,如果索引到0xFF代表整个文件索引结束。

索引到0x00代表空间空闲没有占用。

科普

为啥这个文档只有761字节却要占4096字节空间呢,因为一个文件最小占用单位都是一个簇(扇区),一个扇区为4096字节。

 

代码讲解

程序源码可以自行百度去FatFs官网移植,作者已经开源

我们所需修改的都在diskio.c文件中修改

建立设备

将你需要读写的设备定义一个物理编号,在diskio.c文件中立个宏,这里我使用到的是SPI对Flash进行读写


/* 为每个设备定义一个物理编号 */
#define ATA			    0     // 预留SD卡使用
#define SPI_FLASH		1     // 外部SPI Flash

获取设备状态

当我们对Flash发送读取ID指令,收到的ID数据与Flash数据手册ID对应一致的话则证明设备OK没问题

/*-----------------------------------------------------------------------*/
/* 获取设备状态                                                          */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
	BYTE pdrv		/* 物理编号 */
)
{

	DSTATUS status = STA_NOINIT;
	
	switch (pdrv) {
		case ATA:	/* SD CARD */
			break;
    
		case SPI_FLASH:      
      /* SPI Flash状态检测:读取SPI Flash 设备ID */
      if(sFLASH_ID == SPI_FLASH_ReadID())
      {
        /* 设备ID读取结果正确 */
        status &= ~STA_NOINIT;
      }
      else
      {
        /* 设备ID读取结果错误 */
        status = STA_NOINIT;;
      }
			break;

		default:
			status = STA_NOINIT;
	}
	return status;
}

设备初始化

就是将SPI和对应的IO进行初始化再加上面函数的获取状态则证明初始化完成

/*-----------------------------------------------------------------------*/
/* 设备初始化                                                            */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
	BYTE pdrv				/* 物理编号 */
)
{
  uint16_t i;
	DSTATUS status = STA_NOINIT;	
	switch (pdrv) {
		case ATA:	         /* SD CARD */
			break;
    
		case SPI_FLASH:    /* SPI Flash */ 
      /* 初始化SPI Flash */
			SPI_FLASH_Init();
      /* 延时一小段时间 */
      i=500;
	    while(--i);	
      /* 唤醒SPI Flash */
	    SPI_Flash_WAKEUP();
      /* 获取SPI Flash芯片状态 */
      status=disk_status(SPI_FLASH);
			break;
      
		default:
			status = STA_NOINIT;
	}
	return status;
}

读扇区

为啥要做地址偏移嘞,参照你的flash分部说明,在下图说明了我的flash前6M放了一些数据最好不要动。

偏移为啥是1536嘞,一个扇区4096字节。

1536*4096/1024/1024=6M

下面读函数的<<12是扇区*4096求出扇区地址

/*-----------------------------------------------------------------------*/
/* 读扇区:读取扇区内容到指定存储区                                              */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
	BYTE pdrv,		/* 设备物理编号(0..) */
	BYTE *buff,		/* 数据缓存区 */
	DWORD sector,	/* 扇区首地址 */
	UINT count		/* 扇区个数(1..128) */
)
{
	DRESULT status = RES_PARERR;
	switch (pdrv) {
		case ATA:	/* SD CARD */
			break;
    
		case SPI_FLASH:
      /* 扇区偏移6MB,外部Flash文件系统空间放在SPI Flash后面10MB空间 */
      sector+=1536;      
      SPI_FLASH_BufferRead(buff, sector <<12, count<<12);
      status = RES_OK;
		break;
    
		default:
			status = RES_PARERR;
	}
	return status;
}

写扇区

 与读扇区的理论差不多,就函数变了

写入前要对相应的扇区进行擦除

<<12是扇区*4096求出扇区地址

/*-----------------------------------------------------------------------*/
/* 写扇区:见数据写入指定扇区空间上                                      */
/*-----------------------------------------------------------------------*/
#if _USE_WRITE
DRESULT disk_write (
	BYTE pdrv,			  /* 设备物理编号(0..) */
	const BYTE *buff,	/* 欲写入数据的缓存区 */
	DWORD sector,		  /* 扇区首地址 */
	UINT count			  /* 扇区个数(1..128) */
)
{
  uint32_t write_addr; 
	DRESULT status = RES_PARERR;
	if (!count) {
		return RES_PARERR;		/* Check parameter */
	}

	switch (pdrv) {
		case ATA:	/* SD CARD */      
		break;

		case SPI_FLASH:
      /* 扇区偏移6MB,外部Flash文件系统空间放在SPI Flash后面10MB空间 */
			sector+=1536;
      write_addr = sector<<12;    
      SPI_FLASH_SectorErase(write_addr);
      SPI_FLASH_BufferWrite((u8 *)buff,write_addr,count<<12);
      status = RES_OK;
		break;
    
		default:
			status = RES_PARERR;
	}
	return status;
}
#endif

报错

如果你在操作的过程中遇到了报错,基本都有log可以查看

main函数中的操作返回值是

FRESULT res_flash;                /* 文件操作结果 */

根据返回值对应FRESULT对应的枚举可以找到报错的原因,文件是ff.h

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 parameter error */
	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;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

wiyoo0

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

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

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

打赏作者

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

抵扣说明:

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

余额充值