《ESP32-S3使用指南—IDF版 V1.6》第三十八章 SPIFFS实验

93 阅读8分钟

第三十八章 SPIFFS 实验

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

本章分为如下几个小节:

38.1 SPIFFS简介

38.2 硬件设计

38.3 程序设计

38.4 下载验证

38.1 SPIFFS 介绍

SPIFFS是一个用于嵌入式目标上的SPINOR flash设备的文件系统,并且有如下特点:

l  小目标,没有堆的少量RAM

l  只有大范围的数据块才能被擦除

l  擦除会将块中的所有位重置为1

l  写操作将1置0

l  0只能被擦除成1

l  磨损均衡

以上几点是SPIFFS的特点,下面则说明了SPIFFS具体能做些什么:

l  专门为低ram使用而设计

l  使用静态大小的ram缓冲区,与文件的数量无关

l  类可移植操作系统接口:打开、关闭、读、写、查找、统计等

l  它可以在任何NOR闪存上运行,不仅是SPI闪存,理论上也可以在微处理器的嵌入式闪存上运行

l  多个spiffs配置可以在相同的目标上运行—甚至可以在相同的SPI闪存设备上运行

l  实现静态磨损调平(也就是flash的寿命维护)

l  内置文件系统一致性检查

l  高度可配置的

38.2 硬件设计

38.2.1 例程功能

1.在nor flash指定区域新建holle.txt文件,然后对这文件进行读写操作

  1. LED闪烁,指示程序正在运行
38.2.2 硬件资源
  1. LED灯

LED -IO0

  1. XL9555

IIC_SDA-IO41

IIC_SCL-IO42

  1. SPILCD

CS-IO21

SCK-IO12

SDA-IO11

DC-IO40(在P5端口,使用跳线帽将IO_SET和LCD_DC相连)

PWR- IO1_3(XL9555)

RST- IO1_2(XL9555)

  1. SPIFFS
38.2.3 原理图

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

38.3 程序设计

38.3.1 程序流程图

程序流程图能帮助我们更好的理解一个工程的功能和实现的过程,对学习和设计工程有很好的主导作用。下面看看本实验的程序流程图:

                      

image002.png

图38.3.1.1 IIC_EXIO实验程序流程图

38.3.2 SPIFFS 函数解析

SPIFFS涉及到的文件并不算多,主要调用到了C库的函数,关于C库的函数我们在这里就不过多介绍了,主要介绍一下调用到ESP32 IDF库中的函数。

1 ,注册装载SPIFFS

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

esp_err_tesp_vfs_spiffs_register(constesp_vfs_spiffs_conf_t * conf);

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

QQ截图20250912170719.png

表38.3.2.1 函数esp_vfs_spiffs_register ()形参描述

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

QQ截图20250912170732.png

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

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

QQ截图20250912170744.png

表38.3.2.3 esp_vfs_spiffs_conf_t结构体参数值描述

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

2 ,获取SPIFFS 的信息

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


esp_err_tesp_spiffs_info(const char*partition_label,

                           size_t *total_bytes,

                           size_t *used_bytes);

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

QQ截图20250912170753.png

表38.3.2.4 函数esp_spiffs_info ()形参描述

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

QQ截图20250912170801.png

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

3 ,注销和卸载SPIFFS

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

esp_err_tesp_vfs_spiffs_unregister(const char*partition_label);

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

QQ截图20250912170810.png

表38.3.2.6 函数esp_vfs_spiffs_unregister ()形参描述

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

QQ截图20250912170817.png

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

38.3.3 SPIFFS 驱动解析

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

# ESP-IDFPartition Table

# 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, ,
38.3.4 CMakeLists.txt 文件

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

set(src_dirs

            IIC

            LCD

            LED

            SPI

            XL9555)

set(include_dirs

            IIC

            LCD

            LED

            SPI

            XL9555)

set(requires

            driver

            )

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。该函数代码如下。

i2c_obj_ti2c0_master;

#defineDEFAULT_FD_NUM          5

#define DEFAULT_MOUNT_POINT     "/spiffs"

#defineWRITE_DATA              "ALIENTEKESP32-S3\r\n"

static const char               *TAG = "spiffs";

/**

* @brief      spiffs初始化

* [@param](http://www.openedv.com/home.php?mod=space&uid=271674)      partition_label:分区表的分区名称

* @param      mount_point:文件系统关联的文件路径前缀

* @param      max_files:可以同时打开的最大文件数

* @retval     无

*/

esp_err_tspiffs_init(char *partition_label,char *mount_point,size_tmax_files)

