让QSPI FLASH(W25Q64)支持Fatfs文件系统

简介: 让QSPI FLASH(W25Q64)支持Fatfs文件系统

想着小熊派板子上带了一个QSPI,有8MB的存储空间,那可不能浪费了呀!之前写的那些开源项目的图片资源其实放在这上面的,如何实现呢?方法如下:


  • 使用SD卡将文件拷贝到QSPI FLASH(采用fatfs文件系统)
  • 写一个QSPI FLASH MDK下载算法,直接将图片数据放在主程序中


接下来进入正文:


小熊派上自带了一个QSPI接口的8M大小的SPI_FLASH,如下图所示:

640.png

640.png

小熊派官方也提供了驱动编写的视频教程以及代码编写例程,关于怎么实现的,这里就不多说了,如果想详细了解原理,可以看看世伟兄以及小熊派之前写的文章:


STM32Cube-18 | 使用QSPI读写SPI Flash(W25Q64)


单片机基础 —— 使用QSPI读写SPI Flash(W25Q64)


今天我们主要来讲解下Fatfs系统功能的配置,在进入正题之前,我已经按上面的教程将QSPI Flash正常驱动起来了,接下来进入主题,如下图所示:

640.png

中间件的地方选择fatfs,然后再Mode处选择User-defined,因为这个不是官方默认支持的,需要用户自己去实现Fatfs关于底层的驱动接口。

1、功能参数配置

其中,关于功能参数的配置,主要是用到了才去配置,不用到的选项默认就行了,这部分请参考ST官网有关STM32cube Fatfs的应用开发文档,如下所示:

640.png

640.png

2、几个重要参数配置说明

CODE PAGE这个选项主要是提供编码格式的支持,根据个人需求配置,这里配置为简体中文:


640.png

USE_LFN这个选项主要是为了支持长文件名,并且当需要支持这个功能的时候需要提供缓存区存放,fatfs提供了BSS、STACK、HEAP三种方式。

640.png

根据个人需求选择存放在STACK中,因为存放在BSS上,则是带有静态工作缓冲区的LFN,不能进行动态分配,而存放在HEP上,则需要重写实现fatfs提供的ff_memalloc和ff_memfree函数,所以一般情况下就把它放在栈区即可。

MAX_SS这个选项配置为4096,为什么要配置为4096呢?请看W25Q64的手册描述:

640.png

640.png

如上,W25Q64这款芯片的最小擦除单位是4KB,也就是4096字节,为了提高擦写效率,一般情况下就直接写4096。

其余的参数用到的时候再去做进一步的配置,均系统默认即可。

640.png

由于对长文件名做了支持,缓存区是在栈区的,所以把堆栈加大一些,自己喜欢就好,只要不溢出就行,根据个人习惯随便填了两个参数,然后生成代码工程。

3、Fatfs驱动QSPI接口实现

对于fatfs,ST官方多封装了一层抽象接口给用户进行填写函数,这个文件是:user_diskio.c,主要提供了如下给用户编写的接口:

Diskio_drvTypeDef  USER_Driver =
{
  USER_initialize,  //初始化驱动盘
  USER_status,      //获取硬盘状态函数
  USER_read,        //读磁盘
#if  _USE_WRITE  
  USER_write,       //写磁盘
#endif  /* _USE_WRITE == 1 */
#if  _USE_IOCTL == 1
  USER_ioctl,       //I/O操作
#endif /* _USE_IOCTL == 1 */
};

以上这些函数直接操作的就是以下fatfs原生的接口:

640.png

接下来我们需要依次实现它们:


初始化磁盘实现:

DSTATUS USER_initialize (
 BYTE pdrv           /* Physical drive nmuber to identify the drive */
)
{
  /* USER CODE BEGIN INIT */
  uint32_t id ;
    id = hal_spi_flash_get_id();
  if(0xEF4017 == id)
  {
   printf("读取ID:0x%x\n",id);
   return RES_OK;
  }
  else
   return RES_ERROR ;
  /* USER CODE END INIT */
}

磁盘状态函数实现(可以留空):

/**
  * @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 */
    return RES_OK;
  /* USER CODE END STATUS */
}

