移植平台: 正点原子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);
}
}
下载运行后串口打印效果如下:
工程移植到此完成,接口的优化后面有时间再写。
基于STM32F103的Littlefs文件系统移植 - 蜗牛韩 - 博客园 (cnblogs.com)