当前位置: 首页 > news >正文

第三十八章 ESP32S3 SPIFFS 实验


上一章实验中已经成功驱动 SD 卡,并可对 SD 卡进行读写操作,但读写 SD 卡时都是直接读出或写入二进制数据,这样使用起来显得十分不方便,因此本章将介绍 SPIFFS, SPIFFS是一个用于 SPI NOR flash 设备的嵌入式文件系统,支持磨损均衡以及文件系统一致性检查等功能。通过本章的学习,将学习到 SPIFFS 的基本使用。
本章分为如下几个小节:
38.1 SPIFFS 简介
38.2 硬件设计
38.3 程序设计
38.4 下载验证

38.1 SPIFFS 介绍

        SPIFFS​​ 是一个专为​​嵌入式系统​​和​​物联网(IoT)设备​​设计的​​开源文件系统​​。它的名字是 ​​SPI Flash File System​​ 的缩写,顾名思义,它主要用于在通过 ​​SPI​​ 接口连接的 ​​NOR Flash​​ 存储器上存储文件。SPIFFS 的设计遵循了嵌入式环境的特定约束,其核心特点包括:

特性

描述

​​轻量级​​

代码量极小(通常只有几千字节),对 RAM 和 CPU 的资源占用极低,非常适合资源受限的 MCU。

​​掉电安全​​

在设计上考虑了意外断电的情况,通过机制确保文件系统结构不会轻易损坏。

​​动态磨损均衡​​

自动将写操作均匀分布到整个 Flash 存储区,避免某些扇区过早磨损而失效,​​显著延长 Flash 寿命​​。

​​基于页管理​​

其内部存储管理基于 Flash 的物理页(Page)和扇区(Sector)结构,效率更高。

​​支持随机访问​​

可以随机读取和写入文件中的任何位置。

表38.1.1 SPIFFS核心特点

工作原理与结构:

        SPIFFS 将 Flash 存储器视为一个线性的、可寻址的空间,并将其逻辑上组织为以下结构:

  • ​​页 (Page)​​:

        这是​​最基本的读写单元​​,大小通常与 Flash 芯片的物理页大小对齐(例如 256 字节)。每个页包含​​数据区​​和​​元数据头​​。元数据头记录了该页的状态(有效、无效、已删除)以及它属于哪个文件。

  • ​​扇区 (Sector)​​:

        由多个​​页​​组成,是​​擦除(Erase)操作的基本单元​​。Flash 存储器只能将位从 1 改为 0(写入),而将 0 改回 1 必须通过​​扇区擦除​​。擦除操作非常耗时。SPIFFS 的核心工作之一就是通过​​垃圾回收(Garbage Collection)​​ 机制,将包含无效数据的扇区进行擦除,使其变为可用状态。

  • ​​文件索引​​:

        SPIFFS 没有传统的目录结构(如 FATFS),所有文件都位于根目录下。它使用一种​​自定义的线性索引​​来跟踪文件及其数据页的位置,而不是像 FAT 表那样的结构。
SPIFFS 内部结构示意图:文件数据被分散存储在多个物理页中,通过元数据链接起来。

与常见文件系统的对比:

特性

​SPIFFS​

​FATFS​​ (用于SD卡)

​LittleFS​​ (SPIFFS的替代者)

​设计目标​

极简,小文件,NOR Flash

通用,兼容性,块设备

更可靠,更高性能,NOR Flash

​目录支持​

​不支持​​(只有根目录)

​支持​​(多级目录)

​支持​​(有限的多级目录)

​功耗优化​

一般

一般

​更优​​(更少的写放大)

​动态磨损均衡​

​支持​

不支持(需手动管理)

​支持​​(更优的算法)

​掉电安全性​

较好

一般(易产生碎片错误)

​更好​​(一致性更强)

​适用介质​

​NOR Flash​​ (SPI接口)

​SD卡、MMC​​ (块设备)

​NOR Flash​​ (SPI接口)

表38.1.2 SPIFFS与常见文件系统的对比

        SPIFFS​​ 是嵌入式领域一个经典的、轻量级的解决方案,特别适合在​​片外 NOR Flash​​ 上存储​​配置文件、网页资源、小数据日志​​等。
然而,由于其已知的缺点和已停止维护的状态,对于​​新项目​​,强烈建议考虑其现代替代品—​​LittleFS​​。LittleFS 提供了更好的性能、更高的可靠性以及更友好的目录支持,正在成为嵌入式Flash 文件系统的新标准。在 ESP-IDF 中,LittleFS已经得到了很好的支持,其使用方式与SPIFFS 非常相似。

