[MM32硬件] 【灵动微电子MM32F0121测评】9、sfud+littlefs在spi flash中实现文件系统

[复制链接]
 楼主| sujingliang 发表于 2025-6-16 19:51 | 显示全部楼层 |阅读模式
本帖最后由 sujingliang 于 2025-6-16 19:55 编辑

本文采用sfud+littlefs实现驱动SPI flash存储,并在flash使用文件系统读写文件

一、SFUD
SFUD (Serial Flash Universal Driver) 是一款开源的串行 Flash 通用驱动库,主要特点包括:
通用性:
支持多种 SPI Flash 芯片 (如 Winbond、GD、MXIC 等品牌)
通过 JEDEC ID 自动识别芯片参数
功能完整:
实现了标准 SPI Flash 操作 (读/写/擦除)
支持 SFDP (Serial Flash Discoverable Parameters) 标准
提供坏块管理接口 (针对 NAND Flash)
轻量级:
代码简洁,资源占用低
适合嵌入式系统使用

总结一下:使用SFUD可以简化flash读写

二、LittleFS
专为嵌入式设计的文件系统
掉电安全
磨损均衡
内存占用小(约2KB RAM)

SFUD 负责底层 Flash 芯片的物理操作
LittleFS 负责上层文件系统的组织和逻辑管理

层次关系:
应用层- --->文件系统层 (LittleFS)  ------>Flash 驱动层 (SFUD)   --------> 硬件接口层 (SPI)
使用 SFUD + LittleFS 的优势:省去 Flash 驱动开发工作、自动适配多种 Flash 芯片、更专注于文件系统应用开发
三、准备


LibSamples_MM32F0120_V1.13.4下新建middlewares目录复制sfud、littlefs目录到middlewares下。
sfud下载地址:https://gitee.com/lgy4647/SFUDlittlefs
下载地址:https://gitee.com/zhistech/littlefs
我没有从网上下载。在之前一个开发板SDK中找到了sfud、littlefs目录,直接复制了一份,这样做的好处是这样获得的版本是经过验证的,并且是经过精简的版本。坏处是不是最新版。

将必要的源文件加入工程
9.png
增加Include Paths
9.png

四、开始编写软件部分1、sfud配置和接口文件


复制sfud_cfg.h和sfud_port.c到工程根目录
sfud_cfg.h

  1. #define SFUD_DEBUG_MODE

  2. #define SFUD_USING_SFDP

  3. #define SFUD_USING_FLASH_INFO_TABLE

  4. enum {
  5.     SFUD_SPI_DEVICE_INDEX = 0,
  6. };

  7. #define SFUD_FLASH_DEVICE_TABLE         \
  8. {                                                                                                                                 \
  9.   [SFUD_SPI_DEVICE_INDEX] = {.name = "ZD25Q80", .spi.name = "SPI1"}, \
  10. }
打开调试使用SDFP使用FLASH_INFO_TABLE板载的flash型号是ZD25Q80,很遗憾sfud没有这个型号,需要在middlewares\sfud\sfud\inc\sfud_flash_def.h文件中增加对ZD25Q80的支持

#define SFUD_MF_ID_ZETTA                               0xBA
#define SFUD_FLASH_CHIP_TABLE                                                                                       \{  其他flash  {"ZD25Q80",SFUD_MF_ID_ZETTA,0x40,0x14,1L*1024L*1024L,SFUD_WM_PAGE_256B,4096,0x20},        \}

名字:"ZD25Q80"
珠海全志(Zetta)的典型ID:FUD_MF_ID_ZETTA(0xBA)
25Q80 类型编码:0x40
1MB容量编码(参考JEDEC):0x14
1MB:1 * 1024 * 1024
256字节页编程:SFUD_WM_PAGE_256B
4KB扇区擦除:4096
扇区擦除命令:0x20

sfud_port.c主要实现了SPI初始化、SPI发送接收函数spi_write_read,这是和SFUD的接口函数。


