博客 / 詳情

返回

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

第三十八章 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文件,然後對這文件進行讀寫操作
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函數解析

SPIFFS涉及到的文件並不算多,主要調用到了C庫的函數,關於C庫的函數我們在這裏就不過多介紹了,主要介紹一下調用到ESP32 IDF庫中的函數。
1,註冊裝載SPIFFS
該函數使用給定的路徑前綴將SPIFFS註冊並裝載到VFS,其函數原型如下所示:

esp_err_tesp_vfs_spiffs_register(constesp_vfs_spiffs_conf_t * conf);

該函數的形參描述,如下表所示:

表38.3.2.1 函數esp_vfs_spiffs_register ()形參描述
該函數的返回值描述,如下表所示:

表38.3.2.2 函數esp_vfs_spiffs_register ()返回值描述
該函數使用esp_vfs_spiffs_conf_t類型的結構體變量傳入,該結構體的定義如下所示:

表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);

該函數的形參描述,如下表所示:

表38.3.2.4 函數esp_spiffs_info ()形參描述
該函數的返回值描述,如下表所示:

表38.3.2.5 函數esp_spiffs_info ()返回值描述
3,註銷和卸載SPIFFS
該函數從VFS註銷和卸載SPIFFS,其函數原型如下所示:

esp_err_tesp_vfs_spiffs_unregister(const char*partition_label);

該函數的形參描述,如下表所示:

表38.3.2.6 函數esp_vfs_spiffs_unregister ()形參描述
該函數的返回值描述,如下表所示:

表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      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文件,然後對這文件進行讀寫操作。

圖38.4.1 程序運行效果圖

user avatar whoru 頭像
1 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.