跳转至

嵌入式文件系统

概述

嵌入式文件系统为嵌入式设备提供文件存储和管理功能,需要考虑存储介质特性、资源占用、性能等因素。

常见嵌入式文件系统

文件系统 特点 适用场景
FAT/FAT32 兼容性好、简单 SD卡、U盘
LittleFS 掉电安全、磨损均衡 SPI Flash
JFFS2 日志结构、压缩 NOR Flash
YAFFS2 NAND Flash优化 NAND Flash
ROMFS 只读、简单 固件存储
RAMFS 内存文件系统 临时存储

FAT文件系统

Linux下使用FAT

Bash
1
2
3
4
5
# 挂载FAT分区
mount -t vfat /dev/mmcblk0p1 /mnt/sd

# 查看FAT信息
fdisk -l /dev/mmcblk0

FatFs库(通用FAT实现)

C
#include "ff.h"

FATFS fs;
FIL file;
FRESULT res;

// 挂载文件系统
res = f_mount(&fs, "0:", 1);

// 打开文件
res = f_open(&file, "0:test.txt", FA_WRITE | FA_CREATE_ALWAYS);

// 写入数据
UINT bw;
char data[] = "Hello, FatFs!";
res = f_write(&file, data, strlen(data), &bw);

// 关闭文件
f_close(&file);

// 读取文件
res = f_open(&file, "0:test.txt", FA_READ);

char buf[128];
UINT br;
res = f_read(&file, buf, sizeof(buf), &br);
buf[br] = '\0';
printf("Read: %s\n", buf);

f_close(&file);

// 卸载文件系统
f_mount(NULL, "0:", 0);

目录操作

C
// 创建目录
f_mkdir("0:mydir");

// 打开目录
DIR dir;
res = f_opendir(&dir, "0:/");

// 读取目录项
FILINFO fno;
while (1) {
    res = f_readdir(&dir, &fno);
    if (res != FR_OK || fno.fname[0] == 0) break;
    
    if (fno.fattrib & AM_DIR) {
        printf("DIR: %s\n", fno.fname);
    } else {
        printf("FILE: %s (%lu bytes)\n", fno.fname, fno.fsize);
    }
}

f_closedir(&dir);

// 删除文件
f_unlink("0:test.txt");

// 获取文件信息
f_stat("0:test.txt", &fno);

LittleFS

LittleFS是专为嵌入式设计的文件系统,具有掉电安全和磨损均衡特性。

Linux使用LittleFS

Bash
1
2
3
4
5
# 安装lfs工具
pip install littlefs-python

# 挂载LittleFS
mount -t littlefs /dev/mtd0 /mnt/lfs

嵌入式LittleFS使用

C
#include "lfs.h"

lfs_t lfs;
lfs_file_t file;

// 配置底层读写函数
int block_device_read(const struct lfs_config *c, lfs_block_t block,
                      lfs_off_t off, void *buffer, lfs_size_t size)
{
    return flash_read(block * c->block_size + off, buffer, size);
}

int block_device_prog(const struct lfs_config *c, lfs_block_t block,
                      lfs_off_t off, const void *buffer, lfs_size_t size)
{
    return flash_write(block * c->block_size + off, buffer, size);
}

int block_device_erase(const struct lfs_config *c, lfs_block_t block)
{
    return flash_erase_block(block);
}

int block_device_sync(const struct lfs_config *c)
{
    return 0;
}

const struct lfs_config cfg = {
    .read  = block_device_read,
    .prog  = block_device_prog,
    .erase = block_device_erase,
    .sync  = block_device_sync,
    .read_size = 16,
    .prog_size = 16,
    .block_size = 4096,
    .block_count = 256,
    .cache_size = 16,
    .lookahead_size = 16,
};

// 挂载
int err = lfs_mount(&lfs, &cfg);

// 文件操作
lfs_file_open(&lfs, &file, "test.txt", LFS_O_WRONLY | LFS_O_CREAT);
lfs_file_write(&lfs, &file, "Hello", 5);
lfs_file_close(&lfs, &file);