读磁盘函数实现:

/**
  * @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 */
    // 以4K字节为单位
    hal_spi_flash_read(buff, count << 12, sector << 12);
    return RES_OK;
  /* USER CODE END READ */
}

写磁盘函数实现:


SPI_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 */
    /* USER CODE HERE */
    uint32_t write_addr;
    write_addr = sector << 12;    // 以4K字节为单位
  hal_spi_flash_erase_write((uint8_t *)buff, count << 12, write_addr);
    return RES_OK;
  /* USER CODE END WRITE */
}
#endif /* _USE_WRITE == 1 */

磁盘命令操作实现:

/**
  * @brief  I/O control operation
  * @param  pdrv: Physical drive number (0..)
  * @param  cmd: Control code
  * @param  *buff: Buffer to send/receive control data
  * @retval DRESULT: Operation result
  */
#if _USE_IOCTL == 1
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 */
    switch (cmd)
    {
        case GET_SECTOR_COUNT:
            *(DWORD * )buff = 2048;  // 总的扇区数
            break;
        case GET_SECTOR_SIZE :
            *(WORD * )buff = 4096;  // 定义一个扇区大小为4K
            break;
        case GET_BLOCK_SIZE :
            *(DWORD * )buff = 65536; // 定义一个块大小为64K
            break;
    }
  return RES_OK ;
  /* USER CODE END IOCTL */
}
#endif /* _USE_IOCTL == 1 */

4、编写测试QSPI FLASH fatfs的程序

测试案例如下:

int main(void)
{
    /* USER CODE BEGIN 1 */
    uint8_t res ;
    uint32_t Total = 0; //读取FLASH总容量
    uint32_t Free = 0; //读取FLASH剩余容量
    /* USER CODE END 1 */
    /* MCU Configuration--------------------------------------------------------*/
    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();
    /* USER CODE BEGIN Init */
    /* USER CODE END Init */
    /* Configure the system clock */
    SystemClock_Config();
    /* USER CODE BEGIN SysInit */
    /* USER CODE END SysInit */
    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_I2C1_Init();
    MX_USART1_UART_Init();
    MX_QUADSPI_Init();
    MX_FATFS_Init();
    /* USER CODE BEGIN 2 */
    Mount_Fatfs();
    f_GetTotal_Free((uint8_t*)"0:", &Total, &Free); //获取SD卡总容量和剩余容量
    printf("当前Fatfs总容量:%dKB==>%dMB 剩余容量:%dKB==>%dMB\n", Total, Total / 1024, Free, Free / 1024);
    /*----------------------- 文件系统测试:写测试 -----------------------------*/
    printf("\r\n****** 即将进行文件写入测试... ******\r\n");
    res = f_open(&USERFile, "0:BearPi.txt", FA_OPEN_ALWAYS | FA_WRITE);
    if(res == FR_OK)
    {
        printf("打开/创建BearPi.txt文件成功,向文件写入数据。\r\n");
        res = f_write(&USERFile, write_buf, strlen((const char *)write_buf), &count);
        if(res != FR_OK)
        {
            printf("f_write 发生错误,err = %d\r\n", res);
            printf("关闭打开的BearPi.txt文件\r\n");
            count = 0;
            f_close(&USERFile);
        }
        else
        {
            printf("文件写入成功,写入字节数据:%d\n", count);
            printf("向文件写入的数据为:\r\n%s\r\n", write_buf);
            printf("关闭打开的BearPi.txt文件\r\n");
            count = 0;
            f_close(&USERFile);
        }
    }
    else printf("打开/创建BearPi.txt文件失败,err = %d\r\n", res);
    /*------------------- 文件系统测试:读测试 ------------------------------------*/
    printf("****** 即将进行文件读取测试... ******\r\n");
    res = f_open(&USERFile, "0:BearPi.txt", FA_OPEN_EXISTING | FA_READ);
    if(res == FR_OK)
    {
        printf("打开BearPi.txt文件成功\r\n");
        res = f_read(&USERFile, read_buf, sizeof(read_buf), &count);
        if(res != FR_OK)
        {
            printf("f_read 发生错误,err = %d\r\n", res);
            printf("关闭打开的BearPi.txt文件\r\n");
            f_close(&USERFile);
        }
        else
        {
            printf("文件读取成功,读取字节数据:%d\n", count);
            printf("向文件读取的数据为:\r\n%s\r\n", read_buf);
            printf("关闭打开的BearPi.txt文件\r\n");
            f_close(&USERFile);
        }
    }
    else printf("打开BearPi.txt文件失败,err = %d\r\n", res);
    /*------------------- 不再使用文件系统,取消挂载文件系统 ------------------------------------*/
    printf("不再使用文件系统,取消挂载文件系统\r\n");
    res = f_mount(NULL, "0:", 1);
    if(res == FR_OK) printf("取消挂载文件系统成功\r\n");
    else    printf("取消挂载文件系统失败,err = %d\r\n", res);
    printf("文件系统测试结束\r\n");
    /* USER CODE END 2 */
    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
        /* USER CODE END WHILE */
        /* USER CODE BEGIN 3 */
    }
    /* USER CODE END 3 */
}

