概述
一個為微控制器設計的小故障安全檔案系統。
- 掉電恢複能力: 設計用于處理随機電源故障。所有檔案操作都有很強的寫時拷貝保證,如果斷電,檔案系統将恢複到上一次已知的良好狀态。
- 動态磨損均衡: 設計考慮到閃存,并提供動态塊磨損均衡。此外,littlefs可以檢測壞塊并在它們周圍工作。
- 有限RAM/ROM: 被設計為使用少量記憶體。RAM的使用是嚴格限制的,這意味着RAM的使用不會随着檔案系統的增長而改變。檔案系統不包含無界遞歸,動态記憶體僅限于可靜态提供的可配置緩沖區。
在lfs.h的評論中可以找到詳細的文檔(或者至少是目前可用的盡可能多的細節)。 littlefs采用了一種配置結構,定義了檔案系統的運作方式。配置結構為檔案系統提供了塊裝置操作和次元、可調整的參數(用于在性能上權衡記憶體使用情況)以及可選的靜态緩沖區(如果使用者希望避免動态記憶體)。 littlefs的狀态存儲在lfs\t類型中,由使用者配置設定,允許同時使用多個檔案系統。使用lfs\u t和configuration struct,使用者可以格式化塊裝置或挂載檔案系統。 挂載後,littlefs提供了一整套類似POSIX的檔案和目錄功能,但檔案系統結構的配置設定必須由使用者提供。 所有POSIX操作,比如remove和rename,都是原子的,即使在斷電的情況下也是如此。此外,在對檔案調用sync或close之前,檔案更新實際上不會送出到檔案系統。
littlefs檔案系統下載下傳位址
Tags · littlefs-project/littlefs · GitHub
下載下傳最新的即可。
源碼下載下傳下來後,隻需要其中的4個檔案:lfs.c、lfs.h、lfs_util.c、lfs_util.h,其他檔案都是用于測試的Demo例程,調試過程中需要參考的話可以了解一下。
移植步驟
1、将lfs.c、lfs.h、lfs_util.c、lfs_util.h這4個檔案放到一個建立的檔案夾,并将其添加到工程中;sos_lfs.c這個檔案是我對lfs.c接口二次封裝生成的檔案,這個檔案根據你自己喜好命名,适配即可。
2、适配lfs_util.h
lfs_util.h中需要修改申請記憶體的方式,如果你代碼中對malloc有修改過,就需要修改為你動态申請記憶體的方式
原版
我的系統移植了FreeRTOS系統,動态申請記憶體是在FreeRTOS的總記憶體中申請,是以我需要修改此記憶體申請的方式
3、适配lfs_config結構體的内容
結構體成員變量 | 說明 |
void *context | 用于給底層塊裝置傳參,比如:要寫入的檔案資訊、位置、大小、是否有壞塊等。該參數的資料結構由底層塊裝置驅動來定義。如果不需要傳參的話可以不指派 |
int (*read) | 塊裝置讀取函數指針 |
int (*prog) | 塊裝置寫入函數指針,寫入前必須保證該寫入塊已經被擦除過 |
int (*erase) | 塊裝置擦除函數指針 |
int (*sync) | 塊裝置同步函數指針,若塊裝置不需要同步操作,可以直接傳回成功 |
lfs_size_t read_size | 最小讀取位元組數,所有的讀取操作位元組數必須是它的整數倍 |
lfs_size_t prog_size | 最小寫入位元組數,所有的寫入操作位元組數必須是它的整數倍 |
lfs_size_t block_size | 擦除塊操作的位元組數,該選項不影響 RAM 消耗,可以比實體擦除尺寸大,但是每個檔案至少占用一個塊,必須是讀取和寫入操作位元組數的整數倍 |
lfs_size_t block_count | 裝置上可擦除塊的數量,block_size x block_count = 塊裝置容量 |
int32_t block_cycles | littlefs 系統删除中繼資料日志并将中繼資料移動到另一個塊之前的擦除周期數。建議取值範圍為 100 ~ 1000,較大數值有較好的性能但是會導緻磨損分布不一緻,若取值 -1 的話,即為禁用塊級磨損均衡 |
lfs_size_t cache_size | 塊緩存大小,每個緩存都會在 RAM 中緩沖一部分塊資料,littlefs 系統需要一個讀取緩存、一個寫入緩存,每個檔案還需要一個額外的緩存。更大的緩存可以通過存儲更多的資料并降低磁盤通路數量等手段來提高性能 |
lfs_size_t lookahead_size | 先行緩沖大小,更大的先行緩沖可以提高配置設定操作中可被發現的塊數量。即配置設定塊時每次配置設定多少個塊,16就表示每次配置設定16個塊。先行緩沖以 bit 位形式來存儲,故 RAM 中的一個位元組對應8個塊。該值必須是8的整數倍 |
int (*lock) | 塊裝置加鎖函數指針,需定義 宏才可用,若不需要多線程操作可不指派 |
int (*unlock) | 塊裝置解鎖函數指針,需定義 宏才可用,若不需要多線程操作可不指派 |
void *read_buffer | 可選參數。讀取靜态緩沖區指針。若目标MCU不支援動态配置設定堆記憶體的話,就需要定義這個參數,用于在棧内配置設定靜态變量 |
void *prog_buffer | 可選參數。寫入靜态緩沖區指針。若目标MCU不支援動态配置設定堆記憶體的話,就需要定義這個參數,用于在棧内配置設定靜态變量 |
void *lookahead_buffer | 可選參數。先行配置設定靜态緩沖區指針,需要32bit對齊。若目标MCU不支援動态配置設定堆記憶體的話,就需要定義這個參數,用于在棧内配置設定靜态變量 |
下面是我的lfs_config配置表
3.1、block_count我用了宏定義,這個大小根據你配置設定記憶體大小給定,我配置設定的littlefs的記憶體大小是2600K,是以INTERNALFLASH_LFS_MAXSIZE是650,其中cache_size的大小和block_size、read_size的大小成比例,否則運作lfs的時候就會出現錯誤的問題
3.2、在單線程的情況下lock和unlock可以不适配,多線程的時候,建議适配,因為會出現搶占資源,導緻不必要的錯誤。
3.3、.read、.prog、.erase 、.sync 、.lock 、.unlock 适配
注:因為flash中有存放了你的程式,不是整塊晶片都用作littlefs檔案系統,是以INTERNALFLASH_STARTADDR是我記憶體已經規劃好相應的位址,需要偏移了指定大小的位址之後才開始被littlefs使用的;
.read
.prog
.erase
.sync
.lock
.unlock
4、測試接口是否适配OK?
4.1、lfs初始化
4.2、測試函數接口
/*
* @brief : 測試little fs系統接口用例
* @param :
* @date : 2022.07.07
* @author:
* @remark:
*/
void lfs_test(void)
{
int err;
/*****************************内部檔案系統測試***************************************/
printf("*****************内部檔案系統測試*********************\r\n");
// mount the filesystem
err = lfs_mount(&lfs_sosInternalFlash, &intCfg);
// reformat if we can't mount the filesystem
// this should only happen on the first boot
if(err){
printf("err2=%d\r\n", err);
lfs_format(&lfs_sosInternalFlash, &intCfg);
lfs_mount(&lfs_sosInternalFlash, &intCfg);
}
// read current count
uint32_t boot_count1 = 0;
lfs_file_open(&lfs_sosInternalFlash, &lfs_file_internalFlash, "boot_count1", LFS_O_RDWR | LFS_O_CREAT);
lfs_file_read(&lfs_sosInternalFlash, &lfs_file_internalFlash, &boot_count1, sizeof(boot_count1));
printf("ID:%d\r\n", lfs_file_internalFlash.id);
// update boot count
boot_count1 += 1;
lfs_file_rewind(&lfs_sosInternalFlash, &lfs_file_internalFlash);
lfs_file_write(&lfs_sosInternalFlash, &lfs_file_internalFlash, &boot_count1, sizeof(boot_count1));
// remember the storage is not updated until the file is closed successfully
lfs_file_close(&lfs_sosInternalFlash, &lfs_file_internalFlash);
uint32_t txt1 = 0;
lfs_file_open(&lfs_sosInternalFlash, &lfs_file_internalFlash, "txt1", LFS_O_RDWR | LFS_O_CREAT);
lfs_file_read(&lfs_sosInternalFlash, &lfs_file_internalFlash, &txt1, sizeof(txt1));
printf("ID:%d\r\n", lfs_file_internalFlash.id);
// update boot count
txt1 += 1;
lfs_file_rewind(&lfs_sosInternalFlash, &lfs_file_internalFlash);
lfs_file_write(&lfs_sosInternalFlash, &lfs_file_internalFlash, &txt1, sizeof(txt1));
// remember the storage is not updated until the file is closed successfully
lfs_file_close(&lfs_sosInternalFlash, &lfs_file_internalFlash);
// release any resources we were using
lfs_unmount(&lfs_sosInternalFlash);
// print the boot count
printf("boot_count1: %d\r\n", boot_count1);
printf("txt1: %d\r\n", txt1);
printf("\r\n");
/*****************************外部檔案系統測試***************************************/
printf("*****************外部檔案系統測試*********************\r\n");
// mount the filesystem
err = lfs_mount(&lfs_sosExternalFlash, &extCfg);
// reformat if we can't mount the filesystem
// this should only happen on the first boot
if(err){
printf("err1=%d\r\n", err);
lfs_format(&lfs_sosExternalFlash, &extCfg);
lfs_mount(&lfs_sosExternalFlash, &extCfg);
}
// read current count
uint32_t boot_count2 = 0;
lfs_file_open(&lfs_sosExternalFlash, &lfs_file_externalFlash, "boot_count2", LFS_O_RDWR | LFS_O_CREAT);
lfs_file_read(&lfs_sosExternalFlash, &lfs_file_externalFlash, &boot_count2, sizeof(boot_count2));
printf("ID:%d\r\n", lfs_file_externalFlash.id);
// update boot count
boot_count2 += 1;
lfs_file_rewind(&lfs_sosExternalFlash, &lfs_file_externalFlash);
lfs_file_write(&lfs_sosExternalFlash, &lfs_file_externalFlash, &boot_count2, sizeof(boot_count2));
// remember the storage is not updated until the file is closed successfully
lfs_file_close(&lfs_sosExternalFlash, &lfs_file_externalFlash);
uint32_t txt2 = 0;
lfs_file_open(&lfs_sosExternalFlash, &lfs_file_externalFlash, "txt2", LFS_O_RDWR | LFS_O_CREAT);
lfs_file_read(&lfs_sosExternalFlash, &lfs_file_externalFlash, &txt2, sizeof(txt2));
printf("ID:%d\r\n", lfs_file_externalFlash.id);
// update boot count
txt2 += 1;
lfs_file_rewind(&lfs_sosExternalFlash, &lfs_file_externalFlash);
lfs_file_write(&lfs_sosExternalFlash, &lfs_file_externalFlash, &txt2, sizeof(txt2));
// remember the storage is not updated until the file is closed successfully
lfs_file_close(&lfs_sosExternalFlash, &lfs_file_externalFlash);
// release any resources we were using
lfs_unmount(&lfs_sosExternalFlash);
// print the boot count
printf("boot_count2: %d\r\n", boot_count2);
printf("txt2: %d\r\n", txt2);
printf("\r\n");
}
測試通過,讀寫和擦除都正常,littlefs算移植完成
5、注意事項
5.1、在建立檔案的時候,如果該檔案已經建立過了,你再次建立同樣的名字,沒有加LFS_O_EXCL屬性,檔案同樣會建立成功,但是不會傳回檔案已存在的錯誤碼,建立的次數越多,後面檔案系統記憶體會出現洩漏,最後導緻寫入檔案都會報記憶體不足的問題,需要注意
5.2、内外檔案系統需要自己注意函數的調用,不要混亂使用。
5.3、lfs_config配置表看自己的需要的大小适配,不過如果配置不對,很容易報錯,推薦用的我的配置表。
6、總結
這篇檔案斷斷續續寫的,有不足的地方希望一起探讨,後續有新的發現和注意事項,我會持續更新。