立创梁山派--移植开源的SFUD和FATFS实现SPI-FLASH文件系统

本文主要是在sfud的基础上进行fatfs文件系统的移植,并不对sfud的移植再进行过多的讲解了哦,所以如果想了解sfud的移植过程,请参考我的另外一篇文章传送门

正文开始咯

  • 首先我们需要先准备资料准备好,这里对于fatfs的移植,我参考的是野火的教程。所以我在文章末尾也会给出相关的教程链接。
    我们需要的东西有:一份实现了sfud库的裸机代码,这个可以从我上篇文章的文末连接中获取,这里我也将代码的链接贴出:传送门, 还有一份fatfs源码,这个为了和野火的教程保持移植,这里用的是跟野火教程版本一致的fatfs源码,这里的源码可以在文末的链接开源代码链接中获取。
  • 开始移植
    打开fatfs代码

    我们将其添加到我们的工程中

    添加文件路径#

对于fatfs的介绍,野火的教程中都有详细的介绍,这里就不再赘述了。其实它的移植是比较简单的,只需要修改diskio.c文件中的相关接口函数和ffconf.h配置文件。

  • 打开diskio.c文件
    首先包含我们需要的头文件,定义我们要操作的flash
c 复制代码
#include "bsp_spi.h"
#include "sfud.h"
const sfud_flash *flash1;

#define W25Q64 0xEF16 //根据板载的spiflash来修改 

/* 为每个设备定义一个物理编号 */
#define ATA			    0     // 预留SD卡使用
#define SPI_FLASH		1     // 外部SPI Flash

对需要修改的函数进行说明

c 复制代码
//获取状态函数
DSTATUS disk_status(BYTE pdrv /* 物理编号 */
)
{

    DSTATUS status = STA_NOINIT;
    switch (pdrv)
    {
    case ATA: /* SD CARD */
        break;

    case SPI_FLASH:
        /* SPI Flash状态检测:读取SPI Flash 设备ID */
        if (W25Q64 == spi4_flash_readID())
        {
            /* 设备ID读取结果正确 */

            status &= ~STA_NOINIT;
        }
        else
        {
            /* 设备ID读取结果错误 */
            status = STA_NOINIT;
            ;
        }
        break;

    default:
        status = STA_NOINIT;
    }
    return status;
}

初始化函数

c 复制代码
DSTATUS disk_initialize(BYTE pdrv /* 物理编号 */
)
{
    uint16_t i;
    DSTATUS status = STA_NOINIT;
    switch (pdrv)
    {
    case ATA: /* SD CARD */
        break;

    case SPI_FLASH: /* SPI Flash */
        sfud_init();
        flash1 = sfud_get_device_table() + 0; //对应到设备表的第一个
        // printf("%0x\n", spi4_flash_readID());//如果不清楚设备id的话,可以在这里打印一下
        status = disk_status(SPI_FLASH); //获取设备状态
        break;
    default:
        status = STA_NOINIT;
    }
    return status;
}

读取函数

c 复制代码
DRESULT disk_read(BYTE pdrv,    /* 设备物理编号(0..) */
                  BYTE *buff,   /* 数据缓存区 */
                  DWORD sector, /* 扇区首地址 */
                  UINT count    /* 扇区个数(1..128) */
)
{
    DRESULT status = RES_PARERR;
    switch (pdrv)
    {
    case ATA: /* SD CARD */
        break;

    case SPI_FLASH:
        //这里的左移12位,等于4096,因为flash的一个扇区是4k字节
        sfud_read(flash1, sector << 12, count << 12, buff);
        status = RES_OK;
        break;

    default:
        status = RES_PARERR;
    }
    return status;
}

写入函数

c 复制代码
#if _USE_WRITE
DRESULT disk_write(BYTE pdrv,        /* 设备物理编号(0..) */
                   const BYTE *buff, /* 欲写入数据的缓存区 */
                   DWORD sector,     /* 扇区首地址 */
                   UINT count        /* 扇区个数(1..128) */
)
{
    DRESULT status = RES_PARERR;
    if (!count)
    {
        return RES_PARERR; /* Check parameter */
    }

    switch (pdrv)
    {
    case ATA: /* SD CARD */
        break;

    case SPI_FLASH:
        sfud_erase(flash1, sector << 12, 4096); // SPI_FLASH都是要先擦除为1才能写
        sfud_write(flash1, sector << 12, count << 12, (uint8_t *)buff);
        status = RES_OK;
        break;

    default:
        status = RES_PARERR;
    }
    return status;
}
#endif