运行结果:

640.png

获取完整demo:

码云仓库:https://gitee.com/morixinguan/bear-pi/tree/master/19.QSPI_Fatfs

640.png

获取方法:

git clone https://gitee.com/morixinguan/bear-pi.git

即可获取本次实验工程全部代码。

往期精彩

基于小熊派气体传感器MQ-2综合实践


RTOS支持STemWin(以RT-Thread为例)


关于MCU产品开发参数存储的几种方案(开源项目持续收集整理中)


整理了很久之前在码云/Github/CSDN上收藏的嵌入式产品级项目分享开源

目录
相关文章
|
3月前
|
存储 缓存 监控
【嵌入式SD NAND】基于FATFS/Littlefs文件系统的日志框架实现
综上所述,构建一个基于FATFS/Littlefs文件系统的日志框架需要对文件系统的操作有深入理解,并以此为基础设计一套完整的日志处理机制。这样的框架不仅能够确保日志数据的完整性和系统的鲁棒性,同时还能够满足嵌入式系统对于性能和资源使用的严格要求。
133 4
|
6月前
|
存储 Linux 内存技术
嵌入式linux下获取flash分区大小
嵌入式linux下获取flash分区大小
72 0
|
小程序 物联网 内存技术
QSPI FLASH与SD卡同时支持fatfs文件系统
QSPI FLASH与SD卡同时支持fatfs文件系统
135 0
|
存储 开发工具 芯片
ZYNQ-QSPI Flash读写操作(一)
ZYNQ-QSPI Flash读写操作
1404 0
ZYNQ-QSPI Flash读写操作(一)
|
存储 IDE 编译器
基于STM32完成FATFS文件系统移植与运用--这是完全免费开源的FAT文件系统
基于STM32完成FATFS文件系统移植与运用--这是完全免费开源的FAT文件系统
586 0
基于STM32完成FATFS文件系统移植与运用--这是完全免费开源的FAT文件系统
|
存储 Linux 芯片
如何编写linux下nand flash驱动-1
1.       硬件特性: 【Flash的硬件实现机制】 Flash全名叫做Flash Memory,属于非易失性存储设备(Non-volatile Memory Device),与此相对应的是易失性存储设备(Volatile Memory Device)。
963 0
|
存储 Linux 芯片
如何编写linux下nand flash驱动-3
【读(read)操作过程详解】 以最简单的read操作为例,解释如何理解时序图,以及将时序图 中的要求,转化为代码。   解释时序图之前,让我们先要搞清楚,我们要做的事情:那就是,要从nand flash的某个页里面,读取我们要的数据。
1058 0
|
存储 算法 Linux
如何编写linux下nand flash驱动-4
2.       软件方面 如果想要在Linux下编写Nand Flash驱动,那么就先要搞清楚Linux下,关于此部分的整个框架。弄明白,系统是如何管理你的nand flash的,以及,系统都帮你做了那些准备工作,而剩下的,驱动底层实现部分,你要去实现哪些功能,才能使得硬件正常工作起来。
945 0