38.2 硬件设计

38.2.1 例程功能
1.在 nor flash 指定区域新建 holle.txt 文件,然后对这文件进行读写操作;
2. LED 闪烁,指示程序正在运行。

38.2.2 硬件资源

        1. LED 灯
LED -IO0
2. XL9555
IIC_SDA-IO41
IIC_SCL-IO42
3. SPILCD
CS-IO21
SCK-IO12
SDA-IO11
DC-IO40(在 P5 端口,使用跳线帽将 IO_SET 和 LCD_DC 相连)
PWR- IO1_3(XL9555)
RST- IO1_2(XL9555)
4. SPIFFS

38.2.3 原理图

        本章实验使用的 SPIFFS 为软件库,因此没有对应的连接原理图。

38.3 程序设计

38.3.1 程序流程图
本实验的程序流程图:

图 38.3.1.1 IIC_EXIO 实验程序流程图

38.3.2 SPIFFS 函数解析

        在 ESP-IDF 环境中,使用 SPIFFS 通常通过 ​​虚拟文件系统 (VFS)​​ 层进行挂载,然后使用标准的 POSIX 函数(如 openreadwriteclose)或 C 库函数(fopenfprintffscanf)进行操作。

(1)注册装载 SPIFFS
该函数使用给定的路径前缀将 SPIFFS 注册并装载到 VFS,其函数原型如下所示:

esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf);

        该函数的形参描述,如下表所示:

形参描述
conf指向 esp_vfs_spiffs_conf_t 配置结构的指针

表 38.3.2.1 函数 esp_vfs_spiffs_register ()形参描述

        该函数的返回值描述,如下表所示:

返回值描述
ESP_OK返回: 0,配置成功
ESP_ERR_NO_MEM如果无法分配对象
ESP_ERR_INVALID_STATE如果已安装或分区已加密
ESP_ERR_NOT_FOUND如果找不到 SPIFFS 的分区
ESP_FAIL如果装载或格式化失败

表 38.3.2.2 函数 esp_vfs_spiffs_register ()返回值描述

        该函数使用 esp_vfs_spiffs_conf_t 类型的结构体变量传入,该结构体的定义如下所示:

结构体成员变量可选参数
esp_vfs_spiffs_conf_tbase_path与文件系统关联的文件路径前缀。
partition_label可选,要使用的 SPIFFS 分区的标签。如果设置为 NULL则 f
max_files可以同时打开的最大文件数。
format_if_mount_failed如果为 true,则在装载失败时将格式化文件系统。

表 38.3.2.3 esp_vfs_spiffs_conf_t 结构体参数值描述

        完成上述结构体参数配置之后,可以将结构传递给esp_vfs_spiffs_register 函数,用以实例化
SPIFFS。

(2)获取 SPIFFS 的信息
该函数用于获取 SPIFFS 的信息,其函数原型如下所示:

esp_err_t esp_spiffs_info(const char* partition_label,size_t *total_bytes,size_t *used_bytes);

        该函数的形参描述,如下表所示:

形参描述
param partition_label指向分区标签的指针,分区表名称
total_bytes文件系统的大小
used_bytes文件系统中当前使用的字节数

表 38.3.2.4 函数 esp_spiffs_info ()形参描述

        该函数的返回值描述,如下表所示:

返回值描述
ESP_OK返回: 0,配置成功
ESP_FAIL如果装载失败

表 38.3.2.5 函数 esp_spiffs_info ()返回值描述

(3)注销和卸载 SPIFFS

        该函数从 VFS 注销和卸载 SPIFFS,其函数原型如下所示:

esp_err_t esp_vfs_spiffs_unregister(const char* partition_label);

        该函数的形参描述,如下表所示:

形参描述
param partition_label指向分区表的指针,分区表名称

表 38.3.2.6 函数 esp_vfs_spiffs_unregister ()形参描述

        该函数的返回值描述,如下表所示:

返回值描述
ESP_OK返回: 0,配置成功
ESP_ERR_INVALID_STATE已注销

表 38.3.2.7 函数 esp_vfs_spiffs_unregister ()返回值描述

38.3.3 SPIFFS 驱动解析

        在IDF版的StandardExampleIDF(v5.3.x)\27_spiffs例程中,在分区表中添加 了 SPIFFS 的内容 , 27_spiffs\components\BSP 路径下并无新的驱动文件增加。分区表内容如下:

Name

Type

SubType

Offset

Size

Flags

nvs

data

nvs

0x9000

0x6000

phy_init

data

phy

0xf000

0x1000

factory

app

factory

0x10000

0x1F0000

vfs

data

fat

0x200000

0xA00000

storage

data

spiffs

0xC00000

0x400000

(1)my_spiffs.h

#define DEFAULT_FD_NUM          5           /* 默认最大可打开文件数量 */
#define DEFAULT_MOUNT_POINT     "/spiffs"   /* 文件系统名称 *//* 函数声明 */
esp_err_t spiffs_init(char *partition_label, char *mount_point, size_t max_files);      /* spiffs初始化 */

(2)my_spiffs.c

static const char *spiffs_tag = "spiffs";/*** @brief       spiffs初始化* @param       partition_label:分区表的分区名称* @param       mount_point:文件系统关联的文件路径前缀* @param       max_files:可以同时打开的最大文件数* @retval      ESP_OK:成功; ESP_FAIL:失败*/
esp_err_t spiffs_init(char *partition_label, char *mount_point, size_t max_files)
{size_t total = 0;   /* SPIFFS总容量 */size_t used = 0;    /* SPIFFS已使用的容量 */esp_vfs_spiffs_conf_t spiffs_conf = {           /* 配置spiffs文件系统的参数 */.base_path              = mount_point,      /* 磁盘路径,比如"0:","1:" */.partition_label        = partition_label,  /* 分区表的分区名称 */.max_files              = max_files,        /* 最大可同时打开的文件数 */.format_if_mount_failed = true,             /* 挂载失败则格式化文件系统 */};esp_err_t ret = esp_vfs_spiffs_register(&spiffs_conf);  /* 初始化和挂载SPIFFS分区 */if (ret != ESP_OK){if (ret == ESP_FAIL){ESP_LOGE(spiffs_tag, "Failed to mount or format filesystem");}else if (ret == ESP_ERR_NOT_FOUND){ESP_LOGE(spiffs_tag, "Failed to find SPIFFS partition");}else{ESP_LOGE(spiffs_tag, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));}return ESP_FAIL;}ret = esp_spiffs_info(spiffs_conf.partition_label, &total, &used);  /* 获取SPIFFS的总容量和已使用的容量 */if (ret != ESP_OK){ESP_LOGI(spiffs_tag, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret));}else{ESP_LOGI(spiffs_tag, "Partition size: total: %d Bytes, used: %d Bytes", total, used);}return ret;
}/*** @brief       注销spiffs* @param       partition_label:分区表的分区名称* @retval      ESP_OK:注销成功; 其他:失败*/
esp_err_t spiffs_deinit(char *partition_label)
{return esp_vfs_spiffs_unregister(partition_label);
}

38.3.4 CMakeLists.txt 文件

        打开本实验 BSP 下的 CMakeLists.txt 文件,其内容如下所示:        

set(src_dirsLEDMYIICXL9555MYSPISPILCDSPIFFS)set(include_dirsLEDMYIICXL9555MYSPISPILCDSPIFFS)set(requiresdriveresp_lcdspiffs)idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)

38.3.5 实验应用代码

        打开 main/main.c 文件,该文件定义了工程入口函数,名为 app_main。该函数代码如下。

