SOC-ESP32S3部分:23-文件系统

飞书文档https://x509p6c8to.feishu.cn/wiki/SXf5w6seIijVVskvic5cNT2wng4

目前,ESP-IDF 框架支持三种文件系统。

SPIFFS(SPI Flash File System)

  • 简介:SPIFFS 是专门为 SPI NOR Flash 设备设计的轻量级文件系统,适用于资源受限的嵌入式系统,在 ESP32 开发中较为常用。
  • 特点
  • 轻量级:占用资源少,对内存和处理能力的要求较低,适合在资源有限的 ESP32 设备上运行。
  • 磨损均衡:支持基本的磨损均衡功能,能够均匀地将数据写入 Flash 芯片的各个块,延长 Flash 的使用寿命。
  • 简单易用:API 相对简单,易于集成到项目中,开发人员可以方便地进行文件的读写操作。
  • 不支持大文件:由于其设计初衷是用于存储小文件,对于大文件的处理能力有限,且文件系统的最大容量也受到一定限制。
  • 适用场景:适合存储配置文件、小图片、脚本等小文件,例如存储设备的配置信息、简单的网页文件等。

FATFS

  • 简介:FATFS 是一个通用的 FAT 文件系统模块,遵循 ANSI C 标准,与平台无关。ESP-IDF 可以通过移植 FATFS 来支持 FAT12、FAT16 和 FAT32 文件系统。
  • 特点
  • 兼容性好:FAT 文件系统是一种广泛使用的文件系统格式,在各种操作系统(如 Windows、Linux 等)中都能很好地支持,方便与其他设备进行数据交互。
  • 支持大文件:能够处理较大的文件和文件夹,适用于需要存储大文件的场景。
  • 功能丰富:支持文件的创建、删除、读写、重命名等常见操作,还支持文件权限和目录结构。
  • 资源占用较多:相比于 SPIFFS,FATFS 需要更多的内存和处理能力来运行,对系统资源有一定的要求。
  • 适用场景:适合需要与其他设备共享数据、存储大文件的场景,例如存储多媒体文件(如音频、视频)、大型配置文件等。

LittleFS

  • 简介:LittleFS 是一种专为嵌入式系统设计的日志结构文件系统,旨在提供高性能、可靠性和低资源占用。
  • 特点
  • 高性能:采用日志结构设计,具有较高的写入性能和随机访问性能,能够快速地进行文件的读写操作。
  • 可靠性强:支持数据的原子写入和崩溃恢复功能,在系统异常断电或崩溃的情况下,能够保证数据的完整性和一致性。
  • 磨损均衡:提供了更高级的磨损均衡算法,能够更有效地延长 Flash 芯片的使用寿命。
  • 资源占用适中:相比于 FATFS,LittleFS 的资源占用相对较少,但比 SPIFFS 略高。
  • 适用场景:适合对文件系统性能和可靠性要求较高的场景,例如存储实时数据、重要的配置文件等。

课程主要讲解 SPIFFS ,其它文件系统的使用都是类似的,很容易扩展。

SPIFFS 是一个用于 SPI NOR flash 设备的嵌入式文件系统,支持磨损均衡、文件系统一致性检查等功能。

SPIFFS NVS 的区别

SPIFFS(Serial Peripheral Interface Flash File System)和 NVS(Non-Volatile Storage)都是 ESP32-S3 的存储系统,但它们有一些关键的区别。

NVS 在 SPI NOR flash 上实现了一个有容错性,和磨损均衡功能的键值对存储。

NVS 可以存储一些 PHY 初始化数据,也可以存储其他数据,一些断电存储的数据建议放在这里。

总的来说,SPIFFS 更适合用于存储文件,而 NVS 更适合用于存储键值对数据。具体使用哪种存储系统,取决于应用需求。

1.1**、创建分区表**

同理,因为文件系统需要用到Flash存储,所以也得进行分区表的创建

复制代码
# Name,   Type, SubType, Offset,  Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs,      data, nvs,     ,        0x6000,
phy_init, data, phy,     ,        0x1000,
factory,  app,  factory, ,        1M,
storage,  data, spiffs,  ,        0xF0000,

然后通过idf.py menuconfig配置自定义分区表

2.1**、注册** & 格式化文件系统

有了分区后,就可以进行文件系统的注册函数esp_vfs_spiffs_register

复制代码
esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t *conf);
参数
const esp_vfs_spiffs_conf_t *conf:
指向 esp_vfs_spiffs_conf_t 结构体的指针,包含 SPIFFS 文件系统的配置信息。

