在线时间1938 小时
UID3338155
注册时间2017-7-26
NXP金币2947
TA的每日心情 | 奋斗 前天 21:18 |
---|
签到天数: 856 天 连续签到: 1 天 [LV.10]以坛为家III
金牌会员
 
- 积分
- 10338
- 最后登录
- 2025-7-25
|
本帖最后由 andeyqi 于 2024-2-1 10:01 编辑
LittleFS是一个应用于单片机内部flash和外挂NOR flash的文件系统。由于它相比传统的FAT文件系统更适合于小型嵌入式系统,具有以下特点:
- 掉电恢复能力: 设计用于处理随机电源故障。所有文件操作都有很强的写时拷贝保证,如果断电,文件系统将恢复到上一次已知的良好状态。
- 动态磨损均衡: 设计考虑到闪存,并提供动态块磨损均衡。此外,littlefs可以检测坏块并在它们周围工作。
- 有限RAM/ROM: 被设计为使用少量内存。RAM的使用是严格限制的,这意味着RAM的使用不会随着文件系统的增长而改变。文件系统不包含无界递归,动态内存仅限于可静态提供的可配置缓冲区。
官方的详细介绍参照此链接(https://github.com/littlefs-project/littlefs/)
S32K146 内部的Pflash 资源大小为1M,这个大小对普通的嵌入式开发资源是有很大的空闲的,本次试验基于内部的pflash 将后512K资源划分为文件系统分区,使用littlefs 进行管理,我们修改链接脚本把后512K资源保留出来给文件系统使用,本次试验使用的IAR环境,link file 修改如下:
littlefs 移植适配依赖物理层的配置结构体如下:- // Configuration provided during initialization of the littlefs
- struct lfs_config {
- // Opaque user provided context that can be used to pass
- // information to the block device operations
- void *context;
- // Read a region in a block. Negative error codes are propagated
- // to the user.
- int (*read)(const struct lfs_config *c, lfs_block_t block,
- lfs_off_t off, void *buffer, lfs_size_t size);
- // Program a region in a block. The block must have previously
- // been erased. Negative error codes are propagated to the user.
- // May return LFS_ERR_CORRUPT if the block should be considered bad.
- int (*prog)(const struct lfs_config *c, lfs_block_t block,
- lfs_off_t off, const void *buffer, lfs_size_t size);
- // Erase a block. A block must be erased before being programmed.
- // The state of an erased block is undefined. Negative error codes
- // are propagated to the user.
- // May return LFS_ERR_CORRUPT if the block should be considered bad.
- int (*erase)(const struct lfs_config *c, lfs_block_t block);
- // Sync the state of the underlying block device. Negative error codes
- // are propagated to the user.
- int (*sync)(const struct lfs_config *c);
- #ifdef LFS_THREADSAFE
- // Lock the underlying block device. Negative error codes
- // are propagated to the user.
- int (*lock)(const struct lfs_config *c);
- // Unlock the underlying block device. Negative error codes
- // are propagated to the user.
- int (*unlock)(const struct lfs_config *c);
- #endif
- // Minimum size of a block read in bytes. All read operations will be a
- // multiple of this value.
- lfs_size_t read_size;
- // Minimum size of a block program in bytes. All program operations will be
- // a multiple of this value.
- lfs_size_t prog_size;
- // Size of an erasable block in bytes. This does not impact ram consumption
- // and may be larger than the physical erase size. However, non-inlined
- // files take up at minimum one block. Must be a multiple of the read and
- // program sizes.
- lfs_size_t block_size;
- // Number of erasable blocks on the device.
- lfs_size_t block_count;
- // Number of erase cycles before littlefs evicts metadata logs and moves
- // the metadata to another block. Suggested values are in the
- // range 100-1000, with large values having better performance at the cost
- // of less consistent wear distribution.
- //
- // Set to -1 to disable block-level wear-leveling.
- int32_t block_cycles;
- // Size of block caches in bytes. 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.
- lfs_size_t cache_size;
- // Size of the lookahead buffer in bytes. A larger lookahead buffer
- // increases the number of blocks found during an allocation pass. The
- // lookahead buffer is stored as a compact bitmap, so each byte of RAM
- // can track 8 blocks. Must be a multiple of 8.
- 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;
- // Optional upper limit on total space given to metadata pairs in bytes. On
- // devices with large blocks (e.g. 128kB) setting this to a low size (2-8kB)
- // can help bound the metadata compaction time. Must be <= block_size.
- // Defaults to block_size when zero.
- lfs_size_t metadata_max;
- };
复制代码
主要包含物理层设备的读写/擦除,及FLASH最小编程块属性配置,查看S32K146 的features 可以知道,最小擦除的sector 为4096字节,最小写操作size 为8字节,我们按照littlefs 依赖的配置结构实现对应的而函数。
- int lfs_mflash_read(const struct lfs_config *lfsc, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size)
- {
- struct lfs_mflash_ctx *ctx;
- uint32_t flash_addr;
- assert(lfsc);
- ctx = (struct lfs_mflash_ctx *)lfsc->context;
- assert(ctx);
- flash_addr = ctx->start_addr + block * lfsc->block_size + off;
-
- for(lfs_size_t i=0; i < size; i++)
- {
- ((int8_t *)buffer)[i] = *((__IO int8_t*)flash_addr);
- flash_addr++;
- }
- return LFS_ERR_OK;
- }
复制代码
- int32_t mflash_drv_program(uint32_t addr,uint32_t size,uint8_t * pdata)
- {
- status_t ret;
- ret = FLASH_DRV_Program(&pSSDConfig,addr,size,pdata);
- return ret == STATUS_SUCCESS ? 0 : -1;
- }
- int lfs_mflash_prog(
- const struct lfs_config *lfsc, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size)
- {
- struct lfs_mflash_ctx *ctx;
- uint32_t flash_addr;
- int32_t ret;
- ctx = (struct lfs_mflash_ctx *)lfsc->context;
- flash_addr = ctx->start_addr + block * lfsc->block_size + off;
-
- ret = mflash_drv_program(flash_addr,size,(uint8_t *)buffer);
-
- return (ret == 0) ? LFS_ERR_OK : LFS_ERR_IO;
- }
复制代码- <blockquote>int32_t mflash_drv_erase(uint32_t dest,uint32_t size)
复制代码 对应的配置结构如下:
适配接口已经对应完成,littlefs 会依赖动态malloc/free 内存接口,本次是基于RT-thread 系统lfs_util.h需做如下修改:
至此Littfs 依赖的适配接口已经完成,我们追加shell 测试命令来验证littlefs的基本创建删除文件及文件夹及读写删除试验,对应的shell 测试命令代码如下: - #include "drv_mflash.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdint.h>
- #include <string.h>
- #include <rtthread.h>
- #define SHELL_Printf rt_kprintf
- #define PRINTF rt_kprintf
- /*******************************************************************************
- * Variables
- ******************************************************************************/
- lfs_t lfs;
- struct lfs_config cfg;
- int lfs_mounted;
- static void format(int argc, char **argv)
- {
- int res;
- if (lfs_mounted)
- {
- SHELL_Printf("LFS is mounted, please unmount it first.\r\n");
- return;
- }
- if (argc != 2 || strcmp(argv[1], "yes"))
- {
- SHELL_Printf("Are you sure? Please issue command "format yes" to proceed.\r\n");
- return;
- }
- res = lfs_format(&lfs, &cfg);
- if (res)
- {
- PRINTF("\rError formatting LFS: %d\r\n", res);
- }
- return;
- }
- MSH_CMD_EXPORT(format,"lfs format api");
- static void mount(int argc, char **argv)
- {
- int res;
- if (lfs_mounted)
- {
- SHELL_Printf("LFS already mounted\r\n");
- return;
- }
- res = lfs_mount(&lfs, &cfg);
- if (res)
- {
- PRINTF("\rError mounting LFS\r\n");
- }
- else
- {
- lfs_mounted = 1;
- }
- return;
- }
- MSH_CMD_EXPORT(mount,lfs mount api);
- static void unmount(int argc, char **argv)
- {
- int res;
- if (!lfs_mounted)
- {
- SHELL_Printf("LFS not mounted\r\n");
- return;
- }
- res = lfs_unmount(&lfs);
- if (res)
- {
- PRINTF("\rError unmounting LFS: %i\r\n", res);
- }
- lfs_mounted = 0;
- return;
- }
- MSH_CMD_EXPORT(unmount,lfs unmount api);
- static void cd(int argc, char **argv)
- {
- SHELL_Printf(
- "There is no concept of current directory in this example.\r\nPlease always specify the full path.\r\n");
- return;
- }
- MSH_CMD_EXPORT(cd,lfs cd api);
- static void lls(int argc, char **argv)
- {
- int res;
- char *path;
- lfs_dir_t dir;
- struct lfs_info info;
- int files;
- int dirs;
- if (!lfs_mounted)
- {
- SHELL_Printf("LFS not mounted\r\n");
- return;
- }
- if (argc > 2)
- {
- SHELL_Printf("Invalid number of parameters\r\n");
- return;
- }
- if (argc < 2)
- {
- path = "/";
- }
- else
- {
- path = argv[1];
- }
- /* open the directory */
- res = lfs_dir_open(&lfs, &dir, path);
- if (res)
- {
- PRINTF("\rError opening directory: %i\r\n", res);
- return;
- }
- PRINTF(" Directory of %s\r\n", path);
- files = 0;
- dirs = 0;
- /* iterate until end of directory */
- while ((res = lfs_dir_read(&lfs, &dir, &info)) != 0)
- {
- if (res < 0)
- {
- /* break the loop in case of an error */
- PRINTF("\rError reading directory: %i\r\n", res);
- break;
- }
- if (info.type == LFS_TYPE_REG)
- {
- SHELL_Printf("%8d %s\r\n", info.size, info.name);
- files++;
- }
- else if (info.type == LFS_TYPE_DIR)
- {
- SHELL_Printf("% DIR %s\r\n", info.name);
- dirs++;
- }
- else
- {
- SHELL_Printf("%???\r\n");
- }
- }
- res = lfs_dir_close(&lfs, &dir);
- if (res)
- {
- PRINTF("\rError closing directory: %i\r\n", res);
- return;
- }
- PRINTF(" %d File(s), %d Dir(s)\r\n", files, dirs);
- return;
- }
- MSH_CMD_EXPORT(lls,lfs ls api);
- static void rm(int argc, char **argv)
- {
- int res;
- if (!lfs_mounted)
- {
- SHELL_Printf("LFS not mounted\r\n");
- return;
- }
- res = lfs_remove(&lfs, argv[1]);
- if (res)
- {
- PRINTF("\rError while removing: %i\r\n", res);
- }
- return;
- }
- MSH_CMD_EXPORT(rm,lfs rm api);
- static void lmkdir(int argc, char **argv)
- {
- int res;
- if (!lfs_mounted)
- {
- SHELL_Printf("LFS not mounted\r\n");
- return;
- }
- res = lfs_mkdir(&lfs, argv[1]);
- if (res)
- {
- PRINTF("\rError creating directory: %i\r\n", res);
- }
- return;
- }
- MSH_CMD_EXPORT(lmkdir,lfs mkdir api);
- static void write(int argc, char **argv)
- {
- int res;
- lfs_file_t file;
- if (!lfs_mounted)
- {
- SHELL_Printf("LFS not mounted\r\n");
- return;
- }
- res = lfs_file_open(&lfs, &file, argv[1], LFS_O_WRONLY | LFS_O_APPEND | LFS_O_CREAT);
- if (res)
- {
- PRINTF("\rError opening file: %i\r\n", res);
- return;
- }
- res = lfs_file_write(&lfs, &file, argv[2], strlen(argv[2]));
- if (res > 0)
- res = lfs_file_write(&lfs, &file, "\r\n", 2);
- if (res < 0)
- {
- PRINTF("\rError writing file: %i\r\n", res);
- }
- res = lfs_file_close(&lfs, &file);
- if (res)
- {
- PRINTF("\rError closing file: %i\r\n", res);
- }
- return;
- }
- MSH_CMD_EXPORT(write,lfs write api);
- static void cat(int argc, char **argv)
- {
- int res;
- lfs_file_t file;
- uint8_t buf[16];
- if (!lfs_mounted)
- {
- SHELL_Printf("LFS not mounted\r\n");
- return;
- }
- res = lfs_file_open(&lfs, &file, argv[1], LFS_O_RDONLY);
- if (res)
- {
- PRINTF("\rError opening file: %i\r\n", res);
- return;
- }
- do
- {
- res = lfs_file_read(&lfs, &file, buf, sizeof(buf));
- if (res < 0)
- {
- PRINTF("\rError reading file: %i\r\n", res);
- break;
- }
- if(res > 0)
- {
- buf[res] = '\0';
- PRINTF("%s",(char *)buf);
- }
- } while (res);
- res = lfs_file_close(&lfs, &file);
- if (res)
- {
- PRINTF("\rError closing file: %i\r\n", res);
- }
- return;
- }
- MSH_CMD_EXPORT(cat,lfs cat api);
- static void lfsinit(int argc, char **argv)
- {
- mflash_drv_init();
- lfs_get_default_config(&cfg);
- return;
- }
- MSH_CMD_EXPORT(lfsinit,lfs init api);
- static void df(int argc, char **argv)
- {
- printf("used block %d total %d\r\n",lfs_fs_size(&lfs),cfg.block_count);
- return;
- }
- MSH_CMD_EXPORT(df,lfs init api);
复制代码至此准备工作已经完成,我们通过shell 来验证文件系统的功能,shell 命令验证文件系统已经可以通过操作文件文件夹。 RT-thread 软件包也有littlefs 的软件包支持,本次实验暂未使用软件包,后续使用RT-thread 软件包来验证littlefs功能。
|
|