设备信息函数

c 复制代码
#if _USE_IOCTL
DRESULT disk_ioctl(BYTE pdrv, /* 物理编号 */
                   BYTE cmd,  /* 控制指令 */
                   void *buff /* 写入或者读取数据地址指针 */
)
{
    DRESULT status = RES_PARERR;
    switch (pdrv)
    {
    case ATA: /* SD CARD */
        break;

    case SPI_FLASH:
        switch (cmd)
        {
        //扇区数量就是例如我这里是8M的flash
        //那就是 8 * 1024 * 1024 (字节)/ 4096(一个扇区的大小) = 2048(扇区数)
        case GET_SECTOR_COUNT:
            *(DWORD *)buff = 2048; //这里是你的扇区数量
            break;
        /* 扇区大小  */
        case GET_SECTOR_SIZE:
            *(WORD *)buff = 4096;
            break;
        /* 同时擦除扇区个数 */
        case GET_BLOCK_SIZE:
            *(DWORD *)buff = 1;
            break;
        }
        status = RES_OK;
        break;

    default:
        status = RES_PARERR;
    }
    return status;
}
#endif

获取时间函数

c 复制代码
//这里是获取时间函数,后续如果有需要的话,可以使用rtc的时间
__weak DWORD get_fattime(void)
{
    /* 返回当前时间戳 */
    return ((DWORD)(2015 - 1980) << 25) /* Year 2015 */
           | ((DWORD)1 << 21)           /* Month 1 */
           | ((DWORD)1 << 16)           /* Mday 1 */
           | ((DWORD)0 << 11)           /* Hour 0 */
           | ((DWORD)0 << 5)            /* Min 0 */
           | ((DWORD)0 >> 1);           /* Sec 0 */
}
  • 对于ffconf.h主要是修改一下宏定义即可

到这里就已经是移植好啦

然后就可以开始进行测试啦

main.c文件

c 复制代码
#include "main.h"
#include "bsp_led.h"
#include "bsp_usart.h"
#include "gd32f4xx.h"
#include "sys.h"
#include "systick.h"
#include <stdio.h>

#include "bsp_spi.h"
#include <sfud.h>

#include "ff.h"

FATFS fs;													/* FatFs文件系统对象 */
FIL fnew;													/* 文件对象 */
FRESULT res_flash;                /* 文件操作结果 */
UINT fnum;            					  /* 文件成功读写数量 */
BYTE ReadBuffer[1024]={0};        /* 读缓冲区 */
BYTE WriteBuffer[] =              /* 写缓冲区*/
"欢迎使用野火STM32 F429开发板 今天是个好日子,新建文件系统测试文件\r\n";  