#define WRITE_DATA              "ALIENTEK ESP32-S3"     /* 写入数据 *//*** @brief       测试spiffs* @param       无* @retval      无*/
void spiffs_test(void)
{ESP_LOGI("spiffs_test", "Opening file");FILE *file_obj = fopen("/spiffs/hello.txt", "w");   /* 建立一个名为/spiffs/hello.txt的只写文件 */if (file_obj == NULL){ESP_LOGE("spiffs_test", "Failed to open file for writing");}fprintf(file_obj, WRITE_DATA);      /* 写入字符 */fclose(file_obj);                   /* 关闭文件 */ESP_LOGI("spiffs_test", "File written");/* 重命名之前检查目标文件是否存在 */struct stat st;if (stat("/spiffs/foo.txt", &st) == 0)  /* 获取文件信息,获取成功返回0 */{/*  从文件系统中删除一个名称。如果名称是文件的最后一个连接,并且没有其它进程将文件打开,名称对应的文件会实际被删除。 */unlink("/spiffs/foo.txt");}/* 重命名创建的文件 */ESP_LOGI("spiffs_test", "Renaming file");if (rename("/spiffs/hello.txt", "/spiffs/foo.txt") != 0){ESP_LOGE("spiffs_test", "Rename failed");}/* 打开重命名的文件并读取 */ESP_LOGI("spiffs_test", "Reading file");file_obj = fopen("/spiffs/foo.txt", "r");if (file_obj == NULL){ESP_LOGE("spiffs_test", "Failed to open file for reading");}char line[64];fgets(line, sizeof(line), file_obj);    /* 从指定的流中读取数据 */fclose(file_obj);char *pos = strchr(line, '\n'); /* 指针pos指向第一个找到‘\n’ */if (pos){*pos = '\0';                /* 将‘\n’替换为‘\0’ */}ESP_LOGI("spiffs_test", "Read from file: '%s'", line);spilcd_show_string(110, 130, 200, 16, 16, line, BLUE);
}/*** @brief       程序入口* @param       无* @retval      无*/
void app_main(void)
{esp_err_t ret;ret = nvs_flash_init();     /* 初始化NVS */if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND){ESP_ERROR_CHECK(nvs_flash_erase());ESP_ERROR_CHECK(nvs_flash_init());}led_init();                 /* LED初始化 */my_spi_init();              /* SPI初始化 */myiic_init();               /* MYIIC初始化 */xl9555_init();              /* XL9555初始化 */spilcd_init();              /* SPILCD初始化 */ESP_ERROR_CHECK(spiffs_init("storage", DEFAULT_MOUNT_POINT, DEFAULT_FD_NUM));    /* SPIFFS初始化 */spilcd_show_string(30, 50,  200, 16, 16, "ESP32-S3", RED);spilcd_show_string(30, 70,  200, 16, 16, "SPIFFS TEST", RED);spilcd_show_string(30, 90,  200, 16, 16, "ATOM@ALIENTEK", RED);spilcd_show_string(30, 130, 200, 16, 16, "Read Text:", RED);spiffs_test();              /* SPIFFS测试 */while (1){LED0_TOGGLE();vTaskDelay(pdMS_TO_TICKS(500));}
}

        在 SPIFFS 驱动中,首先初始化并挂载了一个 SPIFFS 分区,然后使用 POSIX 和 C 库 API 写入和读取数据。

        可以看到,本实验的应用代码中,在一系列初始化之后,配置 spiffs文件系统各个参数,再
建立一个名为/spiffs/hello.txt 的只写文件, LED 闪烁表明程序正在运行。

38.4 下载验证

        在完成编译和烧录操作后,在指定区域新建 hello.txt 文件,然后对这文件进行读写操作。

图 38.4.1 程序运行效果图

http://www.dtcms.com/a/442342.html

相关文章:

  • Seata 与 Redisson从底层到实战
  • 如何将wsl安装的Ubuntu系统从C盘移到D盘?
  • 怎么用阿里云做网站如何开发游戏
  • 网站服务器费用免费生成ppt的网站
  • 自动驾驶中的传感器技术62——USS(8)
  • AI时代数据存储和数据恢复 | 数据恢复损坏文件修复经验建议
  • 淄博网站制作定制中国寰球工程公司
  • MTK调试- 工程模式配置
  • 黑龙江生产建设兵团知识网站商城系统平台开发
  • 做麻将网站即墨网站设计
  • 网络请求完整指南:从零开始理解前端数据交互
  • Coze源码分析-资源库-编辑知识库-前端源码-核心逻辑/API
  • 【解决】Springboot+Mybatis数据分表后前端如何根据条件映射到对应子表中查询数据?!
  • 小迪自用web笔记53
  • 芜湖做网站哪个公司好网页设计基础的课程介绍
  • 网络营销郑州网站搭建方案开发 网站 团队
  • 做网站联系电话wordpress相册分类
  • win10/11 下载安装git教程 简单版
  • STM32 UART篇
  • 直播网站是怎么做的万网是做网站的吗
  • Python自动连接已保存WiFi
  • 数据结构二叉树
  • 我们做网站 出教材 办育心经做外贸必须建网站吗
  • GLUE任务
  • 【VS2022】LNK assimp64.lib找不到文件_openframework
  • 【架构艺术】构建变更风险防控能力市场的一些经验
  • 代做网站推广的公司wix做网站手机乱了
  • 操作系统应用开发(二十一)RustDesk 域名访问故障—东方仙盟筑基期
  • 做网站的都是直男癌吗创业做网站
  • JPA/Hibernate 批量插入实战:告别低效,实现真正的 MySQL 批量写入