返回值
esp_err_t
表示函数执行的结果,通常为以下几种情况:
ESP_OK: 成功初始化并挂载 SPIFFS 文件系统。
ESP_FAIL: 挂载或格式化失败。
ESP_ERR_NOT_FOUND: 未找到 SPIFFS 分区。
ESP_ERR_NO_MEM: 内存不足,无法初始化文件系统。
ESP_ERR_INVALID_ARG: 输入参数无效。
其他可能的错误代码,具体取决于底层实现。

esp_vfs_spiffs_conf_t 结构体用于配置 SPIFFS(SPI Flash File System)文件系统的参数。通过设置这些参数,可以控制 SPIFFS 文件系统的挂载行为、分区选择、最大同时打开的文件数以及挂载失败时的处理方式。

复制代码
字段说明
base_path: 文件系统的挂载路径前缀。所有通过该文件系统访问的文件路径都将基于此路径。。
例如,如果 base_path 设置为 "/spiffs",则文件 hello.txt 的完整路径将是 "/spiffs/hello.txt"。

partition_label: 可选参数,指定要使用的 SPIFFS 分区的标签,如果有多个 SPIFFS 分区,可以通过设置 partition_label 来指定具体的分区。
如果不设置,则默认使用第一个子类型为 spiffs 的分区。

max_files: 最大同时打开的文件数。

format_if_mount_failed: 如果为 true,则在挂载失败时自动格式化文件系统。
说明: 如果挂载 SPIFFS 分区失败(例如分区损坏或未初始化),
设置此标志为 true 可以自动格式化分区,使其重新可用。

使用参考:

复制代码
    // 配置 SPIFFS 文件系统
    esp_vfs_spiffs_conf_t conf = {
        .base_path = "/spiffs",            // 文件系统的挂载路径
        .partition_label = NULL,           // 使用默认的 SPIFFS 分区标签
        .max_files = 5,                    // 最大同时打开的文件数
        .format_if_mount_failed = true     // 如果挂载失败,则格式化文件系统
    };
    // 使用上述配置初始化并挂载 SPIFFS 文件系统
    esp_err_t ret = esp_vfs_spiffs_register(&conf);

    // 处理挂载结果
    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount or format filesystem"); // 挂载或格式化失败
        } else if (ret == ESP_ERR_NOT_FOUND) {
            ESP_LOGE(TAG, "Failed to find SPIFFS partition"); // 未找到 SPIFFS 分区
        } else {
            ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); // 初始化 SPIFFS 失败
        }
        return;
    }

3.1**、获取文件系统的信息**

注册完成后,就可以使用esp_spiffs_info 函数用于获取指定 SPIFFS 分区的总大小和已使用的空间大小。

复制代码
esp_err_t esp_spiffs_info(const char* partition_label, size_t* total, size_t* used);
参数
const char* partition_label:
SPIFFS 分区的标签。如果设置为 NULL,则使用默认的 SPIFFS 分区。

size_t* total:
用于存储 SPIFFS 分区的总大小(以字节为单位)。

size_t* used:
用于存储 SPIFFS 分区已使用的空间大小(以字节为单位)。

使用参考:

复制代码
    // 获取 SPIFFS 分区的信息
    size_t total = 0, used = 0;
    ret = esp_spiffs_info(conf.partition_label, &total, &used);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s). Formatting...", esp_err_to_name(ret));
        // 获取分区信息失败,格式化
        esp_spiffs_format(conf.partition_label);
        return;
    } else {
        ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); // 分区大小信息
    }

4.1**、检查文件系统的完整性**

最后,调用esp_spiffs_check 函数用于检查 SPIFFS 文件系统的完整性。具体来说:

  • 它会扫描指定的 SPIFFS 分区,查找并修复文件系统中的潜在问题。

  • 这个函数可以帮助检测和修复由于突然断电、程序异常终止等原因导致的文件系统损坏。

  • 检查过程包括验证文件系统结构、索引节点、数据块等的正确性。

    使用参考
    // 检查报告的分区大小信息的一致性
    if (used > total) {
    ESP_LOGW(TAG, "Number of used bytes cannot be larger than total. Performing SPIFFS_check()."); // 已使用字节数不能大于总字节数,执行 SPIFFS_check
    //如果 ESP32-S3 在文件系统操作期间断电,可能会导致 SPIFFS 损坏。但是仍可通过 esp_spiffs_check 函数恢复文件系统。
    ret = esp_spiffs_check(conf.partition_label);
    if (ret != ESP_OK) {
    ESP_LOGE(TAG, "SPIFFS_check() failed (%s)", esp_err_to_name(ret)); // SPIFFS_check 失败
    return;
    } else {
    ESP_LOGI(TAG, "SPIFFS_check() successful"); // SPIFFS_check 成功
    }
    }