// 读取
lfs_file_open(&lfs, &file, "test.txt", LFS_O_RDONLY);
char buf[32];
lfs_size_t size = lfs_file_read(&lfs, &file, buf, sizeof(buf));
lfs_file_close(&lfs, &file);

// 卸载
lfs_unmount(&lfs);

目录操作

C
lfs_mkdir(&lfs, "mydir");

lfs_dir_t dir;
lfs_dir_open(&lfs, &dir, "/");

struct lfs_info info;
while (lfs_dir_read(&lfs, &dir, &info) > 0) {
    if (info.type == LFS_TYPE_DIR) {
        printf("DIR: %s\n", info.name);
    } else {
        printf("FILE: %s (%ld bytes)\n", info.name, info.size);
    }
}

lfs_dir_close(&lfs, &dir);

JFFS2

JFFS2是专为NOR Flash设计的日志结构文件系统。

Bash
1
2
3
4
5
6
7
8
# 擦除Flash分区
flash_eraseall /dev/mtd0

# 创建JFFS2镜像
mkfs.jffs2 -r rootfs -o rootfs.jffs2 -e 0x10000 --pad=0x400000

# 挂载JFFS2
mount -t jffs2 /dev/mtdblock0 /mnt/jffs2

YAFFS2

YAFFS2专为NAND Flash设计,处理NAND Flash的坏块和位翻转。

Bash
1
2
3
4
5
# 格式化NAND
nandwrite /dev/mtd0 yaffs2.img

# 挂载
mount -t yaffs2 /dev/mtdblock0 /mnt/yaffs2

ROMFS

只读文件系统,用于存储固件。

Bash
1
2
3
4
5
# 创建ROMFS镜像
genromfs -f romfs.img -d romfs_dir/

# 挂载
mount -t romfs -o loop romfs.img /mnt/romfs

RAMFS/ TMPFS

内存文件系统,掉电后数据丢失。

Bash
1
2
3
4
5
# 挂载ramfs
mount -t ramfs none /mnt/ram

# 挂载tmpfs(大小限制)
mount -t tmpfs -o size=10M tmpfs /mnt/tmp

SPIFFS

专为SPI NOR Flash设计的文件系统。

C
#include "spiffs.h"

spiffs fs;

// 配置
spiffs_config cfg;
cfg.hal_read_f = my_spi_read;
cfg.hal_write_f = my_spi_write;
cfg.hal_erase_f = my_spi_erase;
cfg.phys_size = 2 * 1024 * 1024;  // 2MB
cfg.phys_addr = 0;
cfg.phys_erase_block = 4096;
cfg.log_block_size = 4096;
cfg.log_page_size = 256;

spiffs_mount(&fs, cfg, work, fd_space, cache, cache_size);

// 文件操作
spiffs_file fd = SPIFFS_open(&fs, "test.txt", SPIFFS_O_CREAT | SPIFFS_O_WRONLY, 0);
SPIFFS_write(&fs, fd, "Hello", 5);
SPIFFS_close(&fs, fd);

SPIFFS_unmount(&fs);

文件系统选择指南

按存储介质选择

Text Only
NOR Flash:
  - LittleFS(推荐)
  - JFFS2

NAND Flash:
  - YAFFS2
  - UBIFS

SD卡:
  - FAT32
  - exFAT(大容量)

RAM:
  - RAMFS
  - TMPFS

只读:
  - ROMFS
  - CramFS

按需求选择

Text Only
掉电安全:
  - LittleFS
  - JFFS2
  - YAFFS2

最大兼容:
  - FAT32

最小资源:
  - LittleFS
  - ROMFS

最高性能:
  - RAMFS
  - TMPFS

存储分区规划

Bash
1
2
3
4
5
6
7
# MTD分区示例
# /etc/mtdparts
mtdparts=mtdparts=spi0.0:256k(boot),128k(env),3m(kernel),-(rootfs)

# U-Boot环境变量
setenv mtdparts 'mtdparts=spi0.0:256k(boot),128k(env),3m(kernel),-(rootfs)'
setenv bootargs 'console=ttyS0,115200 root=/dev/mtdblock3 rootfstype=jffs2'

参考资料