{

    /* 配置spiffs文件系统各个参数 */

    esp_vfs_spiffs_conf_t conf = {

        .base_path= mount_point,

        .partition_label= partition_label,

        .max_files= max_files,

        .format_if_mount_failed= true,

    };

    /* 使用上面定义的设置来初始化和挂载SPIFFS文件系统 */

    esp_err_t ret_val =esp_vfs_spiffs_register(&conf);

    /* 判断SPIFFS挂载及初始化是否成功 */

    if (ret_val!= ESP_OK)

    {

        if (ret_val== ESP_FAIL)

        {

            printf("Failedto mount or format filesystem\n");

        }

        else if (ret_val== ESP_ERR_NOT_FOUND)

        {

            printf("Failedto find SPIFFS partition\n");

        }

        else

        {

           printf("Failed to initialize SPIFFS(%s)\n",esp_err_to_name(ret_val));

        }

        returnESP_FAIL;

    }

    /* 打印SPIFFS存储信息 */

    size_t total = 0, used = 0;

    ret_val =esp_spiffs_info(conf.partition_label, &total, &used);

    if (ret_val!= ESP_OK)

    {

        ESP_LOGE(TAG,

      "Failed to get SPIFFS partitioninformation(%s)",

       esp_err_to_name(ret_val));

    }

    else

    {

        ESP_LOGE(TAG, "Partitionsize: total: %d, used: %d", total, used);

    }

    return ret_val;

}

/**

* @brief      注销spiffs初始化

* @param      partition_label:分区表标识

* @retval     无

*/

esp_err_tspiffs_deinit(char *partition_label)

{

    returnesp_vfs_spiffs_unregister(partition_label);

}

/**

* @brief      测试spiffs

* @param      无

* @retval     无

*/

voidspiffs_test(void)

{

    ESP_LOGI(TAG, "Openingfile");

    /* 建立一个名为/spiffs/hello.txt的只写文件 */

    FILE* f = fopen("/spiffs/hello.txt", "w");

    if (f == NULL)

    {

        ESP_LOGE(TAG, "Failedto open file for writing");

        return;

    }

    /* 写入字符 */

    fprintf(f,WRITE_DATA);

    fclose(f);

    ESP_LOGI(TAG, "Filewritten");

    /* 重命名之前检查目标文件是否存在 */

    struct stat st;

    if (stat("/spiffs/foo.txt", &st) == 0) /* 获取文件信息,获取成功返回0 */

    {

        /*  从文件系统中删除一个名称。

            如果名称是文件的最后一个连接,并且没有其它进程将文件打开,

            名称对应的文件会实际被删除。 */

        unlink("/spiffs/foo.txt");

    }

    /* 重命名创建的文件 */

    ESP_LOGI(TAG, "Renamingfile");

    if (rename("/spiffs/hello.txt", "/spiffs/foo.txt") != 0)

    {

        ESP_LOGE(TAG, "Renamefailed");

        return;

    }

    /* 打开重命名的文件并读取 */

    ESP_LOGI(TAG, "Readingfile");

    f = fopen("/spiffs/foo.txt", "r");

    if (f == NULL)

    {

        ESP_LOGE(TAG, "Failedto open file for reading");

        return;

    }

    char line[64];

    fgets(line, sizeof(line), f);

    fclose(f);

   

    char* pos =strchr(line, '\n'); /* 指针pos指向第一个找到‘\n’*/

    if (pos)

    {

        *pos = '\0';                /* 将‘\n’替换为‘\0’ */

    }

    ESP_LOGI(TAG, "Readfrom file: '%s'", line);

    lcd_show_string(90, 110, 200, 16, 16, line, RED);

}

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

i2c_obj_ti2c0_master;

/**

* @brief      程序入口

* @param      无

* @retval     无

*/

voidapp_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());

        ret =nvs_flash_init();

    }

    ESP_ERROR_CHECK(ret);

    led_init();                                               /* LCD初始化 */

    i2c0_master =iic_init(I2C_NUM_0);                         /* 初始化IIC0 */

    spi2_init();                                               /*SPI初始化 */

    xl9555_init(i2c0_master);                                  /*XL9555初始化 */

    lcd_init();                                               /* LCD初始化 */

    spiffs_init("storage",DEFAULT_MOUNT_POINT, DEFAULT_FD_NUM);/*SPIFFS初始化*/

    /* 显示实验信息 */

    lcd_show_string(10, 50, 200, 16, 16, "ESP32", RED);

    lcd_show_string(10, 70, 200, 16, 16, "SPIFFSTEST", RED);

    lcd_show_string(10, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);

    lcd_show_string(10, 110, 200, 16, 16, "Readfile:", BLUE);

    spiffs_test();                                             /*SPIFFS测试 */

    while (1)

    {

        LED_TOGGLE();

        vTaskDelay(500);

    }

}

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

38.4 下载验证

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

image003.png

图38.4.1 程序运行效果图