"Flash 选型千千万,文件系统两行泪。" 在嵌入式开发中,选择一款适合 Flash 特性的文件系统至关重要。
今天,基于实际工程项目,从零开始讲解如何在 Zephyr RTOS 上使用 W25Q128 SPI Flash 搭配 LittleFS 文件系统。文章涵盖硬件接线、设备树配置、内核参数调优以及常见问题解决方案.
一、LittleFS 介绍
在嵌入式领域,常见的文件系统有 FATFS、JFFS2、YAFFS、LittleFS 等。选择 LittleFS 的核心理由:
| 特性 | 说明 |
|---|---|
| 掉电安全 | 内置磨损均衡和日志机制,写操作原子性有保障 |
| 资源占用小 | 典型 RAM 占用 < 2KB,适合 MCU 环境 |
| 抗意外断电 | 每次写入都有完整性检查,崩溃后可恢复 |
| 无需文件系统清理 | 无需像 FATFS 那样定期执行 fsck |
Zephyr 官方将 LittleFS 作为第一方支持的文件系统,提供完整的设备驱动和配置接口。
二、硬件平台
2.1 芯片选型
本项目采用 GD32F450ZGT6(兆易创新,基于 ARM Cortex-M4),主频 200MHz,256KB SRAM,1MB Flash。
2.2 W25Q128 简介
W25Q128 是华邦电子推出的 16MB SPI NOR Flash,性能参数:
| 参数 | 值 |
|---|---|
| 容量 | 16MB(128Mbit) |
| SPI 频率 | 支持高达 80MHz |
| 扇区大小 | 4KB |
| 擦除方式 | 支持 4KB 扇区、32KB/64KB 块擦除 |
| 寿命 | 10万次擦写周期 |
三、设备树配置
设备树(Device Tree)是 Zephyr 连接硬件与应用的关键。本项目使用 boards/gd32f450z_eval.overlay 文件进行配置:
dts
&spi5 {
status = "okay";
pinctrl-0 = <&spi5_default>;
pinctrl-names = "default";
cs-gpios = <&gpiog 9 GPIO_ACTIVE_LOW>;
w25q128: w25q128@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <4000000>; /* 4 MHz */
size = <0x1000000>; /* 16 MB */
jedec-id = [ef 40 18]; /* Winbond W25Q128 */
status = "okay";
};
};
&w25q128 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
boot_partition: partition@0 {
label = "mcuboot";
reg = <0x00000000 0x00080000>; /* 512KB 保留区 */
read-only;
};
storage_partition: partition@80000 {
label = "storage";
reg = <0x00080000 0x00F80000>; /* 15.5MB LittleFS 分区 */
};
};
};
说明:
- 前 512KB(0x00000 ~ 0x80000)保留给 bootloader(如 MCUboot)
- LittleFS 分区从 512KB 开始,占用剩余 15.5MB
jedec-id = [ef 40 18]是 W25Q128 的 JEDEC ID,Zephyr 据此自动匹配驱动
四、Kconfig 内核配置
在 prj.conf 中启用文件系统和 SPI Flash 支持:
ini
# 文件系统支持
CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y
# SPI 和 NOR Flash 驱动
CONFIG_SPI=y
CONFIG_SPI_NOR=y
CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
# 可选:强制每次重新创建文件系统(调试用)
# CONFIG_APP_WIPE_STORAGE=y
CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 指定 Flash 页大小,W25Q128 的扇区正好是 4KB。
LittleFS 关键参数(按需调优)
| 参数 | 默认值 | 说明 |
|---|---|---|
CONFIG_FS_LITTLEFS_CACHE_SIZE |
64 | 文件缓存大小,增大可提升大文件写入性能 |
CONFIG_FS_LITTLEFS_PROG_SIZE |
16 | Flash 编程单位,必须为 2 的幂 |
CONFIG_FS_LITTLEFS_READ_SIZE |
16 | Flash 读取单位 |
CONFIG_FS_LITTLEFS_BLOCK_CYCLES |
512 | 磨损均衡参数,值越小磨损越均匀但影响寿命 |
五、应用层代码实战
5.1 挂载文件系统
c
static int littlefs_mount(struct fs_mount_t *mp)
{
int rc;
/* 擦除 Flash 存储区(可选) */
rc = littlefs_flash_erase((uintptr_t)mp->storage_dev);
if (rc < 0) {
return rc;
}
/* 挂载文件系统 */
rc = fs_mount(mp);
if (rc < 0) {
LOG_ERR("FAIL: mount at %s: %d\n", mp->mnt_point, rc);
return rc;
}
LOG_PRINTK("%s mount: %d\n", mp->mnt_point, rc);
return 0;
}
5.2 写入文件
c
static int write_test_file(const char *fname)
{
struct fs_file_t file;
uint8_t buf[64];
int rc;
/* 填充测试数据 */
memset(buf, 0x55, sizeof(buf));
fs_file_t_init(&file);
rc = fs_open(&file, fname, FS_O_CREATE | FS_O_RDWR);
if (rc < 0) {
LOG_ERR("FAIL: open %s: %d", fname, rc);
return rc;
}
rc = fs_write(&file, buf, sizeof(buf));
if (rc < 0) {
LOG_ERR("FAIL: write %s: %d", fname, rc);
}
fs_close(&file);
return rc;
}
六、避坑指南
坑 1:写入失败(文件过大)
症状 :fs_write() 返回负值,错误码 -22(EINVAL)。
原因 :LittleFS 默认 CACHE_SIZE=64,当写入数据超过缓存大小时,操作被拒绝。
如果觉得有用,别忘了点赞 + 在看 + 转发三连,让更多开发者看到!
🎯 关注、私信即可获得完整工程源码!