作用
当我们利用SPI操作Flash时往往读写的都是一段连续的扇区,而FatFs文件系统可以将我们要写入的数据拆分成不连续的扇区见缝插针写入,类似与链表一块扇区指向下一块扇区,不需要物理逻辑地址连续也可以读取整个文件。
这是为啥嘞,举个例子。
各各颜色代表各自的文件,A文件占了flash的1个扇区,B占了2个,C文件占了4个扇区
当我对B文件进行删除擦除扇区时,就是以下图那样
这时我们要写入一个占4扇区大小的新文件时,像SPI我们写的库那样写的都是连续的地址会把C文件的数据覆盖,造成数据损坏,而FatFs文件系统就解决了这个问题。
它会把新文件拆开来插入见缝插针,每块扇区都有尾指针指向下一块扇区,解决了不连续的问题,把空间利用最大化。
系统生成
文件系统的结构与特性
文件系统的空间示意图
目录示意图
文件分配表
下面一行相当于下个扇区的地址作为索引,如果索引到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;