• 正文
  • 相关推荐
申请入驻 产业图谱

介绍一个给SPI NOR Flash用的轻量文件系统

08/24 14:00
295
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

大家好,我是杂烩君。

SPI Flash 读写会了,配置、小日志、网页资源往哪放?自己按扇区管偏移,加两个文件就乱。有没有轻一点的文件系统?

MCU 上这类需求很常见。FAT 能用,RAM 和代码量往往先涨一截;LittleFS 是后来社区里讨论更多的方向。再往前翻几年,很多板子——尤其 ESP8266、早期 ESP32 资料里——碰到的常常是 SPIFFS

https://github.com/pellepl/spiffs

SPIFFS解决什么问题?

SPI NOR Flash 有几条硬约束,SPIFFS 几乎是围着它们写的:

    只能按块(或扇区)擦除擦除把这一片全置成 1写只能把 1 拉成 0想把 0 变回 1,只能再擦

MCU 还常常没有像样的 heap。TECH_SPEC 写得很直白:不能假设目标机有堆,文件系统只能靠你交给它的 work 缓冲干活。这块缓冲固定是 2 倍逻辑页大小

作者说思路借鉴过 YAFFS,但 YAFFS 面向 NAND、面向 RAM 更宽裕的机器。SPIFFS 更像是把「按文件名读写」这件事,裁到几 KB~几十 KB RAM 的 NOR 场景里。

它不是桌面文件系统的缩小版。更现实的切口是:你已经有一块 SPI NOR,不想自己维护扇区分配。

块、页,对象查找表

集成时要在 spiffs_config 里填几何参数,别跟芯片手册上的 Block / Sector 混着记:

phys_erase_block:芯片一次能擦多大

log_block_size:SPIFFS 自己的逻辑块,必须 ≥ 擦除块,并且对齐

log_page_size:逻辑页,块内最小数据单元

页太小,页头(大约 5~9 字节)占比高,work 缓冲也窄,扫描会多读几次 Flash。页太大,一个 1 字节文件也要占两页(一页索引 + 一页数据),小文件一多就浪费。TECH_SPEC 给了个起步公式,不是最优解:

逻辑页大小 = 逻辑块大小 / 256

逻辑块 64KB,页可以先按 256 字节试;128KB 块可以先试 512。最终还是得按文件大小和 RAM 预算拧。

每个逻辑块内部大致长这样:

开头若干页:Object Lookup(LU),没有页头,就是一串 obj_id后面才是数据页和索引页,带页头找文件时不必把块里每个页头都读一遍,先扫各块的 LU。这是用一点冗余元数据,换更少的短随机读——SPI 交易本身就有命令和地址开销。

obj_id 有几个特殊值,排障时有用:

0xFFFF:空闲页

0x0000

    :已删除(NOR 只能 1→0,删除就是把全 1 写成全 0)最高位为 1:索引页最高位为 0:数据页

一个文件在 SPIFFS 里叫 object。最小开销就是 2 页。配置如果拆成几十个几十字节的小文件,Flash 会先被索引吃掉一截。这点比 API 清单更值得提前算。

集成要点

源码可以按三层记:

spiffs_hydrogen.c:你直接调的 POSIX 味 API

spiffs_nucleus.c:查找表、页分配、对象读写

spiffs_gc.cspiffs_cache.cspiffs_check.c:回收、缓存、一致性检查

你要自己实现的,其实就三个 HAL 回调:read / write / erase。擦除的地址和长度必须对齐 phys_erase_block。写路径要尊重 NOR 语义,别假设能把 0 写回 1。

datasheet 上 4KB sector、64KB block 经常混着写,phys_erase_block 填错,format 时 erase 回调会直接失败,或者只擦了一半。phys_addr 必须落在块边界上。和固件OTA 挤在同一颗 Flash 时,先把分区表算清楚再 mount,别拿应用区去覆盖引导区。

GC 不一定要你手动调。创建、写入、追加时,内部会在分配页之前检查空间。串口日志里突然卡住一下,有时就是它在搬页。

GC 和磨损

候选块打分大致是:删除页多加分,还活着的页要搬家就减分,擦得越老越倾向回收。空间已经很挤时,擦除年龄这项会先丢掉,优先腾地方。

SPIFFS_gc_quick 只擦「几乎全是删除页」的块,不搬页,也不做磨损均衡。空闲时偶尔收拾可以,别当成日常写路径上的默认动作。

README 表明:它不是实时栈。一次 write 可能很快,下一次可能跟着一轮 GC。日志、配置这种可以接受抖动的数据还行;硬实时控制环里塞 SPIFFS_write,后面排查延时会很烦。

内部会尽量留出大约 2 个空闲块。看起来 SPIFFS_info 还有空间,create 却返回 SPIFFS_ERR_FULL,有时就是这个余量,不是 API 坏了。调试阶段可以把 SPIFFS_GC_DBG 打开,串口里能看到它在打分、搬页,比猜「系统卡死」靠谱。

注意事项

没有真正的目录。tmp/config.json

是一个文件名,不是 tmp 目录下的文件。

readdir 能列文件,但别按树状路径去想。

规模不适合很大。

    •  设计目标是常见容量的 SPI Flash。往 128MB 以上堆,LU 扫描会跟着块数涨,RAM 省下来的优势就不好看了。

不管坏块。

    •  NOR 一般也不按 NAND 那套 ECC 来设计。

掉电不是零成本。SPIFFS_info 出现 used 比 total 还大,先跑 SPIFFS_check()。更糟的情况(GC 中途反复掉电),可能得手动删文件才能缓过来。

一份配置对应一份二进制。

     没有通吃所有几何参数的通用库。

AI 帮忙搜 API、对着 TECH_SPEC 解释结构,会快一些。页大小合不合适、HAL 擦写是否对齐、掉电后 check 修不回来,还是得实际调试确认。

有没有用过SPIFFS的朋友?欢迎评论区分享经验

相关推荐

本公众号专注于嵌入式技术,包括但不限于C/C++、嵌入式、物联网、Linux等编程学习笔记,同时,公众号内包含大量的学习资源。欢迎关注,一同交流学习,共同进步!