CS控制不能使用GPIO设置高低电平的方式,而要像下面这样写:
#define SPI_FLASH_CS_H() SPI_CSInternalSelected(SPI1, DISABLE)
#define SPI_FLASH_CS_L() SPI_CSInternalSelected(SPI1, ENABLE)
  1. #include <sfud.h>
  2. #include <stdarg.h>
  3. #include "hal_conf.h"
  4. #include "sfud_cfg.h"
  5. #include "main.h"

  6. /* send dummy data for read data */
  7. #define DUMMY_DATA                               0xFF
  8. extern uint32_t SystemCoreClock;
  9. static char log_buf[256u];

  10. void sfud_log_debug(const char *file, const long line, const char *format, ...);
  11. static void retry_delay_100us(void);

  12. static void spi_lock(const sfud_spi *spi)
  13. {
  14.     __disable_irq();
  15. }

  16. static void spi_unlock(const sfud_spi *spi)
  17. {
  18.     __enable_irq();
  19. }

  20. /* xfer data by spi port. */
  21. static uint8_t spi_xfer(const uint8_t value)
  22. {
  23.                
  24.     while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_TXEPT))
  25.     {
  26.     }
  27.                 SPI_SendData(SPI1, value);
  28.     while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_RXAVL))
  29.     {
  30.     }
  31.     return SPI_ReceiveData(SPI1);
  32. }

  33. /* control the cs pin output. */
  34. static void spi_cs_control(bool enable)
  35. {
  36.     if (true == enable)
  37.     {
  38.         //GPIO_ResetBits(BOARD_FLASH_CS_GPIO_PORT, BOARD_FLASH_CS_GPIO_PIN);
  39.                                 SPI_FLASH_CS_L();
  40.                         
  41.     }
  42.     else
  43.     {
  44.         //GPIO_SetBits(BOARD_FLASH_CS_GPIO_PORT, BOARD_FLASH_CS_GPIO_PIN);
  45.                                 SPI_FLASH_CS_H();
  46.     }
  47. }

  48. /**
  49. * SPI write data then read data
  50. */
  51. static sfud_err spi_write_read(const sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
  52.         size_t read_size) {
  53.     sfud_err result = SFUD_SUCCESS;
  54.                 uint32_t i;

  55.     /* assert. */
  56.     if (write_size)
  57.     {
  58.         SFUD_ASSERT(write_buf);
  59.     }
  60.     if (read_size)
  61.     {
  62.         SFUD_ASSERT(read_buf);
  63.     }

  64.     /* CS Pin valid. */
  65.     spi_cs_control(true);

  66.     /* put data. */
  67.     for (i = 0u; i < write_size; i++)
  68.     {
  69.         spi_xfer(write_buf[i]);
  70.     }

  71.     /* recv data. */
  72.     for (i = 0u; i < read_size; i++)
  73.     {
  74.         read_buf[i] = spi_xfer(DUMMY_DATA);
  75.     }

  76.     /* CS Pin invalid. */
  77.     spi_cs_control(false);

  78.     return result;
  79. }


  80. void SPI_Configure(void)
  81. {
  82.     GPIO_InitTypeDef GPIO_InitStruct;
  83.     SPI_InitTypeDef  SPI_InitStruct;

  84.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

  85.     SPI_StructInit(&SPI_InitStruct);
  86.     SPI_InitStruct.SPI_Mode      = SPI_Mode_Master;
  87.     SPI_InitStruct.SPI_DataSize  = SPI_DataSize_8b;
  88.     SPI_InitStruct.SPI_DataWidth = 8;
  89.     SPI_InitStruct.SPI_CPOL      = SPI_CPOL_Low;
  90.     SPI_InitStruct.SPI_CPHA      = SPI_CPHA_1Edge;
  91.     SPI_InitStruct.SPI_NSS       = SPI_NSS_Soft;
  92.     SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
  93.     SPI_InitStruct.SPI_FirstBit  = SPI_FirstBit_MSB;
  94.     SPI_Init(SPI1, &SPI_InitStruct);

  95.     SPI_BiDirectionalLineConfig(SPI1, SPI_Enable_RX);
  96.     SPI_BiDirectionalLineConfig(SPI1, SPI_Enable_TX);

  97.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

  98.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource4,  GPIO_AF_0);
  99.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource5,  GPIO_AF_0);
  100.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource6,  GPIO_AF_0);
  101.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource7,  GPIO_AF_0);

  102.     GPIO_StructInit(&GPIO_InitStruct);
  103.     GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
  104.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
  105.     GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;
  106.     GPIO_Init(GPIOA, &GPIO_InitStruct);

  107.     GPIO_StructInit(&GPIO_InitStruct);
  108.     GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_6;
  109.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
  110.     GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_IPU;
  111.     GPIO_Init(GPIOA, &GPIO_InitStruct);

  112.     SPI_Cmd(SPI1, ENABLE);
  113. }

  114. sfud_err sfud_spi_port_init(sfud_flash *flash)
  115. {
  116.     sfud_err result = SFUD_SUCCESS;

  117.     /* Setup SPI module. */
  118.                 SPI_Configure();

  119.     /* init sfud spi obj. */
  120.     flash->spi.wr           = spi_write_read;
  121.     flash->spi.lock         = spi_lock;
  122.     flash->spi.unlock       = spi_unlock;
  123.     flash->spi.user_data    = NULL;
  124.     flash->retry.delay      = retry_delay_100us;
  125.     flash->retry.times      = 60u * 10000u;

  126.     return result;
  127. }

  128. /**
  129. * This function is print debug info.
  130. *
  131. * @param file the file which has call this function
  132. * @param line the line number which has call this function
  133. * @param format output format
  134. * @param ... args
  135. */
  136. void sfud_log_debug(const char *file, const long line, const char *format, ...)
  137. {
  138.     va_list args;

  139.     /* args point to the first variable parameter */
  140.     va_start(args, format);
  141.     printf("[SFUD](%s:%ld) ", file, line);
  142.     /* must use vprintf to print */
  143.     vsnprintf(log_buf, sizeof(log_buf), format, args);
  144.     printf("%s\r\n", log_buf);
  145.     va_end(args);
  146. }

  147. /**
  148. * This function is print routine info.
  149. *
  150. * @param format output format
  151. * @param ... args
  152. */
  153. void sfud_log_info(const char *format, ...)
  154. {
  155.     va_list args;

  156.     /* args point to the first variable parameter */
  157.     va_start(args, format);
  158.     printf("[SFUD]");
  159.     /* must use vprintf to print */
  160.     vsnprintf(log_buf, sizeof(log_buf), format, args);
  161.     printf("%s\r\n", log_buf);
  162.     va_end(args);
  163. }

  164. static void retry_delay_100us(void)
  165. {
  166.                 uint32_t i;
  167.     for ( i = 0; i < SystemCoreClock / 10000u; i++)
  168.     {
  169.         __NOP();
  170.     }
  171. }

