硬件

  • STM32F103ZET6
  • Flash芯片:W25Q64JV

FatFs 源码

解压后,源码都在 source 文件夹中

添加到 Keil 工程,记得添加头文件路径

配置 ffconf.h

// 使能 f_mkfs 函数
#define FF_USE_MKFS     1
// 支持 U.S. 编码。中文是936,但是会大大增加烧录程序的大小
#define FF_CODE_PAGE    437
// 支持长文件名
#define FF_USE_LFN      1
// 文件名使用 utf8 编码,如果设置了这一项,则 FF_CODE_PAGE 没有意义
#define FF_LFN_UNICODE  2
// 物理设备数量
#define FF_VOLUMES      1
// 扇区大小的最小值
#define FF_MIN_SS       512
// 扇区大小的最大值
#define FF_MAX_SS       4096
// 不使用时间戳,如果设置0启用,则需要添加 get_fattime 函数
#define FF_FS_NORTC     1

编辑 diskio.c

需修改设备编号 DEV_FLASH 宏定义,及所有函数。

/* This is an example of glue functions to attach various exsisting      */
/* storage control modules to the FatFs module with a defined API.       */
/*-----------------------------------------------------------------------*/

#include "ff.h"                 /* Obtains integer types */
#include "diskio.h"             /* Declarations of disk functions */
#include "bsp_flash.h"

/* Definitions of physical drive number for each drive */
#define DEV_FLASH    0


/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
    BYTE pdrv               /* Physical drive nmuber to identify the drive */
)
{
    switch (pdrv) {
        case DEV_FLASH :
            // 通过读 Flash id 判断
            if(FLASH_JADEC_ID == FLASH_ReadJedecID())
                return RES_OK;
            else
                return STA_NOINIT;
        default:
            return STA_NOINIT;
    }
}



/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (
    BYTE pdrv               /* Physical drive nmuber to identify the drive */
)
{
    switch (pdrv) {
        case DEV_FLASH :
            // Flash 初始化
            FLASH_Init();
            return RES_OK;
        default:
            return STA_NOINIT;
    }
}



/*-----------------------------------------------------------------------*/
/* 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 */
)
{
    switch (pdrv) {
        case DEV_FLASH :
            FLASH_Read(sector*FLASH_SECTOR_SIZE, buff, count*FLASH_SECTOR_SIZE);
            return RES_OK;
        default:
            return RES_PARERR;
    }
}



/*-----------------------------------------------------------------------*/
/* 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 */
)
{
    switch (pdrv) {
        case DEV_FLASH :
            // Flash 写入前需擦除扇区
            FLASH_SectorErase(sector*FLASH_SECTOR_SIZE);
            FLASH_Write(sector*FLASH_SECTOR_SIZE, (uint8_t*)buff, count*FLASH_SECTOR_SIZE);
            return RES_OK;
        default:
            return RES_PARERR;
    }
}

#endif



/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

DRESULT disk_ioctl (
    BYTE pdrv,              /* Physical drive nmuber (0..) */
    BYTE cmd,               /* Control code */
    void *buff              /* Buffer to send/receive control data */
)
{
    switch (pdrv) {
        case DEV_FLASH:
            switch (cmd) {
                case GET_SECTOR_COUNT:
                    // 扇区数量 2048
                    *(DWORD*)buff = FLASH_SECTOR_COUNT;
                    return RES_OK;
                    // 扇区大小 4096
                case GET_SECTOR_SIZE:
                    *(WORD*)buff = FLASH_SECTOR_SIZE;
                    return RES_OK;
                    // 同时擦除扇区个数
                case GET_BLOCK_SIZE:
                    *(DWORD*)buff = 1;
                    return RES_OK;
                default:
                    return RES_OK;
            }
        default:
            return RES_PARERR;
    }
}

测试程序

  • Flash 驱动见这篇文章 Flash SPI
  • 因为缓冲区数组较大,所以要适当修改 启动文件 的大小,以免溢出。
uint16_t fatfs_test(void){
    FATFS fs;                        // 文件系统对象
    FIL fp;                          // 文件对象
    FRESULT ret;                     // 返回值
    UINT fnum;                       // 存储写入/读取的字节数 
    BYTE workbuf[FF_MAX_SS];         // 格式化缓冲区
    BYTE readbuf[100];               // 读缓冲区
    BYTE writebuf[] = "你好,世界!";   // 写缓冲区
    TCHAR* dev      = "0:";          // DEV_FLASH设备号
    TCHAR* fname    = "0:hello.txt"; // 文件名

    MKFS_PARM opt;             // 格式化参数               
    opt.fmt     = FM_FAT;      // 文件系统类型
    opt.n_fat   = 0;           // 0表示默认值
    opt.align   = 0;           //
    opt.n_root  = 0;           //
    opt.au_size = FF_MAX_SS;   // 簇大小    

    // 挂载设备
    ret = f_mount(&fs, dev, 1);
    switch(ret){
        case FR_OK:
            printf("挂载成功\r\n");
            break;
        case FR_NO_FILESYSTEM:
            ret = f_mkfs("0:", &opt, workbuf, FF_MAX_SS);
            if(!ret){
                printf("格式化成功\r\n");
            }else{
                printf("格式化失败(%d)\r\n", ret);
                return ret;
            }
            break;
        default:
            printf("挂载失败(%d)\r\n", ret);
            return ret;
    }

    // 测试打开文件写入
    ret = f_open(&fp, fname, FA_CREATE_ALWAYS | FA_WRITE );
    if(ret){
        printf("打开/创建文件失败(%d)\r\n", ret);
        return ret;
    }
    ret = f_write(&fp, writebuf, sizeof(writebuf), &fnum);
    if(ret){
        printf("文件写入失败(%d)\r\n", ret);
        return ret;
    }
    printf("文件写入字节数:%d \r\n", fnum);
    f_close(&fp);

    // 测试打开文件读取
    ret = f_open(&fp, fname, FA_OPEN_EXISTING | FA_READ);
    if(ret){
        printf("打开文件失败(%d)\r\n", ret);
        return ret;
    }
    ret = f_read(&fp, readbuf, sizeof(readbuf), &fnum);
    if(ret){
        printf("文件读取失败(%d)\r\n", ret);
        return ret;
    }
    printf("文件读取字节数:%d \r\n", fnum);
    printf("文件内容:\r\n%s\r\n", readbuf);
    f_close(&fp);

    // 卸载设备
    f_mount(NULL, dev, 1);
    return 0;
}