【S32K146RT-thread】基于内部PFLASH的littlefs适配

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 littlefsstruct 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 依赖的配置结构实现对应的函数。

  • read 接口实现:



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;}
  • prog接口实现:


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;}
  • erase 接口实现;

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 #include #include #include #include 
#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 命令验证文件系统已经可以通过操作文件文件夹。

视频链接:https://www.nxpic.org.cn/module/forum/data/attachment/forum/202401/28/084053c1f9tfnw98w1q9ww.gif 

RT-thread 软件包也有littlefs 的软件包支持,本次实验暂未使用软件包,后续使用RT-thread 软件包来验证littlefs功能。


——————End——————

想要在RT-Thread平台或社区投放内容?或想参与相关直播活动及赛事?RT-Thread已开放对接窗口,请通过邮件与我们取得联系,期待合作!

合作邮箱: tongfangyi@rt-thread.com 


RTThread物联网操作系统 帮助您了解RT-Thread相关的资讯.
评论
  • 根据Global Info Research(环洋市场咨询)项目团队最新调研,预计2030年全球无人机电池和电源产值达到2834百万美元,2024-2030年期间年复合增长率CAGR为10.1%。 无人机电池是为无人机提供动力并使其飞行的关键。无人机使用的电池类型因无人机的大小和型号而异。一些常见的无人机电池类型包括锂聚合物(LiPo)电池、锂离子电池和镍氢(NiMH)电池。锂聚合物电池是最常用的无人机电池类型,因为其能量密度高、设计轻巧。这些电池以输出功率大、飞行时间长而著称。不过,它们需要
    GIRtina 2025-01-13 10:49 161浏览
  • 在不断发展的电子元件领域,继电器——作为切换电路的关键设备,正在经历前所未有的技术变革。固态继电器(SSR)和机械继电器之间的争论由来已久。然而,从未来发展的角度来看,固态继电器正逐渐占据上风。本文将从耐用性、速度和能效三个方面,全面剖析固态继电器为何更具优势,并探讨其在行业中的应用与发展趋势。1. 耐用性:经久耐用的设计机械继电器:机械继电器依靠物理触点完成电路切换。然而,随着时间的推移,这些触点因电弧、氧化和材料老化而逐渐磨损,导致其使用寿命有限。因此,它们更适合低频或对切换耐久性要求不高的
    腾恩科技-彭工 2025-01-10 16:15 93浏览
  • 01. 什么是过程能力分析?过程能力研究利用生产过程中初始一批产品的数据,预测制造过程是否能够稳定地生产符合规格的产品。可以把它想象成一种预测。通过历史数据的分析,推断未来是否可以依赖该工艺持续生产高质量产品。客户可能会要求将过程能力研究作为生产件批准程序 (PPAP) 的一部分。这是为了确保制造过程能够持续稳定地生产合格的产品。02. 基本概念在定义制造过程时,目标是确保生产的零件符合上下规格限 (USL 和 LSL)。过程能力衡量制造过程能多大程度上稳定地生产符合规格的产品。核心概念很简单:
    优思学院 2025-01-12 15:43 471浏览
  • 电动汽车(EV)正在改变交通运输,为传统内燃机提供更清洁、更高效的替代方案。这种转变的核心是电力电子和能源管理方面的创新,而光耦合器在其中发挥着关键作用。这些不起眼的组件可实现可靠的通信、增强安全性并优化电动汽车系统的性能,使其成为正在进行的革命中不可或缺的一部分。光耦合器,也称为光隔离器,是一种使用光传输电信号的设备。通过隔离高压和低压电路,光耦合器可确保安全性、减少干扰并保持信号完整性。这些特性对于电动汽车至关重要,因为精确控制和安全性至关重要。 光耦合器在电动汽车中的作用1.电池
    腾恩科技-彭工 2025-01-10 16:14 69浏览
  • ARMv8-A是ARM公司为满足新需求而重新设计的一个架构,是近20年来ARM架构变动最大的一次。以下是对ARMv8-A的详细介绍: 1. 背景介绍    ARM公司最初并未涉足PC市场,其产品主要针对功耗敏感的移动设备。     随着技术的发展和市场需求的变化,ARM开始扩展到企业设备、服务器等领域,这要求其架构能够支持更大的内存和更复杂的计算任务。 2. 架构特点    ARMv8-A引入了Execution State(执行状
    丙丁先生 2025-01-12 10:30 433浏览
  • 新年伊始,又到了对去年做总结,对今年做展望的时刻 不知道你在2024年初立的Flag都实现了吗? 2025年对自己又有什么新的期待呢? 2024年注定是不平凡的一年, 一年里我测评了50余块开发板, 写出了很多科普文章, 从一个小小的工作室成长为科工公司。 展望2025年, 中国香河英茂科工, 会继续深耕于,具身机器人、飞行器、物联网等方面的研发, 我觉得,要向未来学习未来, 未来是什么? 是掌握在孩子们生活中的发现,和精历, 把最好的技术带给孩子,
    丙丁先生 2025-01-11 11:35 428浏览
  • PNT、GNSS、GPS均是卫星定位和导航相关领域中的常见缩写词,他们经常会被用到,且在很多情况下会被等同使用或替换使用。我们会把定位导航功能测试叫做PNT性能测试,也会叫做GNSS性能测试。我们会把定位导航终端叫做GNSS模块,也会叫做GPS模块。但是实际上他们之间是有一些重要的区别。伴随着技术发展与越发深入,我们有必要对这三个词汇做以清晰的区分。一、什么是GPS?GPS是Global Positioning System(全球定位系统)的缩写,它是美国建立的全球卫星定位导航系统,是GNSS概
    德思特测试测量 2025-01-13 15:42 447浏览
  • 流量传感器是实现对燃气、废气、生活用水、污水、冷却液、石油等各种流体流量精准计量的关键手段。但随着工业自动化、数字化、智能化与低碳化进程的不断加速,采用传统机械式检测方式的流量传感器已不能满足当代流体计量行业对于测量精度、测量范围、使用寿命与维护成本等方面的精细需求。流量传感器的应用场景(部分)超声波流量传感器,是一种利用超声波技术测量流体流量的新型传感器,其主要通过发射超声波信号并接收反射回来的信号,根据超声波在流体中传播的时间、幅度或相位变化等参数,间接计算流体的流量,具有非侵入式测量、高精
    华普微HOPERF 2025-01-13 14:18 443浏览
  • 随着通信技术的迅速发展,现代通信设备需要更高效、可靠且紧凑的解决方案来应对日益复杂的系统。中国自主研发和制造的国产接口芯片,正逐渐成为通信设备(从5G基站到工业通信模块)中的重要基石。这些芯片凭借卓越性能、成本效益及灵活性,满足了现代通信基础设施的多样化需求。 1. 接口芯片在通信设备中的关键作用接口芯片作为数据交互的桥梁,是通信设备中不可或缺的核心组件。它们在设备内的各种子系统之间实现无缝数据传输,支持高速数据交换、协议转换和信号调节等功能。无论是5G基站中的数据处理,还是物联网网关
    克里雅半导体科技 2025-01-10 16:20 424浏览
  • Snyk 是一家为开发人员提供安全平台的公司,致力于协助他们构建安全的应用程序,并为安全团队提供应对数字世界挑战的工具。以下为 Snyk 如何通过 CircleCI 实现其“交付”使命的案例分析。一、Snyk 的挑战随着客户对安全工具需求的不断增长,Snyk 的开发团队面临多重挑战:加速交付的需求:Snyk 的核心目标是为开发者提供更快、更可靠的安全解决方案,但他们的现有 CI/CD 工具(TravisCI)运行缓慢,无法满足快速开发和部署的要求。扩展能力不足:随着团队规模和代码库的不断扩大,S
    艾体宝IT 2025-01-10 15:52 160浏览
  • 随着数字化的不断推进,LED显示屏行业对4K、8K等超高清画质的需求日益提升。与此同时,Mini及Micro LED技术的日益成熟,推动了间距小于1.2 Pitch的Mini、Micro LED显示屏的快速发展。这类显示屏不仅画质卓越,而且尺寸适中,通常在110至1000英寸之间,非常适合应用于电影院、监控中心、大型会议、以及电影拍摄等多种室内场景。鉴于室内LED显示屏与用户距离较近,因此对于噪音控制、体积小型化、冗余备份能力及电气安全性的要求尤为严格。为满足这一市场需求,开关电源技术推出了专为
    晶台光耦 2025-01-13 10:42 455浏览
  •   在信号处理过程中,由于信号的时域截断会导致频谱扩展泄露现象。那么导致频谱泄露发生的根本原因是什么?又该采取什么样的改善方法。本文以ADC性能指标的测试场景为例,探讨了对ADC的输出结果进行非周期截断所带来的影响及问题总结。 两个点   为了更好的分析或处理信号,实际应用时需要从频域而非时域的角度观察原信号。但物理意义上只能直接获取信号的时域信息,为了得到信号的频域信息需要利用傅里叶变换这个工具计算出原信号的频谱函数。但对于计算机来说实现这种计算需要面对两个问题: 1.
    TIAN301 2025-01-14 14:15 76浏览
  • 随着全球向绿色能源转型的加速,对高效、可靠和环保元件的需求从未如此强烈。在这种背景下,国产固态继电器(SSR)在实现太阳能逆变器、风力涡轮机和储能系统等关键技术方面发挥着关键作用。本文探讨了绿色能源系统背景下中国固态继电器行业的前景,并强调了2025年的前景。 1.对绿色能源解决方案日益增长的需求绿色能源系统依靠先进的电源管理技术来最大限度地提高效率并最大限度地减少损失。固态继电器以其耐用性、快速开关速度和抗机械磨损而闻名,正日益成为传统机电继电器的首选。可再生能源(尤其是太阳能和风能
    克里雅半导体科技 2025-01-10 16:18 319浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