2、littlefs接口文件


lfs_port.c需要实现几个接口函数,需要注意lfs_flash_init中的参数与flash的参数相对应
  1. #include "lfs.h"
  2. #include "sfud.h"

  3.        int lfs_flash_init(      struct lfs_config *c);
  4. static int lfs_flash_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size);
  5. static int lfs_flash_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size);
  6. static int lfs_flash_erase(const struct lfs_config *c, lfs_block_t block);
  7. static int lfs_flash_sync(const struct lfs_config *c);

  8. int lfs_flash_init(struct lfs_config *c)
  9. {
  10.     sfud_init(); /* init sfud. */
  11.     const sfud_flash *flash = sfud_get_device(0u);

  12.     c->read = lfs_flash_read;
  13.     c->prog = lfs_flash_prog;
  14.     c->erase = lfs_flash_erase;
  15.     c->sync = lfs_flash_sync;

  16.     c->read_size = 16;
  17.     c->prog_size = 256;//16;
  18.     //c->block_size = LFS_FLASH_SECTOR_SIZE;
  19.     c->block_size = 4096;//flash->chip.erase_gran;
  20.     c->block_count = 256;
  21.     c->block_cycles = 500;
  22.     c->cache_size = 256;//16;
  23.     c->lookahead_size = 16;

  24.     //c->read_buffer = (void *)lfs_flash_read_buf;
  25.     //c->prog_buffer = (void *)lfs_flash_prog_buf;
  26.     //c->lookahead_buffer = (void *)lfs_flash_lookahead_buf;


  27.     return LFS_ERR_OK;
  28. }

  29. static int lfs_flash_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size)
  30. {
  31.     uint32_t addr = block * c->block_size + off;
  32.     const sfud_flash *flash = sfud_get_device(0u);

  33.     sfud_read(flash, addr, size, (uint8_t *)buffer);
  34.     return LFS_ERR_OK;
  35. }

  36. static int
  37. lfs_flash_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size)
  38. {
  39.     uint32_t addr = block * c->block_size + off;
  40.     const sfud_flash *flash = sfud_get_device(0u);

  41.     sfud_write(flash, addr, size, (uint8_t *)buffer);
  42.     return LFS_ERR_OK;
  43. }

  44. static int lfs_flash_erase(const struct lfs_config *c, lfs_block_t block)
  45. {
  46.     const sfud_flash *flash = sfud_get_device(0u);

  47.     sfud_erase(flash, block * c->block_size, c->block_size);
  48.     return LFS_ERR_OK;
  49. }

  50. static int lfs_flash_sync(const struct lfs_config *c)
  51. {
  52.     return LFS_ERR_OK;
  53. }

