RYMCU

STM32篇——FATFS 文件系统

STM32 HAL 库 FATFS 文件系统 W25Q64 FLASH

1. 前期准备

安装好 STM32CubeMX

安装好 Clion

上一章已经掌握了如何读写 FLASH,如果一直采用直接操作 FLASH 的方法会非常的繁琐,需要自行记住哪些地方用了,哪些没用以及哪些地方具体放了哪些内容,显然不适合管理。这章我们引进文件管理系统 FATFS,由她来直接操作 FLASH,我们只需要调用她提供的方法实现文件管理即可,解放生产力。

2. 创建项目

在上一章《STM32 篇——SPI 通信》基础上新增配置如下:

重新生成代码,项目将自动添加 FATFS 相关代码。

3. 编辑代码

重新生成代码之后,别忘记在 main.cSPI 对象声明前添加 extern 关键字,如下所示:

extern SPI_HandleTypeDef hspi1;

FATFS 添加 FLASH 驱动,让她可以直接操作 FLASH。我们只需更改 user_diskio.c 文件中相关函数,实现对 FLASH 的初始化、擦除、读、写等操作。包括:USER_ioctl()USER_initialize()USER_status()USER_read()USER_write(),下面逐一介绍:

  1. 添加 FLASH 头文件
//user_diskio.c文件中添加
#include "bsp_spi_flash.h"//添加FLASH驱动
  1. USER_ioctl() 修改

FATFS 调用该函数获取 FLASH 的器件信息,函数中添加扇区大小、扇区数量信息等,代码如下:

DRESULT USER_ioctl (
    BYTE pdrv,      /* Physical drive nmuber (0..) */
    BYTE cmd,       /* Control code */
    void *buff      /* Buffer to send/receive control data */
)
{
  /* USER CODE BEGIN IOCTL */
    DRESULT res = RES_ERROR;
    //获取FLASH参数
    if(pdrv==0) {
        switch (cmd) {
            case CTRL_SYNC:
                res = RES_OK;
                break;
            case GET_SECTOR_SIZE:
                *(DWORD *) buff = 4096;//W25Q64每扇区为4096Byte
                res = RES_OK;
                break;
            case GET_SECTOR_COUNT:
                *(DWORD *) buff = 2048;//W25Q64容量为8MB,共计2048个扇区
                res = RES_OK;
                break;
            case GET_BLOCK_SIZE:
                *(DWORD *) buff = 1;//单次读写操作擦除扇区个数
                res = RES_OK;
                break;
            default:
                res = RES_PARERR;
        }
    }
    else
    {
        res = RES_PARERR;
    }
    return res;
  /* USER CODE END IOCTL */
}
  1. USER_initialize() 修改

FATFS 调用该函数实现 FLASH 的初始化,我们在 main.c 中已经完成了,只需要返回 OK 即可,代码如下:

/**
  * @brief  Initializes a Drive
  * @param  pdrv: Physical drive number (0..)
  * @retval DSTATUS: Operation status
  */
DSTATUS USER_initialize (
    BYTE pdrv           /* Physical drive nmuber to identify the drive */
)
{
  /* USER CODE BEGIN INIT */
    Stat = STA_NOINIT;
    //添加代码
    switch (pdrv)
    {
        case 1://SD
            Stat = RES_PARERR;
            break;
        case 0://Flash,工程中使用驱动器0
            //main函数中自动生成了W25Q64的初始化代码,直接返回成功。
            Stat = RES_OK;
            break;
        default:
            Stat = RES_PARERR;
    }
    return Stat;
  /* USER CODE END INIT */
}
  1. USER_status() 修改

FATFS 调用该函数获取 FLASH 状态,已经准备好了,同样返回 OK 即可,代码如下:

/**
  * @brief  Gets Disk Status
  * @param  pdrv: Physical drive number (0..)
  * @retval DSTATUS: Operation status
  */
DSTATUS USER_status (
    BYTE pdrv       /* Physical drive number to identify the drive */
)
{
  /* USER CODE BEGIN STATUS */
    Stat = STA_NOINIT;
    //返回启动器的状态,工程中使用了驱动器0
    if(pdrv==0) Stat = RES_OK;//main函数中自动生成了FLASH初始化代码,直接返回成功。
    return Stat;
  /* USER CODE END STATUS */
}
  1. USER_read() 修改

FATFS 调用该函数读取 FLASH 数据,添加读取函数即可,代码如下:

/**
  * @brief  Reads Sector(s)
  * @param  pdrv: Physical drive number (0..)
  * @param  *buff: Data buffer to store read data
  * @param  sector: Sector address (LBA)
  * @param  count: Number of sectors to read (1..128)
  * @retval DRESULT: Operation result
  */
DRESULT USER_read (
    BYTE pdrv,      /* Physical drive nmuber to identify the drive */
    BYTE *buff,     /* Data buffer to store read data */
    DWORD sector,   /* Sector address in LBA */
    UINT count      /* Number of sectors to read */
)
{
  /* USER CODE BEGIN READ */
  //添加FLASH读函数
  if(pdrv==0)
  {
      //调用读数据函数,值得注意的是sector指的是0-2047,即哪一个扇区,与W25Q64实际扇区地址不是一个概念
      //一个扇区对应4096个字节,因此,输入地址为sector*4096,count同理。
      SPI_FLASH_BufferRead((u8 *)buff,sector*4096,count*4096);
      return RES_OK;
  }
  else
  {
      return RES_PARERR;
  }
  /* USER CODE END READ */
}
  1. USER_write() 修改

FATFS 调用该函数实现对 FLASH 写操作,添加写函数即可,代码如下:

/**
  * @brief  Writes Sector(s)
  * @param  pdrv: Physical drive number (0..)
  * @param  *buff: Data to be written
  * @param  sector: Sector address (LBA)
  * @param  count: Number of sectors to write (1..128)
  * @retval DRESULT: Operation result
  */
#if _USE_WRITE == 1
DRESULT USER_write (
    BYTE pdrv,          /* Physical drive nmuber to identify the drive */
    const BYTE *buff,   /* Data to be written */
    DWORD sector,       /* Sector address in LBA */
    UINT count          /* Number of sectors to write */
)
{
  /* USER CODE BEGIN WRITE */
  if(pdrv==0)
  {
      //添加写函数
      //FLASH写操作前需要进行扇区擦除,需要擦除count个扇区,在USER_ioctl函数中
      //设置了GET_BLOCK_SIZE为1,即单次读写操作擦除扇区个数为1,所以count值
      //始终为1,因此只需要调用一次扇区擦除即可。
      SPI_FLASH_SectorErase(sector*4096);
      SPI_FLASH_BufferWrite((u8 *)buff,sector*4096,count*4096);
      /* USER CODE HERE */
      return RES_OK;
  }
  else
  {
      return RES_PARERR;
  }
  /* USER CODE END WRITE */
}
#endif /* _USE_WRITE == 1 */
  1. 准备就绪

至此,已经完成了 FATFS 所有驱动的修改,添加上层代码便可实现文件管理了。

4. FATFS 文件系统示例

main.c 中添加代码实现文件的创建、写入、读取功能!实现功能为:在 FLASH 上创建 RY.txt 文件,并写入、读出数据。mian.c 文件中添加代码如下:

//FATFS测试
void FATFS_FLASH_Test(void)
{
    FATFS fs;//文件系统对象
    static FIL fnew;//文件对象
    BYTE FATFS_Wr_Buff[128]  ="hello,嵌入式知识开源社区RYMCU欢迎你!rn";// 写缓冲区
    BYTE FATFS_Rd_Buff[128]  ={0};// 读缓冲区
    UINT fnum;//成功读写数量
    FRESULT res;//返回值

    printf("rnrn------------------FLASH FATFS文件系统测试------------------rnrn");
    res = f_mount(&fs,"0:",1);
    if(res == FR_NO_FILESYSTEM)
    {
        printf("FLASH上没有建立文件系统,开始格式化!rn");
        res = f_mkfs("0:",0,0);//格式化
        if(res == FR_OK)
        {
            printf("FLASH文件系统格式化成功!rn");
            //格式化成功后先取消,再重新挂载!
            res = f_mount(NULL,"0:",1);
            printf("取消挂载成功!: %drn",res);
            res = f_mount(&fs,"0:",1);
            printf("重新挂载成功!: %drn",res);
        }
        else
        {
            printf("FLASH文件系统格式化失败!rn");
        }
    }
    printf("rnrn-------------------FATFS系统测试:写文件-------------------rn");
    //打开文件,文件不存在则创建并打开
    res = f_open(&fnew,"RY.txt",FA_CREATE_ALWAYS | FA_WRITE);
    if(res == FR_OK) printf("打开或创建RY.txt成功!rn");
    else             printf("打开或创建RY.txt失败!rn");
    //写测试
    res = f_write(&fnew,FATFS_Wr_Buff, sizeof(FATFS_Wr_Buff),&fnum);
    if(res == FR_OK) printf("往RY.txt写入数据:rn%s",FATFS_Wr_Buff);
    else             printf("往RY.txt写入数据失败,code: %d!rn",res);
    //完成写操作后,关闭文件
    f_close(&fnew);
    printf("rn-------------------FATFS系统测试:读文件-------------------rnrn");
    //打开文件,读方式打开已创建的文件
    res = f_open(&fnew,"RY.txt",FA_OPEN_EXISTING | FA_READ);
    if(res != FR_OK){printf("打开文件失败!rn");return;}
    //读测试
    res = f_read(&fnew,FATFS_Rd_Buff, sizeof(FATFS_Rd_Buff),&fnum);
    if(res != FR_OK){printf("读取文件失败!rn");return;}
    printf("读取文件数据:rn%srn",FATFS_Rd_Buff);
    f_close(&fnew);//读取完毕,关闭文件。
}

5. 编译下载

将程序编译下载至开发板,并将开发板连接至 PC,打开串口调试助手 RYCOM,并设置为:115200+8+N+1,接收结果如下。

6. 小结

本章学习了通过 FATFS 系统,实现了简单的文件操作。

后发布评论