5.1**、创建文件、读写操作**

如果到这里都没问题,我们就可以用c的文件操作函数进行文件的创建、写入、读取等操作了,最终代码如下:

复制代码
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_spiffs.h"

static const char *TAG = "example";

void app_main(void)
{
    // 初始化 SPIFFS
    ESP_LOGI(TAG, "Initializing SPIFFS");

    // 配置 SPIFFS 文件系统
    esp_vfs_spiffs_conf_t conf = {
        .base_path = "/spiffs",            // 文件系统的挂载路径
        .partition_label = NULL,           // 使用默认的 SPIFFS 分区标签
        .max_files = 5,                    // 最大同时打开的文件数
        .format_if_mount_failed = true     // 如果挂载失败,则格式化文件系统
    };

    // 使用上述配置初始化并挂载 SPIFFS 文件系统
    esp_err_t ret = esp_vfs_spiffs_register(&conf);

    // 处理挂载结果
    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount or format filesystem"); // 挂载或格式化失败
        } else if (ret == ESP_ERR_NOT_FOUND) {
            ESP_LOGE(TAG, "Failed to find SPIFFS partition"); // 未找到 SPIFFS 分区
        } else {
            ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); // 初始化 SPIFFS 失败
        }
        return;
    }

    // 获取 SPIFFS 分区的信息
    size_t total = 0, used = 0;
    ret = esp_spiffs_info(conf.partition_label, &total, &used);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s). Formatting...", esp_err_to_name(ret));
        // 获取分区信息失败,格式化
        esp_spiffs_format(conf.partition_label);
        return;
    } else {
        ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); // 分区大小信息
    }

    // 检查报告的分区大小信息的一致性
    if (used > total) {
        ESP_LOGW(TAG, "Number of used bytes cannot be larger than total. Performing SPIFFS_check()."); // 已使用字节数不能大于总字节数,执行 SPIFFS_check
        //如果 ESP32-S3 在文件系统操作期间断电,可能会导致 SPIFFS 损坏。但是仍可通过 esp_spiffs_check 函数恢复文件系统。
        ret = esp_spiffs_check(conf.partition_label);
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "SPIFFS_check() failed (%s)", esp_err_to_name(ret)); // SPIFFS_check 失败
            return;
        } else {
            ESP_LOGI(TAG, "SPIFFS_check() successful"); // SPIFFS_check 成功
        }
    }

    // 使用 POSIX 和 C 标准库函数来处理文件
    // 首先创建一个文件
    ESP_LOGI(TAG, "Opening file"); // 打开文件
    FILE* f = fopen("/spiffs/hello.txt", "w");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for writing"); // 打开文件失败
        return;
    }
    fprintf(f, "Hello World!\n"); // 写入文件
    fclose(f);
    ESP_LOGI(TAG, "File written"); // 文件写入成功

    // 打开文件进行读取
    ESP_LOGI(TAG, "Reading file"); // 读取文件
    f = fopen("/spiffs/hello.txt", "r");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for reading"); // 打开文件失败
        return;
    }
    char line[64];
    fgets(line, sizeof(line), f); // 读取文件内容
    fclose(f);
    // 去掉换行符
    char* pos = strchr(line, '\n');
    if (pos) {
        *pos = '\0';
    }
    ESP_LOGI(TAG, "Read from file: '%s'", line); // 读取到的内容

    // 完成所有操作,卸载分区并禁用 SPIFFS
    esp_vfs_spiffs_unregister(conf.partition_label);
    ESP_LOGI(TAG, "SPIFFS unmounted"); // SPIFFS 卸载成功
}

首次启动时,因为需要初始化挂载格式化文件系统,所以会阻塞10s左右,这是正常的

扩展:如何把本地文件烧录到板卡文件系统中?

spiffs_create_partition_image 是一个用于将文件系统中的文件打包成 SPIFFS 镜像并烧录到 ESP32 Flash中的命令,可以在程序烧录阶段一同将自定义的SPIFFS 文件镜像烧录到开发板。

使用方法很简单,例如,我在电脑创建一个txt文件夹,文件夹内部创建一个文件hello1.txt

demo/txt/hello1.txt

复制代码
this is hello1 txt

然后在demo/main/CMakeLists.txt中,就可以添加spiffs_create_partition_image指令

指令参数如下

复制代码
spiffs_create_partition_image(分区名 "path/to/files" FLASH_IN_PROJECT)

所以内容如下:

复制代码
spiffs_create_partition_image(storage "../txt" FLASH_IN_PROJECT)

这句指令的意思是将工程下../txt 目录中的文件打包成 SPIFFS 镜像,并且。

最终用代码打开

复制代码
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_spiffs.h"

static const char *TAG = "example";

void app_main(void)
{
    // 初始化 SPIFFS
    ESP_LOGI(TAG, "Initializing SPIFFS");

    // 配置 SPIFFS 文件系统
    esp_vfs_spiffs_conf_t conf = {
        .base_path = "/spiffs",            // 文件系统的挂载路径
        .partition_label = NULL,           // 使用默认的 SPIFFS 分区标签
        .max_files = 5,                    // 最大同时打开的文件数
        .format_if_mount_failed = true     // 如果挂载失败,则格式化文件系统
    };

    // 使用上述配置初始化并挂载 SPIFFS 文件系统
    esp_err_t ret = esp_vfs_spiffs_register(&conf);

    // 处理挂载结果
    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount or format filesystem"); // 挂载或格式化失败
        } else if (ret == ESP_ERR_NOT_FOUND) {
            ESP_LOGE(TAG, "Failed to find SPIFFS partition"); // 未找到 SPIFFS 分区
        } else {
            ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); // 初始化 SPIFFS 失败
        }
        return;
    }

    // 获取 SPIFFS 分区的信息
    size_t total = 0, used = 0;
    ret = esp_spiffs_info(conf.partition_label, &total, &used);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s). Formatting...", esp_err_to_name(ret));
        // 获取分区信息失败,格式化
        esp_spiffs_format(conf.partition_label);
        return;
    } else {
        ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); // 分区大小信息
    }

    // 检查报告的分区大小信息的一致性
    if (used > total) {
        ESP_LOGW(TAG, "Number of used bytes cannot be larger than total. Performing SPIFFS_check()."); // 已使用字节数不能大于总字节数,执行 SPIFFS_check
        //如果 ESP32-S3 在文件系统操作期间断电,可能会导致 SPIFFS 损坏。但是仍可通过 esp_spiffs_check 函数恢复文件系统。
        ret = esp_spiffs_check(conf.partition_label);
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "SPIFFS_check() failed (%s)", esp_err_to_name(ret)); // SPIFFS_check 失败
            return;
        } else {
            ESP_LOGI(TAG, "SPIFFS_check() successful"); // SPIFFS_check 成功
        }
    }

    // 打开文件进行读取
    ESP_LOGI(TAG, "Reading file"); // 读取文件
    FILE* f = fopen("/spiffs/hello1.txt", "r");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for reading"); // 打开文件失败
        return;
    }
    char line[64];
    fgets(line, sizeof(line), f); // 读取文件内容
    fclose(f);
    // 去掉换行符
    char* pos = strchr(line, '\n');
    if (pos) {
        *pos = '\0';
    }
    ESP_LOGI(TAG, "Read from file: '%s'", line); // 读取到的内容

    // 完成所有操作,卸载分区并禁用 SPIFFS
    esp_vfs_spiffs_unregister(conf.partition_label);
    ESP_LOGI(TAG, "SPIFFS unmounted"); // SPIFFS 卸载成功
}
相关推荐
许有杨1 小时前
BKP(备份寄存器)和 RTC(实时时钟)
单片机·嵌入式硬件
iCxhust2 小时前
Prj09--8088单板机C语言8253产生1KHz方波(1)
c语言·开发语言·c++·单片机·嵌入式硬件·mcu
Moonnnn.3 小时前
【PCB设计】STM32开发板——原理图设计(电源部分)
笔记·stm32·单片机·嵌入式硬件·学习
Rousson3 小时前
硬件学习笔记--62 MCU的ECC功能简介
笔记·单片机·学习
Bob99983 小时前
Logitech (罗技)单通道、双通道与6通道 Unifying 接收器:USB ID、功能与实用性解析
java·网络·c++·python·stm32·单片机·嵌入式硬件
广药门徒5 小时前
对比ODR直接赋值的非原子操作和BSRR原子操作
stm32·单片机
小智学长 | 嵌入式15 小时前
SOC-ESP32S3部分:25-HTTP请求
开发语言·单片机·esp32
比特森林探险记16 小时前
ESP32与STM32
stm32·单片机·嵌入式硬件
limitless_peter17 小时前
相量法正弦稳态电路的分析(面向题目)
单片机·嵌入式硬件
爱出名的狗腿子17 小时前
vscode + cmake + ninja+ gcc 搭建MCU开发环境
ide·vscode·单片机·c·cmake·gcc·ninja