/*!
    \brief    main function
    \param[in]  none
    \param[out] none
    \retval     none
*/
int main(void)
{
    systick_config();
    led_gpio_config(); // led初始化
    usart_gpio_config(115200U);
    /* SFUD initialize */
//    if (sfud_init() == SFUD_SUCCESS)
//    {
//       sfud_demo(0, sizeof(sfud_demo_test_buf), sfud_demo_test_buf);
//    }

    printf("****** 这是一个SPI FLASH 文件系统实验 ******\r\n");
  
	//在外部SPI Flash挂载文件系统,文件系统挂载时会对SPI设备初始化
	res_flash = f_mount(&fs,"1:",1);
	
/*----------------------- 格式化测试 ---------------------------*/  
	/* 如果没有文件系统就格式化创建创建文件系统 */
	if(res_flash == FR_NO_FILESYSTEM)
	{
		printf("》FLASH还没有文件系统,即将进行格式化...\r\n");
    /* 格式化 */
		res_flash=f_mkfs("1:",0,0);							
		
		if(res_flash == FR_OK)
		{
			printf("》FLASH已成功格式化文件系统。\r\n");
      /* 格式化后,先取消挂载 */
			res_flash = f_mount(NULL,"1:",1);			
      /* 重新挂载	*/			
			res_flash = f_mount(&fs,"1:",1);
		}
		else
		{
			printf("《《格式化失败。》》\r\n");
			while(1);
		}
	}
  else if(res_flash!=FR_OK)
  {
    printf("!!外部Flash挂载文件系统失败。(%d)\r\n",res_flash);
    printf("!!可能原因:SPI Flash初始化不成功。\r\n");
	printf("请下载 SPI---读写串行FLASH 例程测试,如果正常,在该例程f_mount语句下if语句前临时多添加一句 res_flash = FR_NO_FILESYSTEM; 让重新直接执行格式化流程\r\n");
		while(1);
  }
  else
  {
    printf("》文件系统挂载成功,可以进行读写测试\r\n");
  }
  
/*----------------------- 文件系统测试:写测试 -----------------------------*/
	/* 打开文件,如果文件不存在则创建它 */
	printf("\r\n****** 即将进行文件写入测试... ******\r\n");	
	res_flash = f_open(&fnew, "1:FatFs读写测试文件.txt",FA_CREATE_ALWAYS | FA_WRITE );
	if ( res_flash == FR_OK )
	{
		printf("》打开/创建FatFs读写测试文件.txt文件成功,向文件写入数据。\r\n");
    /* 将指定存储区内容写入到文件内 */
		res_flash=f_write(&fnew,WriteBuffer,sizeof(WriteBuffer),&fnum);
    if(res_flash==FR_OK)
    {
      printf("》文件写入成功,写入字节数据:%d\n",fnum);
      printf("》向文件写入的数据为:\r\n%s\r\n",WriteBuffer);
    }
    else
    {
      printf("!!文件写入失败:(%d)\n",res_flash);
    }    
		/* 不再读写,关闭文件 */
    f_close(&fnew);
	}
	else
	{	
		printf("!!打开/创建文件失败。\r\n");
	}
	
/*------------------- 文件系统测试:读测试 ------------------------------------*/
	printf("****** 即将进行文件读取测试... ******\r\n");
	res_flash = f_open(&fnew, "1:FatFs读写测试文件.txt", FA_OPEN_EXISTING | FA_READ); 	 
	if(res_flash == FR_OK)
	{
		printf("》打开文件成功。\r\n");
		res_flash = f_read(&fnew, ReadBuffer, sizeof(ReadBuffer), &fnum); 
    if(res_flash==FR_OK)
    {
      printf("》文件读取成功,读到字节数据:%d\r\n",fnum);
      printf("》读取得的文件数据为:\r\n%s \r\n", ReadBuffer);	
    }
    else
    {
      printf("!!文件读取失败:(%d)\n",res_flash);
    }		
	}
	else
	{
		printf("!!打开文件失败。\r\n");
	}
	/* 不再读写,关闭文件 */
	f_close(&fnew);	
  
	/* 不再使用文件系统,取消挂载文件系统 */
	f_mount(NULL,"1:",1);
  
    while (1)
    {
    }
}
效果如下

origin_url=image-4.png&pos_id=img-2thXHWxj-1721718767616)

移植成功

相关的开源代码:gitee

如果对你有所帮助的话,请给我点一个star,嘿嘿

相关推荐
PegasusYu30 分钟前
STM32CUBEIDE FreeRTOS操作教程(九):eventgroup事件标志组
stm32·教程·rtos·stm32cubeide·free-rtos·eventgroup·时间标志组
lantiandianzi5 小时前
基于单片机的多功能跑步机控制系统
单片机·嵌入式硬件
文弱书生6565 小时前
输出比较简介
stm32
哔哥哔特商务网5 小时前
高集成的MCU方案已成电机应用趋势?
单片机·嵌入式硬件
跟着杰哥学嵌入式5 小时前
单片机进阶硬件部分_day2_项目实践
单片机·嵌入式硬件
lsjweiyi6 小时前
极简AI工具箱网站开源啦!
opencv·开源·微信支付·支付宝支付·百度ai·极简ai工具箱·ai图像处理
东芝、铠侠总代136100683937 小时前
浅谈TLP184小型平面光耦
单片机·嵌入式硬件·物联网·平面
lantiandianzi7 小时前
基于单片机中医药柜管理系统的设计
单片机·嵌入式硬件
嵌入式知识大讲堂7 小时前
HDMI数据传输三种使用场景
单片机
开源社7 小时前
一场开源视角的AI会议即将在南京举办
人工智能·开源