移植平台: 正點原子STM32F1精英版V1.41
MCU:STM32F103ZET6
SPI Falsh:W25Q128
LittleFS是ARM mbedOS的官方推薦文件系統,具有輕量級,掉電安全的特性。
參考文檔
文件系統移植
首先去Github上下載最新發型版本的LittleFs源碼,我下載的是V2.2.1版本,並閱讀README文檔了解移植詳情,因為README.md
文檔講解很有限,所以下面會在移植過程中做出補充。在工程中添加文件系統如下:
以上兩個文件在文件系統源碼根目錄下;
移植要點
- 對
struct lfs_config
的賦值; - LittleFS讀寫內存的分配函數指定;
- 使用
lfs_mount
掛在文件系統;
lfs_config結構體
lfs_config
為移植的重中之重,定義在lfs.h
中:
// Configuration provided during initialization of the littlefs
struct lfs_config {
// 用於傳遞信息給塊設備,便於塊設備驅動進行特定的處理,比如:告訴塊設備驅動
// 具體哪個范圍用於文件系統(傳參);
// 這個內容的數據結構由塊設備驅動來定義;
void *context;
// 從指定塊內讀數據
//
int (*read)(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
// 寫塊接口,用於將一段數據寫入到塊中,這個塊必須是已經被擦除的
//(文件系統會確保擦除)
int (*prog)(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
//擦除一個塊
int (*erase)(const struct lfs_config *c, lfs_block_t block);
// Sync the state of the underlying block device. Negative error codes
// are propogated to the user.(可以直接寫空)
int (*sync)(const struct lfs_config *c);
// 最小讀取字節數,所有的讀取操作字節數必須是它的倍數(影響內存消耗)
lfs_size_t read_size;
// 最小寫入字節數,所有的讀取操作字節數必須是它的倍數(影響內存消耗)
lfs_size_t prog_size;
//擦除塊字節數 不會影響內存消耗;這個數值可以比物理擦除地址大,但是這個數值應該盡可能小,
//因為每個文件至少占用一個塊;值必須是讀取和編程粒度的整數倍;
lfs_size_t block_size;
// 可以被擦除的塊數量,取決於設備容量
lfs_size_t block_count;
//littlefs逐出元數據日志並將元數據移動到另一個塊之前的擦除周期數。 建議值在
//100-1000范圍內,較大的值具有較好的性能,但是會導致磨損分布不均勻。
// -1 禁用塊級磨損均衡
int32_t block_cycles;
// Size of block caches. Each cache buffers a portion of a block in RAM.
// The littlefs needs a read cache, a program cache, and one additional
// cache per file. Larger caches can improve performance by storing more
// data and reducing the number of disk accesses. Must be a multiple of
// the read and program sizes, and a factor of the block size.
//塊緩存的大小。 每個緩存都會在RAM中緩沖一部分塊。littlefs需要一個讀緩存,
//一個程序緩存以及每個文件一個額外的緩存。 更大的緩存可以通過存儲更多數據
//來減少磁盤訪問次數來提高性能。
//該值必須是讀取和編程大小的倍數,並且是塊大小的因數。
lfs_size_t cache_size;
// 塊分配時的預測深度(分配塊時每次步進多少個塊),這個數值必須為8的整數倍,
//如1024表示每次預測1024個block。這個值對於內存消耗影響不大
lfs_size_t lookahead_size;
// Optional statically allocated read buffer. Must be cache_size.
// By default lfs_malloc is used to allocate this buffer.
void *read_buffer;
// Optional statically allocated program buffer. Must be cache_size.
// By default lfs_malloc is used to allocate this buffer.
void *prog_buffer;
// Optional statically allocated lookahead buffer. Must be lookahead_size
// and aligned to a 32-bit boundary. By default lfs_malloc is used to
// allocate this buffer.
void *lookahead_buffer;
// Optional upper limit on length of file names in bytes. No downside for
// larger names except the size of the info struct which is controlled by
// the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in
// superblock and must be respected by other littlefs drivers.
lfs_size_t name_max;
// Optional upper limit on files in bytes. No downside for larger files
// but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored
// in superblock and must be respected by other littlefs drivers.
lfs_size_t file_max;
// Optional upper limit on custom attributes in bytes. No downside for
// larger attributes size but must be <= LFS_ATTR_MAX. Defaults to
// LFS_ATTR_MAX when zero.
lfs_size_t attr_max;
};
read接口解析
/*
* @brief 從指定塊內讀數據
* @param [in] lfs_config格式參數;
* @param [in] block 邏輯塊編號,從0開始
* @param [in] off 塊內偏移,lfs在調用read接口時,傳入的off值一定能被read_size整除
* @param [out] 讀出數據的輸出緩沖區
* @param [in] size 要讀取的字節數,lfs在讀取時會確保不會跨塊;
* @retval 0 成功 <0 錯誤碼
*/
int (*read)(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
write接口解析
/*
* @brief 從指定塊內讀數據
* @param [in] lfs_config格式參數;
* @param [in] block 邏輯塊編號,從0開始
* @param [in] off 塊內偏移,lfs在調用prog接口時,傳入的off值一定能被rprog_size整除
* @param [in] 寫入數據的緩沖區
* @param [in] size 要讀取的字節數,lfs在讀取時會確保不會跨塊;
* @retval 0 成功 <0 錯誤碼
*/
int (*prog)(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
erase接口解析
/*
* @brief 從指定塊內讀數據
* @param [in] lfs_config格式參數;
* @param [in] block 要擦除的邏輯塊編號,從0開始
* @retval 0 成功 <0 錯誤碼
*/
int (*erase)(const struct lfs_config *c, lfs_block_t block);
上述接口需要根據想要移植的特定設備進行編寫;
lfs的動態內存
lfs同時支持靜態和動態內存兩種方式,使用LFS_NO_MALLOC
來進行開啟關閉,當未定義LFS_NO_MALLOC
時,用戶需要提供自己的內存申請以及釋放函數,接口在lfs_util.h
中定義:
// Allocate memory, only used if buffers are not provided to littlefs
// Note, memory must be 64-bit aligned
static inline void *lfs_malloc(size_t size) {
#ifndef LFS_NO_MALLOC
return mymalloc(SRAMIN,size); //用戶申請內存函數
#else
(void)size;
return NULL;
#endif
}
// Deallocate memory, only used if buffers are not provided to littlefs
static inline void lfs_free(void *p) {
#ifndef LFS_NO_MALLOC
myfree(SRAMIN,p); //用戶釋放內存函數
#else
(void)p;
#endif
}
如果用戶不支持動態內存,則需要定義
LFS_NO_MALLOC
,並且需要在lfs_config結構體中給read_buffer/prog_buffer/lookahead_buffer/file_buffer
設置靜態內存,並且同一時刻只能打開一個文件(實例);
若要支持同時打開多個文件,則動態內存(HEAP)是必須的。
測試實例
我基於正點原子STM32F1精英版V1.41開發板進行了littleFS的移植,並且同時支持了STM32內部Flash和外部的W25Q128,項目地址為 :
項目測試例如下所示:
/* lfs for STM32Flash ********************************************************/
lfs_t lfs_Stm32Flash;
lfs_file_t file_Stm32Flash;
// configuration of the filesystem is provided by this struct
const struct lfs_config cfg_Stm32Flash = {
// block device operations
.read = stm32flash_readLittlefs,
.prog = stm32flash_writeLittlefs,
.erase = stm32flash_eraseLittlefs,
.sync = stm32flash_syncLittlefs,
// block device configuration
.read_size = 128,
.prog_size = 128,
.block_size = STM32Flash_ERASE_GRAN,
.block_count = STM32Flash_NUM_GRAN/2,
.cache_size = 512,
.lookahead_size = 512,
.block_cycles = 500,
};
extern void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite);
int main(void)
{
int err = -1;
delay_init(); //ÑÓʱº¯Êý³õʼ»¯
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//ÉèÖÃÖжÏÓÅÏȼ¶·Ö×éΪ×é2£º2λÇÀÕ¼ÓÅÏȼ¶£¬2λÏìÓ¦ÓÅÏȼ¶
uart_init(115200); //´®¿Ú³õʼ»¯Îª115200
LED_Init(); //³õʼ»¯ÓëLEDÁ¬½ÓµÄÓ²¼þ½Ó¿Ú
KEY_Init(); //°´¼ü³õʼ»¯
W25QXX_Init(); //W25QXX³õʼ»¯
while(W25QXX_ReadID()!=W25Q128) //¼ì²â²»µ½W25Q128
{
printf("W25Q128 Check Failed!\r\n");
delay_ms(500);
printf("Please Check! \r\n");
delay_ms(500);
LED0=!LED0;//DS0ÉÁ˸
}
printf("W25Q128 Ready!\r\n");
// err = lfs_mount(&lfs, &cfg);
//
// if(err )
// {
// lfs_format(&lfs, &cfg);
// lfs_mount(&lfs, &cfg);
// }
//
err = lfs_mount(&lfs_Stm32Flash, &cfg_Stm32Flash);
if( err )
{
lfs_format(&lfs_Stm32Flash, &cfg_Stm32Flash);
lfs_mount(&lfs_Stm32Flash, &cfg_Stm32Flash);
}
while(1)
{
LED0=!LED0;
uint32_t boot_count = 0;
lfs_file_open(&lfs_Stm32Flash, &file_Stm32Flash, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
lfs_file_read(&lfs_Stm32Flash, &file_Stm32Flash, &boot_count, sizeof(boot_count));
// update boot count
boot_count += 1;
lfs_file_rewind(&lfs_Stm32Flash, &file_Stm32Flash); // seek the file to begin
lfs_file_write(&lfs_Stm32Flash, &file_Stm32Flash, &boot_count, sizeof(boot_count));
// remember the storage is not updated until the file is closed successfully
lfs_file_close(&lfs_Stm32Flash, &file_Stm32Flash);
// // release any resources we were using
// lfs_unmount(&lfs);
// print the boot count
printf("boot_count: %d\n", boot_count);
delay_ms(20000);
}
}
下載運行后串口打印效果如下:
工程移植到此完成,接口的優化后面有時間再寫。