所需工具

  • CUBEMX5.6
  • STM32F103ZET6开发板(正点原子战舰)
  • LINK及其他线束

1、CUBEMX设置

  • 设置RCC
    切记选择第三个,否则串口无法使用。
  • 设置串口
  • 设置SPI_FLASH
  • 设置FATFS
  • CODE_PAGE指的是编码格式,可以使用中文编码,只是占用空间会比较大
  • USE_LFN提供了四个选项,BSS,STACK,HEAP,或者禁用。
    该选项主要是控制是否使用中长文件名,如果不是用的话可以禁用掉。
    选用BSS的话则将缓冲放入BSS段内的静态缓冲区,大小在编译时间已经固定,不能扩容。
    STACK和HEAP就不说了,一般选择STACK.
  • 缓冲区内大小为(FF_MAX_LFN + 1)* 2字节,另外在启用exFAT时额外占用(FF_MAX_LFN + 44)/15 * 32个字节数。参考:FatFs Module Application Note
  • 短文件名一般是8.3格式,即文件名最大8个字符,后缀名最多3个字符。 SFN允许的字符是ASCII字母数字,一些ASCII标记( $%’-_ @〜`!(){} ^#& )和非ASCII字符(\ x80-\ xFF)。
  • MAX_SS是最大扇区大小,W25Q128的扇区大小是4KB,所以选择了4096,这里引用别人博客的一段话

为什么最大扇区大小是4096Byte?一般别人都是512Byte? 其实这个是根据你自己使用的存储芯片和驱动相关的。因为我使用的W25Q256这款芯片是最小擦除单位是4096。不使用512byte是因为效率大大降低但是优点是空间利用率会大大提高。比如你文件系统最大分区是512,但是芯片最小擦除单位是4096,那么你在驱动就要实现先用缓存区把整个扇区4096byte全部读出来,然后判读其中写入512byte中有没有擦除过(即全0xFF),没有的话先擦除,在把数据写入缓存区最后写入芯片。所以步骤繁琐效率低,但是优点就是存储空间的利用率会大大提高,避免太多浪费。 我很懒,所以选择使用4096。参考: STM32CbueMX之Fatfs移植到SPI_Flash

2、移植修改文件

  1. 添加W25Q128驱动文件,详情在这
  2. 修改user_diskio.c文件,修改完如下所示
/* USER CODE BEGIN Header */
/********************************************************************************* @file    user_diskio.c* @brief   This file includes a diskio driver skeleton to be completed by the user.******************************************************************************* @attention** <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.* All rights reserved.</center></h2>** This software component is licensed by ST under Ultimate Liberty license* SLA0044, the "License"; You may not use this file except in compliance with* the License. You may obtain a copy of the License at:*                             www.st.com/SLA0044********************************************************************************//* USER CODE END Header */#ifdef USE_OBSOLETE_USER_CODE_SECTION_0
/* * Warning: the user section 0 is no more in use (starting from CubeMx version 4.16.0)* To be suppressed in the future. * Kept to ensure backward compatibility with previous CubeMx versions when * migrating projects. * User code previously added there should be copied in the new user sections before * the section contents can be deleted.*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
#endif/* USER CODE BEGIN DECL *//* Includes ------------------------------------------------------------------*/
#include <string.h>
#include "ff_gen_drv.h"
#include "w25qxx.h" /* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*//* Private variables ---------------------------------------------------------*/
/* Disk status */
static volatile DSTATUS Stat = STA_NOINIT;
#define FLASH_SECTOR_SIZE   (4096/FLASH_BLOCK_SIZE)
#define  FLASH_SECTOR_COUNT    ((12*1024*1024)/FLASH_SECTOR_SIZE)//12的单位是MB
#define FLASH_BLOCK_SIZE        (8)
/*
FLASH_SECTOR_SIZE    设置为512或者4096都可以使用,但是在512的时候,文件系统认为FLASH中的扇区大小是512字节,在使用的时候,效率远没有4096高
FLASH_SECTOR_COUNT    挂载的总容量,根据自己需求
FLASH_BLOCK_SIZE      每次擦除的块的个数,原子哥写的是8,因为FLASH的最小擦除单位为扇区(4KB),原子哥的FLASH_SECTOR_SIZE 为512,\
所以是512*8,我们用1即可。
*/
/* USER CODE END DECL *//* Private function prototypes -----------------------------------------------*/
DSTATUS USER_initialize (BYTE pdrv);
DSTATUS USER_status (BYTE pdrv);
DRESULT USER_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
#if _USE_WRITE == 1DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff);
#endif /* _USE_IOCTL == 1 */Diskio_drvTypeDef  USER_Driver =
{USER_initialize,USER_status,USER_read,
#if  _USE_WRITEUSER_write,
#endif  /* _USE_WRITE == 1 */
#if  _USE_IOCTL == 1USER_ioctl,
#endif /* _USE_IOCTL == 1 */
};
/* Private functions ---------------------------------------------------------*//*** @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;if(W25Q128 == BSP_W25QXX_ReadID()){/* 设备ID读取结果正确 */Stat &= ~STA_NOINIT;}else{/* 设备ID读取结果错误 */Stat = STA_NOINIT;;}BSP_W25QXX_Init();return Stat;/* 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 */if(pdrv != 0)Stat = STA_NOINIT;elseStat &= ~STA_NOINIT;return Stat;/* 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 */DRESULT res = RES_ERROR;if(!count)return RES_PARERR; //count不能等于0,否则返回参数错误for(; count > 0; count--){BSP_W25QXX_Read(buff, sector * FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE);sector++;buff += FLASH_SECTOR_SIZE;res = RES_OK;}//处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值return res;/* USER CODE END READ */
}/*** @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 */DRESULT res = RES_ERROR;if(!count)return res; //count不能等于0,否则返回参数错误for(; count > 0; count--){BSP_W25QXX_Write((uint8_t*)buff, sector * FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE);sector++;buff += FLASH_SECTOR_SIZE;res = RES_OK;}//处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值return res;/* USER CODE HERE *//* 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 */DRESULT res = RES_ERROR;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;}   return res;/* USER CODE END IOCTL */
}
#endif /* _USE_IOCTL == 1 *//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

FATFS的最小操作单位为簇,在使用flash的时候,可以简单的理解为簇就是扇区,下方的三个宏其实就是FATFS对与总容量和最小操作单位的读取操作。

命令 返回值 描述
GET_BLOCK_SIZE FLASH_BLOCK_SIZE 返回以扇区为单位的存储阵列的擦除块大小赋给 Buffer 指向的 DWORD 变量。当擦除块大小未知或是磁盘设备时,返回 1 。只在 f_mkfs 函数中,使用了该命令。告诉上层每次擦除的时候最小的操作单位是多少,由于FLASH的最小擦除单位为4096,所以 F L A S H B L O C K S I Z E = 4096 F L A S H S E C T O R S I Z E FLASH_~BLOCK_~SIZE = \dfrac{4096}{FLASH_~SECTOR_~SIZE} FLASH ​BLOCK ​SIZE=FLASH ​SECTOR ​SIZE4096​
GET_SECTOR_SIZE FLASH_SECTOR_SIZE 只有当FF_MAX_SS > FF_MIN_SS时才需要这个命令。通俗点来说,告诉FATFS系统每个扇区有多少大容量
GET_SECTOR_COUNT FLASH_SECTOR_COUNT 挂载的总容量大小 ,这里划分了W25Q128的前12MB空间, F L A S H S E C T O R C O U N T = x ∗ 1024 ∗ 1024 F L A S H S E C T O R S I Z E FLASH_~SECTOR_~COUNT = \dfrac{x* 1024*1024}{FLASH_~SECTOR_~SIZE} FLASH ​SECTOR ​COUNT=FLASH ​SECTOR ​SIZEx∗1024∗1024​
  • 在main.c文件添加如下内容
#include <stdio.h>
#include <string.h>
FATFS spi_fs;
FIL fil;
unsigned int count = 0;
unsigned char work[4096] = {0};
unsigned char read_buf[50] = {0};
unsigned char write_buf[50] = "hello sudaroot\r\n";
void test()
{FRESULT retSD;printf("\r\n ****** FatFs Example ******\r\n\r\n");/*##-1- Register the file system object to the FatFs module ##############*/retSD = f_mount(&spi_fs, "0:", 1);if(retSD != FR_OK){if(retSD == FR_NO_FILESYSTEM){printf("f_mount 没有文件系统,开始格式化spi-flash\r\n");retSD = f_mkfs("0:", 0, 0);if(retSD != FR_OK){printf("f_mkfs 格式化失败,err = %d\r\n", retSD);while(1);}else{printf("格式化成功,开始重新挂载spi-flash\r\n");retSD = f_mount(&spi_fs, "0:", 1);if(retSD != FR_OK){printf("f_mount 发生错误,err = %d\r\n", retSD);}else printf("spi-flash文件系统挂载成功\r\n");}}else{printf("f_mount 发生其他错误,err = %d\r\n", retSD);while(1);}}else printf("spi-flash文件系统挂载成功\r\n");/*----------------------- 文件系统测试:写测试 -----------------------------*/printf("\r\n****** 即将进行文件写入测试... ******\r\n");retSD = f_open(&fil, "0:sudaroot.txt", FA_OPEN_ALWAYS | FA_WRITE);if(retSD == FR_OK){printf("打开/创建sudaroot.txt文件成功,向文件写入数据。\r\n");retSD = f_write(&fil, write_buf, strlen((const char *)write_buf), &count);if(retSD != FR_OK){printf("f_write 发生错误,err = %d\r\n", retSD);printf("关闭打开的sudaroot.txt文件\r\n");count = 0;f_close(&fil);}else{printf("文件写入成功,写入字节数据:%d\n", count);printf("向文件写入的数据为:\r\n%s\r\n", write_buf);printf("关闭打开的sudaroot.txt文件\r\n");count = 0;f_close(&fil);}}else printf("打开/创建sudaroot.txt文件失败,err = %d\r\n", retSD);/*------------------- 文件系统测试:读测试 ------------------------------------*/printf("****** 即将进行文件读取测试... ******\r\n");retSD = f_open(&fil, "0:sudaroot.txt", FA_OPEN_EXISTING | FA_READ);if(retSD == FR_OK){printf("打开sudaroot.txt文件成功\r\n");retSD = f_read(&fil, read_buf, sizeof(read_buf), &count);if(retSD != FR_OK){printf("f_read 发生错误,err = %d\r\n", retSD);printf("关闭打开的sudaroot.txt文件\r\n");f_close(&fil);}else{printf("文件读取成功,读取字节数据:%d\n", count);printf("向文件读取的数据为:\r\n%s\r\n", read_buf);printf("关闭打开的sudaroot.txt文件\r\n");f_close(&fil);}}else printf("打开sudaroot.txt文件失败,err = %d\r\n", retSD);
}
  • 在main函数里面添加test()。

3、结果

需要注意的是,由于配置阶段没有打开USE_LFN,所以如果文件名长度超过8位,或者后缀名超过3位则无法进行读写操作。

参考

  • FatFs - Generic FAT Filesystem Module

3.1、CUBEMX使用FATFS读写SPI_FLASH相关推荐

  1. STM32CubeMX+SPI+FATFS读写SD卡

    一.软件硬件说明 软件:STM32CubeMX V6.6.1 /KEIL5 V5.29 硬件:正点原子mini开发板,SD卡,通过SPI方式驱动SD卡,用的是SPI1接口 以上内容来源于正点原子min ...

  2. stm32 Fatfs 读写SD卡

    源:stm32 Fatfs 读写SD卡 转载于:https://www.cnblogs.com/LittleTiger/p/4864052.html

  3. FatFs读写SD卡出现FR_NO_FILESYSTEM解决方法.

    原文地址:http://www.devlabs.cn/?p=226 18 一月 2014 3 起因 去年做了个GPS路径记录器, 将路径息记录于TF卡上, 上了FatFs系统. 刚开始那会虽然偶尔罢工 ...

  4. FatFs读写SD卡出现FR_NO_FILESYSTEM解决方法.(写的好)

    来源:http://www.devlabs.cn/?p=226 起因 去年做了个GPS路径记录器, 将路径息记录于TF卡上, 上了FatFs系统. 刚开始那会虽然偶尔罢工, 但好歹能工作. 后来没时间 ...

  5. 从零实现 USB_SLAVE读卡器 USB_MSC+FATFS+SD/SPI_FLASH/NANDFLASH

    欢迎大家留言交流~ 要实现USB读卡器,其核心就是F429作为从设备用USB_MSC与主机通讯,利用FATFS从SD卡.SPI_FLASH.NANDFLASH中读写数据. 什么是OTG?OTG是On- ...

  6. cubemx使用FATFS实现对U盘读取 --- STM32F407

    说明:本文主要使用 USB的全速模式和FATFS实现对U盘的存储和读取.其中单片机作为主机,U盘为从机,需要为U盘进行供电. 1:cubemx配置: (1) 如果想要支持给USB供电,点击" ...

  7. 基于STM32F407平台实现FATFS读写大容量(128G)SD卡的心得

    本人是沈阳大学的一名小白,之前,无论是STM32,还是FATFS,都是小白一个,甚至不理解那是什么东西,但是据说这种技术目前为止好像是读写大容量卡挺费劲,只能64G,就到头了,但是最近接到一个任务就是 ...

  8. HAL库U盘升级 STM32F407 CUBEMX:FATFS + USB_HOST + USB_OTG_FS

    一.测试平台: MCU:STM32F407VET6 固件库:CUBEMX IDE:MDK 二.实验目的: 将U盘里面的bin文件插入要升级的设备,通过BootLoader来进行升级 在这是用板载的LE ...

  9. fatfs读写csv文件

    参考例程 u8 text_buffer[512] = "hospital,bednumber,name,age,sex,\n"; int tf_add_write_file(cha ...

最新文章

  1. 脚本控制向Android模拟拨打电话,发送短信,定位设置功能
  2. linux中各种文件的颜色表示是什么意思?
  3. RDA PQ工具使用 (Adi Analysis)
  4. 程序员公司选择:创业公司、中等规模公司、大公司
  5. 微服务有麻烦吗? Lagom在这里为您提供帮助。 尝试一下!
  6. 分布式设计模式中的Quorum思想
  7. ECS入门之Hello World
  8. [十]JavaIO之FilterInputStream FilterOutputStream
  9. SpringCloudAlibaba--Seata简单案例
  10. 【云周刊】第141期:阿里正式发布《Java开发手册》终极版!绝对珍藏!
  11. GPS之Ublox方案设计
  12. 安装fitz报错_Kylo单机安装详解
  13. Eplan执行翻译操作详细教程
  14. 解决jsp页面引入百度编辑器,出现xss漏洞
  15. OSChina 周一乱弹 ——连自己老婆的双胞胎妹妹都不放过
  16. 中国知网论文查重算法和修改攻略
  17. 数据结构-图(图的定义、分类、基本术语和存储结构)
  18. JVM——深入理解类加载器
  19. Mybatis框架创建逆向工程步骤
  20. sql server存储过程练习

热门文章

  1. Android中图案锁的实现
  2. soc低功耗电路设计应用-Zynq MPSoC
  3. DHCP 和 DHCP防护 配置
  4. Android应用开发记录-Android歌词秀(5)完善一下,像一个产品了
  5. linux服务器连接k8s平台配置
  6. LTE-TDD measurement gaps位置计算--Python代码实现
  7. valet-windows
  8. 基于 RICS-V 架构的单周期处理器设计(含所有格式指令)—— 逻辑部件概述
  9. 鸿蒙是RISC,华为转投第三大CPU架构RISC-V?首款鸿蒙开发板曝光
  10. Window bat rar压缩脚本