基於STM32F103的Littlefs文件系統移植


移植平台: 正點原子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,項目地址為 :

基於正點原子STM32F1精英版的LittleFS移植

項目測試例如下所示:


/* 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);
	}
}

下載運行后串口打印效果如下:

工程移植到此完成,接口的優化后面有時間再寫。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM