STM32CbueMX之Fatfs移植到SPI_Flash

2024-02-10 14:48

本文主要是介绍STM32CbueMX之Fatfs移植到SPI_Flash,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

我使用的是SPI_Flash芯片是W25Q256,兼容MX25L256。驱动程序在STM32CbueMX之W25Q256_sudaroot的博客-CSDN博客_w25q256驱动

STM32CubeMX配置图:

解释一下选项:

1、支持中文简体编码格式就没什么好说的。

2、缓存工作区为什么放在栈?其实fatfs提供了三个选项:BSS,STACK , HEAP,根据个人情况选一个。

      在BSS上启用带有静态工作缓冲区的LFN,不能动态分配。

      如果选择了HEAP(堆)且自己有属于自己的malloc就去重写ff_memalloc  ff_memfree函数。如果是库的malloc就不需要。

      一般都选择使用STACK(栈),能动态分配。

     当使用堆栈作为工作缓冲区时,请注意堆栈溢出。

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

4、VOLUMES(逻辑驱动器):如果你是有多块储存芯片,每块存储芯片都是一个逻辑驱动器可以根据自己选择,我这个只有一块外挂的SPI flash存储芯片,故文章多块逻辑驱动器不做讨论(其实都过程差不多)。

我缓存区使用的STACK,所以我给了0x400(1024byte)。随便给的,不溢出就行了。

其实这个可以算一下,一般我们stack给0x200(512byte),带了文件系统的话,因为是启用了_USE_LFN对长文件名的支持,所以需要会大一下,我们最大支持_MAX_LFN个字节的文件名,我选的是支持_MAX_LFN = 255;

工作缓冲区在非exFAT时占用(_MAX_LFN + 1)* 2个字节;

工作缓冲区在启用exFAT时占用(_MAX_LFN + 1)* 2个字节和额外的608个字节。

计算过程如下:

一般我们格式化都是FAT12/16/32 所需stack = 0x200 +  (_MAX_LFN + 1) * 2  = 0x200 + (255  + 1) * 2 = 1024byte = 0x400

启用exFAT时最大所需stack = 0x200 +  (_MAX_LFN + 1) * 2 + 608 = 0x200 + (255  + 1) * 2 + 608 = 1632byte

生成工程。打开user_diskio.c

DSTATUS USER_initialize (BYTE pdrv)是初始化驱动盘。

因为我只有0号盘,spi_flash初始化就是初始化spi协议,所以简单读取一下ID号,正确就行了。

/*** @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 */uint8_t id[2] = {0};if(pdrv != 0)   return STA_NOINIT;BSP_W25Q256_Init();BSP_W25Qx_Read_ID(id);if(id[0] == 0xEF && id[1] == 0x18) Stat = !STA_NOINIT;else   Stat = STA_NOINIT;return Stat;/* USER CODE END INIT */
}

USER_status(BYTE pdrv)获取硬盘状态函数(简单些)

/*** @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)   return 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 */if(pdrv != 0 || count == 0)   return RES_PARERR;if(BSP_W25Qx_Read(buff, sector * 4096, count * 4096) == W25Qx_OK)return RES_OK;else return RES_ERROR;/* 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 *//* USER CODE HERE */uint8_t i = 0;if(pdrv != 0 || count == 0)   return RES_PARERR;for(i = 0; i < count; i++){if(BSP_W25Qx_Erase_Sector((sector + i) * 4096) != W25Qx_OK)return RES_ERROR;if(BSP_W25Qx_Write((uint8_t*)buff + (i * 4096), (sector + i) * 4096, 4096) != W25Qx_OK)return RES_ERROR;}return RES_OK;/* USER CODE END WRITE */
}

这个USER_ioctl函数很重要。

/*** @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;if(pdrv != 0)   return RES_PARERR;switch(cmd){case CTRL_SYNC:res = RES_OK;break;case GET_SECTOR_COUNT:*(DWORD*)buff = 8192;res = RES_OK;break;case GET_SECTOR_SIZE:*(WORD*)buff = 4096;res = RES_OK;break;case GET_BLOCK_SIZE:*(DWORD*)buff = 1;res = RES_OK;break;default:res = RES_PARERR;break;}return res;/* USER CODE END IOCTL */
}
#endif /* _USE_IOCTL == 1 */

main.c

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "fatfs.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
#include <W25Qx.h>
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
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";
/* USER CODE END PM *//* Private variables ---------------------------------------------------------*/
SPI_HandleTypeDef hspi5;UART_HandleTypeDef huart1;/* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI5_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int fputc(int ch, FILE* FILE)
{HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, HAL_MAX_DELAY);return ch;
}
/* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 */uint32_t res = 0;/* 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_SPI5_Init();MX_USART1_UART_Init();MX_FATFS_Init();/* USER CODE BEGIN 2 */HAL_Delay(1000);printf("sduaroot fatfs test\r\n");printf("****** 擦除SPI_FLASH 扇区0 ******\r\n");BSP_W25Qx_Erase_Sector(0);HAL_Delay(1000);/*----------------------- 挂载测试 ---------------------------*/printf("****** 这是一个SPI FLASH 文件系统实验 ******\r\n");res = f_mount(&spi_fs, "0:", 1);if(res != FR_OK){if(res == FR_NO_FILESYSTEM){printf("f_mount 没有文件系统,开始格式化spi-flash\r\n");res = f_mkfs("0:", FM_ANY, 0, &work, 4096);if(res != FR_OK){printf("f_mkfs 格式化失败,err = %d\r\n", res);while(1);}else{printf("格式化成功,开始重新挂载spi-flash\r\n");res = f_mount(&spi_fs, "0:", 1);if(res != FR_OK){printf("f_mount 发生错误,err = %d\r\n", res);}else printf("spi-flash文件系统挂载成功\r\n");}}else{printf("f_mount 发生其他错误,err = %d\r\n", res);while(1);}}else printf("spi-flash文件系统挂载成功\r\n");/*----------------------- 文件系统测试:写测试 -----------------------------*/printf("\r\n****** 即将进行文件写入测试... ******\r\n");res = f_open(&fil, "0:sudaroot.txt", FA_OPEN_ALWAYS | FA_WRITE);if(res == FR_OK){printf("打开/创建sudaroot.txt文件成功,向文件写入数据。\r\n");res = f_write(&fil, write_buf, strlen((const char *)write_buf), &count);if(res != FR_OK){printf("f_write 发生错误,err = %d\r\n", res);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", res);/*------------------- 文件系统测试:读测试 ------------------------------------*/printf("****** 即将进行文件读取测试... ******\r\n");res = f_open(&fil, "0:sudaroot.txt", FA_OPEN_EXISTING | FA_READ);if(res == FR_OK){printf("打开sudaroot.txt文件成功\r\n");res = f_read(&fil, read_buf, sizeof(read_buf), &count);if(res != FR_OK){printf("f_read 发生错误,err = %d\r\n", res);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", 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 */HAL_Delay(500);HAL_GPIO_TogglePin(RUN_LED_GPIO_Port, RUN_LED_Pin);}/* USER CODE END 3 */
}

效果:

  全篇完。

本人是一个嵌入式未入门小白,博客仅仅代表我个人主观见解,记录成长笔记。
笔记是以最简单的方式,只展示最核心的原理。
若有与 大神大大 见解有歧义,我绝对坚信 大神大大 见解是对的,我的是错的。
若无积分等无法下载源码,可加入QQ群657407920下载交流经验。感谢~!

 

 

这篇关于STM32CbueMX之Fatfs移植到SPI_Flash的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/697388

相关文章

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

什么是 Flash Attention

Flash Attention 是 由 Tri Dao 和 Dan Fu 等人在2022年的论文 FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness 中 提出的, 论文可以从 https://arxiv.org/abs/2205.14135 页面下载,点击 View PDF 就可以下载。 下面我

STM32内部闪存FLASH(内部ROM)、IAP

1 FLASH简介  1 利用程序存储器的剩余空间来保存掉电不丢失的用户数据 2 通过在程序中编程(IAP)实现程序的自我更新 (OTA) 3在线编程(ICP把整个程序都更新掉) 1 系统的Bootloader写死了,只能用串口下载到指定的位置,启动方式也不方便需要配置BOOT引脚触发启动  4 IAP(自己写的Bootloader,实现程序升级) 1 比如蓝牙转串口,

FreeRTOS-基本介绍和移植STM32

FreeRTOS-基本介绍和STM32移植 一、裸机开发和操作系统开发介绍二、任务调度和任务状态介绍2.1 任务调度2.1.1 抢占式调度2.1.2 时间片调度 2.2 任务状态 三、FreeRTOS源码和移植STM323.1 FreeRTOS源码3.2 FreeRTOS移植STM323.2.1 代码移植3.2.2 时钟中断配置 一、裸机开发和操作系统开发介绍 裸机:前后台系

STM32 ADC+DMA导致写FLASH失败

最近用STM32G070系列的ADC+DMA采样时,遇到了一些小坑记录一下; 一、ADC+DMA采样时进入死循环; 解决方法:ADC-dma死循环问题_stm32 adc dma死机-CSDN博客 将ADC的DMA中断调整为最高,且增大ADCHAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_Buffer_Size); 的ADC_Bu

RT-Thread(Nano版本)的快速移植(基于NUCLEO-F446RE)

目录 概述 1 RT-Thread 1.1 RT-Thread的版本  1.2 认识Nano版本 2 STM32F446U上移植RT-Thread  2.1 STM32Cube创建工程 2.2 移植RT-Thread 2.2.1 安装RT-Thread Packet  2.2.2 加载RT-Thread 2.2.3 匹配相关接口 2.2.3.1 初次编译代码  2.2.3.

stm32之软件SPI读写W25Q64存储器应用案例

系列文章目录 1. stm32之SPI通信协议 文章目录 系列文章目录前言一、电路接线图二、应用案例代码三、应用案例分析3.1 SPI通信模块3.2 W25Q64模块3.3 主程序 前言 提示:本文主要用作在学习江科大自化协STM32入门教程后做的归纳总结笔记,旨在学习记录,如有侵权请联系作者 本案例使用软件SPI通信的方式实现了STM32与W25Q64 Flas

libmad音频解码库-Linux交叉编译移植

下载并解压libmad-0.15.1b.tar.gz 下载链接:https://downloads.sourceforge.net/mad/libmad-0.15.1b.tar.gz $tar -xvf libmad-0.15.1b.tar.gz$cd libmad-0.15.1b 1、先执行下面的命令:这条命令是为了适配高版本的gcc,因为高版本的gcc已经将-fforce-mem去除了:

arm linux lua移植

lua: lua home 1.下载lua源码 lua下载 lua-5.3.4.tar.gz 2.解压: tar xvf lua-5.3.4.tar.gz 3.修改makefile and luaconf.h $修改 lua-5.3.4/Makefile #INSTALL_TOP= /usr/local INSTALL_TOP= $(shell pwd)/out #修改安装目录(当前目录/o

Flink1.10基于工厂模式的任务提交与SPI机制

《2021年最新版大数据面试题全面开启更新》 Flink任务执行模式包含了yarn-session、standalone、per-job、local, 在1.10中又增加k8s的执行模式,那么在任务提交过程中如何根据不同的执行模式进行任务提交呢?主要通过两个接口来实现:PipelineExecutorFactory 与 PipelineExecutor。PipelineExecutorF