第三十八章 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 程序運行效果圖