3、写一个测试函数
  1. void lfsTask(void *pvParameters )
  2. {
  3.         int err = lfs_flash_init(&app_lfs_config);
  4.     if (err)
  5.     {
  6.         printf("lfs_flash_init() failed.\r\n");
  7.         while (1);
  8.     }
  9.                
  10.                 err = lfs_mount(&app_lfs, &app_lfs_config);
  11.     if (err)
  12.     {
  13.         printf("lfs_mount() failed.\r\n");
  14.         lfs_format(&app_lfs, &app_lfs_config);
  15.         printf("lfs_format() done.\r\n");
  16.         lfs_mount(&app_lfs, &app_lfs_config);
  17.         printf("lfs_mount() done.\r\n");
  18.     }
  19.                
  20.                 // read current count
  21.     uint32_t boot_count = 0;
  22.     lfs_file_open(&app_lfs, &app_lfs_file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
  23.     lfs_file_read(&app_lfs, &app_lfs_file, &boot_count, sizeof(boot_count));

  24.     // update boot count
  25.     boot_count += 1;
  26.     lfs_file_rewind(&app_lfs, &app_lfs_file);
  27.     lfs_file_write(&app_lfs, &app_lfs_file, &boot_count, sizeof(boot_count));

  28.     // remember the storage is not updated until the file is closed successfully
  29.     lfs_file_close(&app_lfs, &app_lfs_file);

  30.     // release any resources we were using
  31.     lfs_unmount(&app_lfs);

  32.    
  33.                
  34.                 for( ;; )
  35.                 {
  36.                         // print the boot count
  37.                         printf("boot_count: %u\r\n", (unsigned)boot_count);
  38.                         vTaskDelay(pdMS_TO_TICKS(2000));
  39.     }
  40. }
主要的功能是初始化little_fs,装载文件系统,如果第一装载会失败,直接格式化文件系统。打开一个boot_count文件。每次开发板重启boot次数加1,并记录到该文件中。

9.png



AdaMaYun 发表于 2025-7-31 17:57 | 显示全部楼层
sfud+littlefs在spi flash中实现文件系统
您需要登录后才可以回帖 登录 | 注册

本版积分规则

84

主题

146

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部