1、fatfs文件系统描述
1.1 fatfs文件结构图
1.2 fatfs移植方式
首先从这个系统结构图中了解到fatfs是独立于硬件层的,通过ff.c中的相关 api 接口去调用不同硬件驱动底层,只需要用户去根据硬件的不同,实现相关的底层驱动接口即可。
2、 移植准备步骤
2.1 下载fatfs0.15资源包 [下载地址](http://elm-chan.org/fsw/ff/00index_e.html)
2.2 stm32f407ZGT6工程模板(正点原子)
2.3 W25Q128驱动、spi驱动
3、工程源文件添加
3.1 添加工程文件
3.2 工程文件描述
fatfs源文件:
diskio.c/.h: IO层的实现。
ff.c/.h: FatFs核心文件,文件管理的实现方法。该文件独立于底层介质操作文件的函数,利用这些函数实现文件的读写。
ffconf.h: 这个头文件包含了对FatFs功能配置的宏定义,通过修改这些宏定义就可以裁剪FatFs的功能。
ffsystem.c: 操作系统相关。
ffunicode.c: unicode编码相关。FF_USE_LFN != 0 时必须包含此文件。
w25q128驱动文件
w25qxx.c
w25qxx.h
spi.c
spi.h
4、代码添加
4.1 实现底层驱动接口
DSTATUS disk_initialize (BYTE pdrv);
DSTATUS disk_status (BYTE pdrv);
DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
4.1.1 disk_initialize
此函数在文件系统开始挂载的时候被调用,用于初始化磁盘硬件驱动
/* Physical drive nmuber to identify the drive */
DSTATUS disk_initialize (BYTE pdrv)
{
uint8_t res=0;
switch (pdrv)
{
case EX_FLASH:
{
W25QXX_Init();
FLASH_SECTOR_COUNT = 2048*12;
break;
}
default:
res=1;
}
if(res)return STA_NOINIT;
else return 0; //初始化成功
}
4.1.2 disk_status
获取当前磁盘的状态
/* Physical drive nmuber to identify the drive */
DSTATUS disk_status (BYTE pdrv)
{
return 0;
}
4.1.3 disk_read
在 f_read 被调用的时候,调用 disk_read 到指定的磁盘指定的簇中读取数据
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;
switch(pdrv)
{
case EX_FLASH:
{
for(;count>0;count--)
{
W25QXX_Read(buff, sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE);
sector++;
buff+=FLASH_SECTOR_SIZE;
}
res = RES_OK;
break;
}
default:
res = RES_ERROR;
}
return res;
}
4.1.4 disk_write
在f_write被调用时,调用disk_write到指定的磁盘指定的簇中写入数据
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_OK;
switch(pdrv)
{
case EX_FLASH://外部flash
for(;count>0;count--)
{
W25QXX_Write((uint8_t*)buff, sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE);
sector++;
buff+=FLASH_SECTOR_SIZE;
}
res = RES_OK;
break;
default:
res = RES_ERROR;
}
return res;
}
4.1.5 disk_ioctl
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res = RES_OK;
if(pdrv==EX_FLASH) //外部FLASH
{
switch(cmd)
{
case CTRL_SYNC:
res = RES_OK;
break;
case GET_SECTOR_SIZE:
*(WORD*)buff = FLASH_SECTOR_SIZE;
res = RES_OK;
break;
case GET_BLOCK_SIZE:
*(WORD*)buff = FLASH_BLOCK_SIZE;
res = RES_OK;
break;
case GET_SECTOR_COUNT:
*(DWORD*)buff = FLASH_SECTOR_COUNT;
res = RES_OK;
break;
default:
res = RES_PARERR;
break;
}
}else
res=RES_ERROR;//其他的不支持
return res;
}
4.2 修改配置文件
//配置fatfs系统有多少个逻辑驱动卷使用
#define FF_VOLUMES 2
/* Number of volumes (logical drives) to be used. (1-10) */
5、调用方式
5.1 相关变量定义
BYTE readBuffer[1024]={0}; /* 读缓冲区 */
BYTE writeBuffer[] = "I can do all things !"; /* 写缓冲区 */
FATFS myFs[1]; //逻辑磁盘工作区
FIL myFile; //文件
UINT br,bw; //读写变量
FRESULT res = FR_OK;
5.2 文件系统挂载
/* Mount/Unmount a logical drive */
//FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt);
f_mount(&myFs[0],"1:",1);
5.3 打开、写、关闭
f_open (&myFile, (const TCHAR*)"1:/yyy.txt", FA_CREATE_ALWAYS | FA_WRITE);
f_write(&myFile,writeBuffer,sizeof(writeBuffer),&bw);
f_close(&myFile);
5.4 打开、读、关闭
f_open (&myFile, (const TCHAR*)"1:/yyy.txt", FA_OPEN_EXISTING | FA_READ);
f_read(&myFile, readBuffer, sizeof(readBuffer), &br);
f_close(&myFile);
6、遇到的问题
6.1 文件系统挂载失败
注意观察逻辑磁盘工作区以及磁盘卷标ID需要与 diskio 中的 pdrv 相对应。
6.2 文件第一次写成功,但直接读取失败
需要调用 f_close 关闭文件后以不同的模式重新打开文件
6.3 为什么定义 FLASH_SECTOR_SIZE 为512Bytes
fatfs 中的 sector 与 w25q128 中的 sector 含义不一样
fatfs 中的 sector 是逻辑上的扇区,规定读写操作的最小基本单元
w25q128 中的sector是物理意义上的扇区,256个 block ,每个block有16个 sector ,每个 sector 有4K Bytes
fatfs文件系统规定的每个 sector 大小为 512/1024/2048/4096 字节
并且可以看到 disk_read 是以 sector 大小进行读取数据的
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 )
所以在 disk_read 中调用 W25QXX_Read 函数去读取数据的时候也是按照文件系统规定的扇区大小去读写数据
W25QXX_Read(buff, sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE);
至于下面这几个变量的定义,则是为了满足文件系统的格式需要设定的,但同时满足芯片内存的大小所规定的,当然也可以改成其它形式
#define FLASH_SECTOR_SIZE 512
#define FLASH_BLOCK_SIZE 8
uint16_t FLASH_SECTOR_COUNT=2048*16;
// 2048*16*512 = 16,777,216 = 16M Byte
例如当调用 W25QXX_Read(buff, sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE)读取数据时
传入 sector*FLASH_SECTOR_SIZE 的大小为所操作的 fs 文件中维护的sector所对应物理内存中的地址,
并且以 FLASH_SECTOR_SIZE 字节大小去读取数据,看下面这几条程序
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);
--> for(i=0;i<NumByteToRead;i++)
pBuffer[i]=SPI1_ReadWriteByte(0XFF); //循环读数
当然也会存在当你所需要读取的数据字节大小小于一个 fatfs 中每个 sector 对应的字节大小的情况,
这时就会直接读取整个 sector(512字节)的数据返回到缓冲区中,
然后再按照你所需要的数据长度裁剪,再拷到用户空间 fp->buf中。
f_read()
--> memcpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt);
7. 源文件分享
有空再写~~