跳转至

LittleFS轻量级文件系统:嵌入式Flash存储的最佳选择

学习目标

完成本教程后,你将能够:

  • 理解LittleFS的设计原理和核心特性
  • 掌握LittleFS的移植方法和配置
  • 熟练使用LittleFS的API进行文件操作
  • 理解LittleFS的掉电保护机制
  • 掌握磨损均衡和垃圾回收原理
  • 能够优化LittleFS的性能
  • 完成一个完整的Flash文件系统项目
  • 掌握LittleFS的调试和故障排除方法

前置要求

在开始学习之前,建议你具备:

知识要求: - 熟悉C语言编程 - 了解Flash存储器的基本特性 - 理解文件系统的基本概念 - 掌握SPI或I2C通信协议 - 了解RTOS的基本使用

技能要求: - 能够编写嵌入式C代码 - 会使用SPI Flash驱动 - 熟悉基本的文件操作 - 能够使用调试工具 - 了解内存管理

开发环境: - STM32或ESP32开发板 - SPI Flash芯片(如W25Q128) - Keil MDK或STM32CubeIDE - 串口调试工具 - 逻辑分析仪(可选)

LittleFS概述

什么是LittleFS

LittleFS是一个专为嵌入式系统设计的轻量级文件系统,特别适合用于Flash存储器。它由ARM公司开发并开源,具有以下显著特点:

核心特性

  1. 掉电安全
  2. 所有操作都是原子性的
  3. 断电后文件系统保持一致性
  4. 不会丢失已写入的数据
  5. 支持事务性操作

  6. 磨损均衡

  7. 自动分散写入操作
  8. 延长Flash使用寿命
  9. 动态块分配
  10. 智能垃圾回收

  11. 资源占用小

  12. RAM占用极低(几KB)
  13. ROM占用小(约15KB)
  14. 适合资源受限的MCU
  15. 可配置的缓存大小

  16. 高可靠性

  17. 元数据冗余存储
  18. CRC校验保护
  19. 坏块管理
  20. 自动错误恢复

LittleFS架构

系统架构图

┌─────────────────────────────────────────────────┐
│    应用层 (Application)                          │
│    文件读写、目录操作                             │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│    LittleFS API层                                │
│    lfs_file_open, lfs_read, lfs_write...        │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│    LittleFS核心层                                │
│    ┌──────────────┐  ┌──────────────┐          │
│    │ 文件管理     │  │ 目录管理     │          │
│    └──────────────┘  └──────────────┘          │
│    ┌──────────────┐  ┌──────────────┐          │
│    │ 块分配器     │  │ 磨损均衡     │          │
│    └──────────────┘  └──────────────┘          │
│    ┌──────────────┐  ┌──────────────┐          │
│    │ 元数据管理   │  │ 缓存管理     │          │
│    └──────────────┘  └──────────────┘          │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│    块设备接口层 (Block Device)                   │
│    read, prog, erase, sync                      │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│    Flash驱动层                                   │
│    SPI Flash, QSPI Flash, 内部Flash             │
└─────────────────────────────────────────────────┘

LittleFS vs 其他文件系统

特性 LittleFS FAT SPIFFS
掉电安全 ✅ 完全支持 ❌ 不支持 ⚠️ 部分支持
磨损均衡 ✅ 自动 ❌ 无 ✅ 支持
RAM占用 极低(几KB) 中等
ROM占用 小(15KB)
目录支持 ✅ 完整支持 ✅ 支持 ❌ 不支持
文件属性 ✅ 支持 ✅ 支持 ⚠️ 有限
性能 中等 中等
适用场景 嵌入式Flash SD卡、U盘 小型Flash

选择建议: - 需要掉电保护 → LittleFS - 需要与PC交互 → FAT - 资源极度受限 → SPIFFS - 大容量存储 → FAT32/exFAT

准备工作

硬件准备

名称 数量 说明 参考型号
开发板 1 STM32F4或ESP32 STM32F407VG
SPI Flash 1 外部Flash芯片 W25Q128(16MB)
杜邦线 若干 连接Flash -
USB线 1 供电和调试 -

软件准备

必需软件: - STM32CubeIDE 或 Arduino IDE(ESP32) - LittleFS源码:https://github.com/littlefs-project/littlefs - SPI Flash驱动库 - 串口调试助手

可选工具: - 逻辑分析仪(调试SPI通信) - Flash编程器(备份恢复)

电路连接

SPI Flash连接示意图

STM32F407              W25Q128
  PA5 (SCK)  ────────► CLK
  PA6 (MISO) ◄──────── DO
  PA7 (MOSI) ────────► DI
  PA4 (CS)   ────────► CS
  3.3V       ────────► VCC
  GND        ────────► GND

连接说明: - 确保Flash芯片供电为3.3V - CS片选信号可以使用任意GPIO - 建议在MISO线上加上拉电阻(10KΩ) - 确保所有信号线长度尽量短

步骤1:获取和配置LittleFS源码

1.1 下载LittleFS

# 克隆LittleFS仓库
git clone https://github.com/littlefs-project/littlefs.git

# 或下载最新release版本
wget https://github.com/littlefs-project/littlefs/archive/refs/tags/v2.5.1.tar.gz

需要的文件: - lfs.c - 核心实现 - lfs.h - API头文件 - lfs_util.c - 工具函数 - lfs_util.h - 工具函数头文件

1.2 配置LittleFS

创建配置文件 lfs_config.h

#ifndef LFS_CONFIG_H
#define LFS_CONFIG_H

// 系统包含
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>

// 调试配置
#ifdef DEBUG
    #define LFS_DEBUG   1
    #define LFS_WARN    1
    #define LFS_ERROR   1
#else
    #define LFS_DEBUG   0
    #define LFS_WARN    0
    #define LFS_ERROR   1
#endif

// 内存分配配置
#define LFS_NO_MALLOC       0  // 使用动态内存分配
#define LFS_NO_ASSERT       0  // 启用断言

// 缓存配置
#define LFS_READ_SIZE       256    // 最小读取单位
#define LFS_PROG_SIZE       256    // 最小编程单位
#define LFS_BLOCK_SIZE      4096   // 擦除块大小
#define LFS_BLOCK_COUNT     4096   // 总块数(16MB)
#define LFS_CACHE_SIZE      256    // 缓存大小
#define LFS_LOOKAHEAD_SIZE  16     // 预读大小

// 块设备配置
#define LFS_BLOCK_CYCLES    500    // 磨损均衡周期

// 文件系统限制
#define LFS_NAME_MAX        255    // 最大文件名长度
#define LFS_FILE_MAX        2147483647  // 最大文件大小
#define LFS_ATTR_MAX        1022   // 最大属性大小

#endif // LFS_CONFIG_H

配置说明: - LFS_READ_SIZE: 必须是Flash页大小的倍数 - LFS_PROG_SIZE: 必须是Flash编程单位的倍数 - LFS_BLOCK_SIZE: 必须等于Flash擦除块大小 - LFS_BLOCK_COUNT: Flash总大小 / 块大小 - LFS_CACHE_SIZE: 影响性能和RAM占用

1.3 添加到工程

STM32CubeIDE步骤

  1. 将LittleFS源文件添加到工程
  2. 右键项目 → New → Folder → 创建 Middlewares/LittleFS
  3. 复制 lfs.c, lfs.h, lfs_util.c, lfs_util.h
  4. 复制 lfs_config.h 到工程

  5. 配置包含路径

  6. 右键项目 → Properties → C/C++ Build → Settings
  7. MCU GCC Compiler → Include paths
  8. 添加 Middlewares/LittleFS

  9. 编译测试

    # 应该能够成功编译
    Build Project
    

步骤2:实现块设备接口

LittleFS需要我们实现底层的Flash读写接口。

2.1 定义块设备结构

创建文件 lfs_port.h

#ifndef LFS_PORT_H
#define LFS_PORT_H

#include "lfs.h"
#include "w25qxx.h"  // SPI Flash驱动头文件

// 块设备配置
#define FLASH_BLOCK_SIZE    4096    // 4KB擦除块
#define FLASH_BLOCK_COUNT   4096    // 16MB总容量
#define FLASH_PAGE_SIZE     256     // 256字节页

// 块设备接口函数
int block_device_read(const struct lfs_config *c, lfs_block_t block,
                     lfs_off_t off, void *buffer, lfs_size_t 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);

int block_device_erase(const struct lfs_config *c, lfs_block_t block);

int block_device_sync(const struct lfs_config *c);

// 初始化函数
int lfs_port_init(void);

#endif // LFS_PORT_H

2.2 实现块设备接口

创建文件 lfs_port.c

#include "lfs_port.h"
#include "w25qxx.h"
#include <string.h>

/**
 * @brief  读取Flash数据
 * @param  c: LittleFS配置
 * @param  block: 块号
 * @param  off: 块内偏移
 * @param  buffer: 数据缓冲区
 * @param  size: 读取大小
 * @retval 0:成功, 负数:错误
 */
int block_device_read(const struct lfs_config *c, lfs_block_t block,
                     lfs_off_t off, void *buffer, lfs_size_t size)
{
    // 计算Flash地址
    uint32_t addr = block * c->block_size + off;

    // 读取Flash数据
    if (W25QXX_Read((uint8_t*)buffer, addr, size) != 0) {
        return LFS_ERR_IO;
    }

    return LFS_ERR_OK;
}

/**
 * @brief  编程(写入)Flash数据
 * @param  c: LittleFS配置
 * @param  block: 块号
 * @param  off: 块内偏移
 * @param  buffer: 数据缓冲区
 * @param  size: 写入大小
 * @retval 0:成功, 负数:错误
 */
int block_device_prog(const struct lfs_config *c, lfs_block_t block,
                     lfs_off_t off, const void *buffer, lfs_size_t size)
{
    // 计算Flash地址
    uint32_t addr = block * c->block_size + off;

    // 写入Flash数据
    if (W25QXX_Write((uint8_t*)buffer, addr, size) != 0) {
        return LFS_ERR_IO;
    }

    return LFS_ERR_OK;
}

/**
 * @brief  擦除Flash块
 * @param  c: LittleFS配置
 * @param  block: 块号
 * @retval 0:成功, 负数:错误
 */
int block_device_erase(const struct lfs_config *c, lfs_block_t block)
{
    // 计算Flash地址
    uint32_t addr = block * c->block_size;

    // 擦除4KB扇区
    if (W25QXX_Erase_Sector(addr) != 0) {
        return LFS_ERR_IO;
    }

    return LFS_ERR_OK;
}

/**
 * @brief  同步Flash数据
 * @param  c: LittleFS配置
 * @retval 0:成功, 负数:错误
 */
int block_device_sync(const struct lfs_config *c)
{
    // W25QXX不需要显式同步
    // 如果使用带缓存的Flash,这里需要刷新缓存
    return LFS_ERR_OK;
}

/**
 * @brief  初始化块设备
 * @retval 0:成功, 负数:错误
 */
int lfs_port_init(void)
{
    // 初始化SPI Flash
    if (W25QXX_Init() != 0) {
        return -1;
    }

    // 检测Flash芯片
    uint16_t id = W25QXX_ReadID();
    if (id == 0 || id == 0xFFFF) {
        return -1;
    }

    return 0;
}

代码说明: - block_device_read: 从Flash读取数据,支持任意偏移和大小 - block_device_prog: 向Flash写入数据,必须先擦除 - block_device_erase: 擦除整个块(4KB) - block_device_sync: 确保数据写入Flash(某些Flash需要)

步骤3:初始化和挂载文件系统

3.1 配置LittleFS

创建文件 lfs_app.c

#include "lfs.h"
#include "lfs_port.h"
#include <stdio.h>

// LittleFS对象
static lfs_t lfs;
static lfs_file_t file;

// 配置结构
static const struct lfs_config cfg = {
    // 块设备操作
    .read  = block_device_read,
    .prog  = block_device_prog,
    .erase = block_device_erase,
    .sync  = block_device_sync,

    // 块设备配置
    .read_size = 256,
    .prog_size = 256,
    .block_size = 4096,
    .block_count = 4096,
    .cache_size = 256,
    .lookahead_size = 16,
    .block_cycles = 500,
};

/**
 * @brief  初始化文件系统
 * @retval 0:成功, 负数:错误
 */
int lfs_app_init(void)
{
    int err;

    // 初始化块设备
    if (lfs_port_init() != 0) {
        printf("Failed to initialize block device\n");
        return -1;
    }

    // 尝试挂载文件系统
    err = lfs_mount(&lfs, &cfg);

    // 如果挂载失败,格式化后重新挂载
    if (err) {
        printf("File system not found, formatting...\n");

        err = lfs_format(&lfs, &cfg);
        if (err) {
            printf("Failed to format: %d\n", err);
            return err;
        }

        err = lfs_mount(&lfs, &cfg);
        if (err) {
            printf("Failed to mount after format: %d\n", err);
            return err;
        }

        printf("File system formatted and mounted\n");
    } else {
        printf("File system mounted successfully\n");
    }

    return 0;
}

/**
 * @brief  卸载文件系统
 * @retval 0:成功, 负数:错误
 */
int lfs_app_deinit(void)
{
    return lfs_unmount(&lfs);
}

3.2 在main函数中初始化

#include "lfs_app.h"

int main(void)
{
    // 系统初始化
    HAL_Init();
    SystemClock_Config();

    // 初始化外设
    MX_GPIO_Init();
    MX_SPI1_Init();
    MX_USART1_UART_Init();

    printf("LittleFS Demo Starting...\n");

    // 初始化文件系统
    if (lfs_app_init() != 0) {
        printf("Failed to initialize file system\n");
        Error_Handler();
    }

    // 应用程序代码
    // ...

    while (1) {
        // 主循环
    }
}

预期输出

LittleFS Demo Starting...
File system mounted successfully

或首次运行时:

LittleFS Demo Starting...
File system not found, formatting...
File system formatted and mounted

步骤4:文件操作实战

4.1 创建和写入文件

/**
 * @brief  写入文件示例
 */
void lfs_write_example(void)
{
    int err;
    const char *filename = "test.txt";
    const char *content = "Hello, LittleFS!\n";

    // 打开文件(创建或覆盖)
    err = lfs_file_open(&lfs, &file, filename,
                       LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
    if (err) {
        printf("Failed to open file: %d\n", err);
        return;
    }

    // 写入数据
    lfs_ssize_t written = lfs_file_write(&lfs, &file, content, strlen(content));
    if (written < 0) {
        printf("Failed to write file: %d\n", (int)written);
        lfs_file_close(&lfs, &file);
        return;
    }

    printf("Written %d bytes to %s\n", (int)written, filename);

    // 关闭文件
    err = lfs_file_close(&lfs, &file);
    if (err) {
        printf("Failed to close file: %d\n", err);
    }
}

文件打开标志: - LFS_O_RDONLY: 只读模式 - LFS_O_WRONLY: 只写模式 - LFS_O_RDWR: 读写模式 - LFS_O_CREAT: 文件不存在时创建 - LFS_O_EXCL: 文件存在时失败 - LFS_O_TRUNC: 截断文件为0 - LFS_O_APPEND: 追加模式

4.2 读取文件

/**
 * @brief  读取文件示例
 */
void lfs_read_example(void)
{
    int err;
    const char *filename = "test.txt";
    char buffer[128];

    // 打开文件(只读)
    err = lfs_file_open(&lfs, &file, filename, LFS_O_RDONLY);
    if (err) {
        printf("Failed to open file: %d\n", err);
        return;
    }

    // 读取数据
    lfs_ssize_t read_size = lfs_file_read(&lfs, &file, buffer, sizeof(buffer) - 1);
    if (read_size < 0) {
        printf("Failed to read file: %d\n", (int)read_size);
        lfs_file_close(&lfs, &file);
        return;
    }

    // 添加字符串结束符
    buffer[read_size] = '\0';

    printf("Read %d bytes from %s:\n%s\n", (int)read_size, filename, buffer);

    // 关闭文件
    lfs_file_close(&lfs, &file);
}

4.3 追加写入

/**
 * @brief  追加写入示例
 */
void lfs_append_example(void)
{
    int err;
    const char *filename = "log.txt";
    char log_entry[64];

    // 生成日志条目
    snprintf(log_entry, sizeof(log_entry), "[%lu] System running\n", HAL_GetTick());

    // 打开文件(追加模式)
    err = lfs_file_open(&lfs, &file, filename,
                       LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND);
    if (err) {
        printf("Failed to open file: %d\n", err);
        return;
    }

    // 写入日志
    lfs_ssize_t written = lfs_file_write(&lfs, &file, log_entry, strlen(log_entry));
    if (written < 0) {
        printf("Failed to write log: %d\n", (int)written);
    } else {
        printf("Log written: %s", log_entry);
    }

    // 关闭文件
    lfs_file_close(&lfs, &file);
}

4.4 文件定位

/**
 * @brief  文件定位示例
 */
void lfs_seek_example(void)
{
    int err;
    const char *filename = "data.bin";
    uint8_t data[16];

    // 打开文件
    err = lfs_file_open(&lfs, &file, filename, LFS_O_RDONLY);
    if (err) {
        printf("Failed to open file: %d\n", err);
        return;
    }

    // 获取文件大小
    lfs_soff_t size = lfs_file_size(&lfs, &file);
    printf("File size: %d bytes\n", (int)size);

    // 定位到文件中间
    lfs_soff_t pos = lfs_file_seek(&lfs, &file, size / 2, LFS_SEEK_SET);
    printf("Seeked to position: %d\n", (int)pos);

    // 读取数据
    lfs_ssize_t read_size = lfs_file_read(&lfs, &file, data, sizeof(data));
    printf("Read %d bytes from middle of file\n", (int)read_size);

    // 定位到文件开头
    lfs_file_rewind(&lfs, &file);
    printf("Rewound to beginning\n");

    // 关闭文件
    lfs_file_close(&lfs, &file);
}

定位模式: - LFS_SEEK_SET: 从文件开头定位 - LFS_SEEK_CUR: 从当前位置定位 - LFS_SEEK_END: 从文件末尾定位

步骤5:目录操作

5.1 创建目录

/**
 * @brief  创建目录示例
 */
void lfs_mkdir_example(void)
{
    int err;

    // 创建目录
    err = lfs_mkdir(&lfs, "logs");
    if (err && err != LFS_ERR_EXIST) {
        printf("Failed to create directory: %d\n", err);
        return;
    }

    printf("Directory 'logs' created\n");

    // 创建嵌套目录
    err = lfs_mkdir(&lfs, "data");
    if (err && err != LFS_ERR_EXIST) {
        printf("Failed to create directory: %d\n", err);
        return;
    }

    err = lfs_mkdir(&lfs, "data/sensors");
    if (err && err != LFS_ERR_EXIST) {
        printf("Failed to create subdirectory: %d\n", err);
        return;
    }

    printf("Nested directories created\n");
}

5.2 遍历目录

/**
 * @brief  遍历目录示例
 */
void lfs_dir_list_example(void)
{
    int err;
    lfs_dir_t dir;
    struct lfs_info info;

    // 打开根目录
    err = lfs_dir_open(&lfs, &dir, "/");
    if (err) {
        printf("Failed to open directory: %d\n", err);
        return;
    }

    printf("Directory listing:\n");
    printf("%-20s %10s %s\n", "Name", "Size", "Type");
    printf("----------------------------------------\n");

    // 遍历目录项
    while (true) {
        err = lfs_dir_read(&lfs, &dir, &info);
        if (err < 0) {
            printf("Failed to read directory: %d\n", err);
            break;
        }

        // 读取完毕
        if (err == 0) {
            break;
        }

        // 跳过 . 和 ..
        if (strcmp(info.name, ".") == 0 || strcmp(info.name, "..") == 0) {
            continue;
        }

        // 打印文件信息
        const char *type = (info.type == LFS_TYPE_DIR) ? "DIR" : "FILE";
        printf("%-20s %10d %s\n", info.name, (int)info.size, type);
    }

    // 关闭目录
    lfs_dir_close(&lfs, &dir);
}

5.3 删除文件和目录

/**
 * @brief  删除文件和目录示例
 */
void lfs_remove_example(void)
{
    int err;

    // 删除文件
    err = lfs_remove(&lfs, "test.txt");
    if (err) {
        printf("Failed to remove file: %d\n", err);
    } else {
        printf("File 'test.txt' removed\n");
    }

    // 删除空目录
    err = lfs_remove(&lfs, "logs");
    if (err) {
        printf("Failed to remove directory: %d\n", err);
    } else {
        printf("Directory 'logs' removed\n");
    }
}

5.4 重命名文件

/**
 * @brief  重命名文件示例
 */
void lfs_rename_example(void)
{
    int err;

    // 重命名文件
    err = lfs_rename(&lfs, "old_name.txt", "new_name.txt");
    if (err) {
        printf("Failed to rename file: %d\n", err);
    } else {
        printf("File renamed successfully\n");
    }

    // 移动文件到其他目录
    err = lfs_rename(&lfs, "file.txt", "data/file.txt");
    if (err) {
        printf("Failed to move file: %d\n", err);
    } else {
        printf("File moved successfully\n");
    }
}

步骤6:文件属性操作

6.1 获取文件信息

/**
 * @brief  获取文件信息示例
 */
void lfs_stat_example(void)
{
    int err;
    struct lfs_info info;

    // 获取文件信息
    err = lfs_stat(&lfs, "test.txt", &info);
    if (err) {
        printf("Failed to stat file: %d\n", err);
        return;
    }

    // 打印文件信息
    printf("File information:\n");
    printf("  Name: %s\n", info.name);
    printf("  Type: %s\n", (info.type == LFS_TYPE_DIR) ? "Directory" : "File");
    printf("  Size: %d bytes\n", (int)info.size);
}

6.2 设置和获取自定义属性

/**
 * @brief  文件属性示例
 */
void lfs_attr_example(void)
{
    int err;
    const char *filename = "config.dat";

    // 定义自定义属性
    struct {
        uint32_t version;
        uint32_t crc;
    } attr_data = {
        .version = 0x0100,
        .crc = 0x12345678
    };

    // 打开文件
    err = lfs_file_open(&lfs, &file, filename,
                       LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
    if (err) {
        printf("Failed to open file: %d\n", err);
        return;
    }

    // 设置自定义属性
    err = lfs_setattr(&lfs, filename, 0x01, &attr_data, sizeof(attr_data));
    if (err) {
        printf("Failed to set attribute: %d\n", err);
    } else {
        printf("Attribute set successfully\n");
    }

    lfs_file_close(&lfs, &file);

    // 读取自定义属性
    struct {
        uint32_t version;
        uint32_t crc;
    } read_attr;

    lfs_ssize_t attr_size = lfs_getattr(&lfs, filename, 0x01,
                                       &read_attr, sizeof(read_attr));
    if (attr_size < 0) {
        printf("Failed to get attribute: %d\n", (int)attr_size);
    } else {
        printf("Attribute read: version=0x%04X, crc=0x%08X\n",
               (unsigned int)read_attr.version, (unsigned int)read_attr.crc);
    }
}

步骤7:掉电保护测试

7.1 掉电保护原理

LittleFS的掉电保护基于以下机制:

元数据对(Metadata Pairs)

┌─────────────────┐
│  Metadata A     │  ← 当前有效
│  - 版本: 5      │
│  - CRC: OK      │
└─────────────────┘
┌─────────────────┐
│  Metadata B     │  ← 备份
│  - 版本: 4      │
│  - CRC: OK      │
└─────────────────┘

写入流程: 1. 写入新数据到备份块 2. 更新元数据版本号 3. 写入CRC校验 4. 原子性切换指针

恢复流程: 1. 检查两个元数据块的CRC 2. 选择版本号较新且CRC正确的块 3. 如果都损坏,使用最后一个好的备份

7.2 掉电保护测试代码

/**
 * @brief  掉电保护测试
 */
void lfs_power_loss_test(void)
{
    int err;
    const char *filename = "critical.dat";
    uint32_t counter = 0;

    printf("Power loss protection test\n");
    printf("Press button to simulate power loss\n");

    while (1) {
        // 打开文件
        err = lfs_file_open(&lfs, &file, filename,
                           LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
        if (err) {
            printf("Failed to open file: %d\n", err);
            break;
        }

        // 写入计数器
        lfs_file_write(&lfs, &file, &counter, sizeof(counter));

        // 同步数据(确保写入Flash)
        lfs_file_sync(&lfs, &file);

        // 关闭文件
        lfs_file_close(&lfs, &file);

        printf("Written counter: %lu\n", counter);
        counter++;

        // 延时
        HAL_Delay(1000);

        // 检查按键(模拟掉电)
        if (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin) == GPIO_PIN_RESET) {
            printf("Simulating power loss...\n");
            // 不卸载文件系统,直接重启
            NVIC_SystemReset();
        }
    }
}

/**
 * @brief  验证掉电后的数据
 */
void lfs_verify_after_power_loss(void)
{
    int err;
    const char *filename = "critical.dat";
    uint32_t counter;

    // 打开文件
    err = lfs_file_open(&lfs, &file, filename, LFS_O_RDONLY);
    if (err) {
        printf("File not found (expected on first run)\n");
        return;
    }

    // 读取计数器
    lfs_ssize_t read_size = lfs_file_read(&lfs, &file, &counter, sizeof(counter));
    if (read_size == sizeof(counter)) {
        printf("Recovered counter after power loss: %lu\n", counter);
        printf("Data integrity verified!\n");
    } else {
        printf("Failed to read counter\n");
    }

    lfs_file_close(&lfs, &file);
}

测试步骤: 1. 运行掉电保护测试程序 2. 等待写入几次数据 3. 按下按键模拟掉电(系统重启) 4. 系统重启后验证数据完整性 5. 应该能读取到最后成功写入的计数器值

步骤8:性能优化

8.1 缓存优化

/**
 * @brief  优化的配置(增大缓存)
 */
static const struct lfs_config cfg_optimized = {
    .read  = block_device_read,
    .prog  = block_device_prog,
    .erase = block_device_erase,
    .sync  = block_device_sync,

    // 增大缓存提升性能
    .read_size = 256,
    .prog_size = 256,
    .block_size = 4096,
    .block_count = 4096,
    .cache_size = 512,      // 增大到512字节
    .lookahead_size = 32,   // 增大预读
    .block_cycles = 500,
};

缓存大小影响: - 更大的缓存 → 更好的性能,更多RAM占用 - 建议:cache_size = block_size / 8 到 block_size / 4 - 最小值:必须 ≥ read_size 和 prog_size

8.2 批量写入优化

/**
 * @brief  批量写入优化示例
 */
void lfs_batch_write_example(void)
{
    int err;
    const char *filename = "data.bin";
    uint8_t buffer[1024];

    // 打开文件
    err = lfs_file_open(&lfs, &file, filename,
                       LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
    if (err) {
        printf("Failed to open file: %d\n", err);
        return;
    }

    // 批量写入(减少sync次数)
    for (int i = 0; i < 100; i++) {
        // 准备数据
        memset(buffer, i, sizeof(buffer));

        // 写入数据
        lfs_file_write(&lfs, &file, buffer, sizeof(buffer));

        // 每10次同步一次(而不是每次都同步)
        if ((i + 1) % 10 == 0) {
            lfs_file_sync(&lfs, &file);
        }
    }

    // 最后同步
    lfs_file_sync(&lfs, &file);

    // 关闭文件
    lfs_file_close(&lfs, &file);

    printf("Batch write completed\n");
}

8.3 预分配空间

/**
 * @brief  预分配文件空间
 */
void lfs_preallocate_example(void)
{
    int err;
    const char *filename = "large_file.dat";

    // 打开文件
    err = lfs_file_open(&lfs, &file, filename,
                       LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
    if (err) {
        printf("Failed to open file: %d\n", err);
        return;
    }

    // 预分配10MB空间
    err = lfs_file_truncate(&lfs, &file, 10 * 1024 * 1024);
    if (err) {
        printf("Failed to preallocate space: %d\n", err);
        lfs_file_close(&lfs, &file);
        return;
    }

    printf("Space preallocated\n");

    // 后续写入会更快
    // ...

    lfs_file_close(&lfs, &file);
}

8.4 性能测试

/**
 * @brief  性能测试
 */
void lfs_performance_test(void)
{
    int err;
    const char *filename = "perf_test.dat";
    uint8_t buffer[1024];
    uint32_t start_tick, end_tick;

    printf("Performance Test\n");

    // 写入测试
    start_tick = HAL_GetTick();

    err = lfs_file_open(&lfs, &file, filename,
                       LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
    if (err) {
        printf("Failed to open file: %d\n", err);
        return;
    }

    // 写入1MB数据
    for (int i = 0; i < 1024; i++) {
        lfs_file_write(&lfs, &file, buffer, sizeof(buffer));
    }

    lfs_file_close(&lfs, &file);

    end_tick = HAL_GetTick();
    uint32_t write_time = end_tick - start_tick;
    float write_speed = 1024.0 / (write_time / 1000.0);  // KB/s

    printf("Write: 1MB in %lu ms (%.2f KB/s)\n", write_time, write_speed);

    // 读取测试
    start_tick = HAL_GetTick();

    err = lfs_file_open(&lfs, &file, filename, LFS_O_RDONLY);
    if (err) {
        printf("Failed to open file: %d\n", err);
        return;
    }

    // 读取1MB数据
    for (int i = 0; i < 1024; i++) {
        lfs_file_read(&lfs, &file, buffer, sizeof(buffer));
    }

    lfs_file_close(&lfs, &file);

    end_tick = HAL_GetTick();
    uint32_t read_time = end_tick - start_tick;
    float read_speed = 1024.0 / (read_time / 1000.0);  // KB/s

    printf("Read: 1MB in %lu ms (%.2f KB/s)\n", read_time, read_speed);
}

步骤9:调试和故障排除

9.1 启用调试输出

lfs_config.h 中启用调试:

// 调试配置
#define LFS_DEBUG   1
#define LFS_WARN    1
#define LFS_ERROR   1

// 自定义调试输出
#define LFS_DEBUG_PRINTF(fmt, ...) printf("[LFS DEBUG] " fmt, ##__VA_ARGS__)
#define LFS_WARN_PRINTF(fmt, ...)  printf("[LFS WARN] " fmt, ##__VA_ARGS__)
#define LFS_ERROR_PRINTF(fmt, ...) printf("[LFS ERROR] " fmt, ##__VA_ARGS__)

9.2 文件系统检查

/**
 * @brief  文件系统完整性检查
 */
void lfs_fsck(void)
{
    int err;

    printf("Checking file system integrity...\n");

    // LittleFS会在挂载时自动检查
    // 如果有问题会返回错误
    err = lfs_unmount(&lfs);
    if (err) {
        printf("Unmount failed: %d\n", err);
    }

    err = lfs_mount(&lfs, &cfg);
    if (err) {
        printf("File system corrupted: %d\n", err);
        printf("Attempting to format...\n");

        err = lfs_format(&lfs, &cfg);
        if (err) {
            printf("Format failed: %d\n", err);
        } else {
            printf("File system formatted successfully\n");
            lfs_mount(&lfs, &cfg);
        }
    } else {
        printf("File system is healthy\n");
    }
}

9.3 获取文件系统信息

/**
 * @brief  获取文件系统统计信息
 */
void lfs_get_info(void)
{
    lfs_ssize_t used_blocks = lfs_fs_size(&lfs);
    if (used_blocks < 0) {
        printf("Failed to get fs size: %d\n", (int)used_blocks);
        return;
    }

    uint32_t total_size = cfg.block_count * cfg.block_size;
    uint32_t used_size = used_blocks * cfg.block_size;
    uint32_t free_size = total_size - used_size;

    printf("File System Information:\n");
    printf("  Total size: %lu KB\n", total_size / 1024);
    printf("  Used size:  %lu KB\n", used_size / 1024);
    printf("  Free size:  %lu KB\n", free_size / 1024);
    printf("  Usage:      %lu%%\n", (used_size * 100) / total_size);
}

9.4 常见错误处理

/**
 * @brief  错误码转字符串
 */
const char* lfs_error_to_string(int err)
{
    switch (err) {
        case LFS_ERR_OK:        return "No error";
        case LFS_ERR_IO:        return "I/O error";
        case LFS_ERR_CORRUPT:   return "Corrupted";
        case LFS_ERR_NOENT:     return "No entry";
        case LFS_ERR_EXIST:     return "Entry exists";
        case LFS_ERR_NOTDIR:    return "Not a directory";
        case LFS_ERR_ISDIR:     return "Is a directory";
        case LFS_ERR_NOTEMPTY:  return "Directory not empty";
        case LFS_ERR_BADF:      return "Bad file descriptor";
        case LFS_ERR_FBIG:      return "File too large";
        case LFS_ERR_INVAL:     return "Invalid parameter";
        case LFS_ERR_NOSPC:     return "No space left";
        case LFS_ERR_NOMEM:     return "No memory";
        case LFS_ERR_NOATTR:    return "No attribute";
        case LFS_ERR_NAMETOOLONG: return "Name too long";
        default:                return "Unknown error";
    }
}

/**
 * @brief  安全的文件操作包装
 */
int safe_file_write(const char *filename, const void *data, size_t size)
{
    int err;

    err = lfs_file_open(&lfs, &file, filename,
                       LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
    if (err) {
        printf("Failed to open %s: %s\n", filename, lfs_error_to_string(err));
        return err;
    }

    lfs_ssize_t written = lfs_file_write(&lfs, &file, data, size);
    if (written < 0) {
        printf("Failed to write %s: %s\n", filename, lfs_error_to_string((int)written));
        lfs_file_close(&lfs, &file);
        return (int)written;
    }

    if ((size_t)written != size) {
        printf("Partial write to %s: %d/%d bytes\n", filename, (int)written, (int)size);
    }

    err = lfs_file_close(&lfs, &file);
    if (err) {
        printf("Failed to close %s: %s\n", filename, lfs_error_to_string(err));
        return err;
    }

    return LFS_ERR_OK;
}

完整示例项目

项目:数据记录器

实现一个完整的数据记录系统,包含配置管理、数据记录和查询功能。

/**
 * @file   data_logger.c
 * @brief  基于LittleFS的数据记录器
 */

#include "lfs.h"
#include "lfs_app.h"
#include <stdio.h>
#include <time.h>

#define LOG_DIR         "logs"
#define CONFIG_FILE     "config.dat"
#define MAX_LOG_SIZE    (100 * 1024)  // 100KB

/**
 * 配置结构
 */
typedef struct {
    uint32_t magic;           // 魔数
    uint32_t version;         // 版本号
    uint32_t log_interval;    // 记录间隔(秒)
    uint32_t max_log_files;   // 最大日志文件数
    uint8_t  enable_compress; // 是否压缩
} config_t;

/**
 * 数据记录结构
 */
typedef struct {
    uint32_t timestamp;       // 时间戳
    float    temperature;     // 温度
    float    humidity;        // 湿度
    uint16_t pressure;        // 气压
    uint8_t  battery;         // 电池电量
} log_entry_t;

static config_t g_config;
static lfs_file_t g_log_file;
static char g_current_log[64];

/**
 * @brief  加载配置
 */
int load_config(void)
{
    int err;
    lfs_file_t file;

    err = lfs_file_open(&lfs, &file, CONFIG_FILE, LFS_O_RDONLY);
    if (err) {
        // 配置文件不存在,使用默认配置
        printf("Config not found, using defaults\n");
        g_config.magic = 0x12345678;
        g_config.version = 1;
        g_config.log_interval = 60;
        g_config.max_log_files = 10;
        g_config.enable_compress = 0;
        return 0;
    }

    lfs_ssize_t read_size = lfs_file_read(&lfs, &file, &g_config, sizeof(g_config));
    lfs_file_close(&lfs, &file);

    if (read_size != sizeof(g_config) || g_config.magic != 0x12345678) {
        printf("Invalid config, using defaults\n");
        return -1;
    }

    printf("Config loaded: interval=%lu, max_files=%lu\n",
           g_config.log_interval, g_config.max_log_files);

    return 0;
}

/**
 * @brief  保存配置
 */
int save_config(void)
{
    int err;
    lfs_file_t file;

    err = lfs_file_open(&lfs, &file, CONFIG_FILE,
                       LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
    if (err) {
        printf("Failed to open config file: %d\n", err);
        return err;
    }

    lfs_file_write(&lfs, &file, &g_config, sizeof(g_config));
    lfs_file_sync(&lfs, &file);
    lfs_file_close(&lfs, &file);

    printf("Config saved\n");
    return 0;
}

/**
 * @brief  创建新的日志文件
 */
int create_new_log_file(void)
{
    int err;
    time_t now = time(NULL);
    struct tm *tm_info = localtime(&now);

    // 确保日志目录存在
    err = lfs_mkdir(&lfs, LOG_DIR);
    if (err && err != LFS_ERR_EXIST) {
        printf("Failed to create log directory: %d\n", err);
        return err;
    }

    // 生成日志文件名
    snprintf(g_current_log, sizeof(g_current_log),
             "%s/%04d%02d%02d_%02d%02d%02d.log",
             LOG_DIR,
             tm_info->tm_year + 1900,
             tm_info->tm_mon + 1,
             tm_info->tm_mday,
             tm_info->tm_hour,
             tm_info->tm_min,
             tm_info->tm_sec);

    // 打开新日志文件
    err = lfs_file_open(&lfs, &g_log_file, g_current_log,
                       LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND);
    if (err) {
        printf("Failed to create log file: %d\n", err);
        return err;
    }

    printf("Created log file: %s\n", g_current_log);
    return 0;
}

/**
 * @brief  写入日志条目
 */
int write_log_entry(const log_entry_t *entry)
{
    lfs_ssize_t written;

    // 检查文件大小
    lfs_soff_t size = lfs_file_size(&lfs, &g_log_file);
    if (size >= MAX_LOG_SIZE) {
        // 关闭当前文件,创建新文件
        lfs_file_close(&lfs, &g_log_file);
        if (create_new_log_file() != 0) {
            return -1;
        }
    }

    // 写入日志条目
    written = lfs_file_write(&lfs, &g_log_file, entry, sizeof(log_entry_t));
    if (written != sizeof(log_entry_t)) {
        printf("Failed to write log entry\n");
        return -1;
    }

    // 定期同步
    static int sync_counter = 0;
    if (++sync_counter >= 10) {
        lfs_file_sync(&lfs, &g_log_file);
        sync_counter = 0;
    }

    return 0;
}

/**
 * @brief  读取日志文件
 */
int read_log_file(const char *filename)
{
    int err;
    lfs_file_t file;
    log_entry_t entry;
    int count = 0;

    err = lfs_file_open(&lfs, &file, filename, LFS_O_RDONLY);
    if (err) {
        printf("Failed to open log file: %d\n", err);
        return err;
    }

    printf("Reading log file: %s\n", filename);
    printf("%-12s %-8s %-8s %-8s %-8s\n",
           "Timestamp", "Temp", "Humid", "Press", "Battery");
    printf("--------------------------------------------------------\n");

    while (1) {
        lfs_ssize_t read_size = lfs_file_read(&lfs, &file, &entry, sizeof(entry));
        if (read_size == 0) {
            break;  // EOF
        }

        if (read_size != sizeof(entry)) {
            printf("Corrupted entry at position %d\n", count);
            break;
        }

        printf("%-12lu %-8.2f %-8.2f %-8u %-8u\n",
               entry.timestamp,
               entry.temperature,
               entry.humidity,
               entry.pressure,
               entry.battery);

        count++;
    }

    lfs_file_close(&lfs, &file);
    printf("Total entries: %d\n", count);

    return 0;
}

/**
 * @brief  列出所有日志文件
 */
int list_log_files(void)
{
    int err;
    lfs_dir_t dir;
    struct lfs_info info;

    err = lfs_dir_open(&lfs, &dir, LOG_DIR);
    if (err) {
        printf("Failed to open log directory: %d\n", err);
        return err;
    }

    printf("Log files:\n");
    printf("%-30s %10s\n", "Filename", "Size");
    printf("----------------------------------------\n");

    while (true) {
        err = lfs_dir_read(&lfs, &dir, &info);
        if (err < 0) {
            break;
        }
        if (err == 0) {
            break;
        }

        if (info.type == LFS_TYPE_REG) {
            printf("%-30s %10d\n", info.name, (int)info.size);
        }
    }

    lfs_dir_close(&lfs, &dir);
    return 0;
}

/**
 * @brief  清理旧日志文件
 */
int cleanup_old_logs(void)
{
    // 实现日志文件清理逻辑
    // 保留最新的N个文件,删除旧文件
    printf("Cleaning up old log files...\n");
    // TODO: 实现清理逻辑
    return 0;
}

/**
 * @brief  主函数示例
 */
void data_logger_demo(void)
{
    log_entry_t entry;

    // 初始化文件系统
    if (lfs_app_init() != 0) {
        printf("Failed to initialize file system\n");
        return;
    }

    // 加载配置
    load_config();

    // 创建日志文件
    if (create_new_log_file() != 0) {
        printf("Failed to create log file\n");
        return;
    }

    // 模拟数据记录
    for (int i = 0; i < 100; i++) {
        // 读取传感器数据(这里使用模拟数据)
        entry.timestamp = HAL_GetTick() / 1000;
        entry.temperature = 25.0f + (rand() % 100) / 10.0f;
        entry.humidity = 60.0f + (rand() % 200) / 10.0f;
        entry.pressure = 1013 + (rand() % 20);
        entry.battery = 80 + (rand() % 20);

        // 写入日志
        if (write_log_entry(&entry) != 0) {
            printf("Failed to write log entry\n");
            break;
        }

        printf("Logged: T=%.1f H=%.1f P=%u B=%u\n",
               entry.temperature, entry.humidity,
               entry.pressure, entry.battery);

        // 延时
        HAL_Delay(g_config.log_interval * 1000);
    }

    // 关闭日志文件
    lfs_file_close(&lfs, &g_log_file);

    // 列出所有日志文件
    list_log_files();

    // 读取最新的日志文件
    read_log_file(g_current_log);

    // 清理旧日志
    cleanup_old_logs();

    // 卸载文件系统
    lfs_app_deinit();
}

故障排除

问题1:挂载失败

现象

Failed to mount: -84 (LFS_ERR_CORRUPT)

可能原因: - Flash未正确初始化 - Flash芯片损坏 - 配置参数错误 - 文件系统损坏

解决方法: 1. 检查Flash连接和初始化

uint16_t id = W25QXX_ReadID();
printf("Flash ID: 0x%04X\n", id);

  1. 验证配置参数

    // 确保参数匹配Flash规格
    .block_size = 4096,  // 必须等于擦除块大小
    .block_count = 4096, // 总容量/块大小
    

  2. 重新格式化

    lfs_format(&lfs, &cfg);
    lfs_mount(&lfs, &cfg);
    

问题2:写入失败

现象

Failed to write file: -28 (LFS_ERR_NOSPC)

可能原因: - Flash空间不足 - 磨损均衡导致可用空间减少 - 文件碎片过多

解决方法: 1. 检查可用空间

lfs_get_info();

  1. 删除不需要的文件

    lfs_remove(&lfs, "old_file.txt");
    

  2. 重新格式化(会丢失所有数据)

    lfs_format(&lfs, &cfg);
    

问题3:性能慢

现象: - 读写速度很慢 - 操作延迟高

可能原因: - 缓存太小 - 频繁同步 - Flash性能差

解决方法: 1. 增大缓存

.cache_size = 512,      // 增大缓存
.lookahead_size = 32,   // 增大预读

  1. 减少同步频率

    // 批量写入后再同步
    for (int i = 0; i < 100; i++) {
        lfs_file_write(&lfs, &file, data, size);
    }
    lfs_file_sync(&lfs, &file);  // 最后同步一次
    

  2. 使用更快的Flash芯片

问题4:掉电后数据丢失

现象: - 掉电后文件损坏 - 最后写入的数据丢失

可能原因: - 未正确同步 - 掉电时机不当 - Flash写入未完成

解决方法: 1. 确保及时同步

lfs_file_write(&lfs, &file, data, size);
lfs_file_sync(&lfs, &file);  // 立即同步

  1. 使用事务性写入

    // 打开文件
    lfs_file_open(&lfs, &file, "data.txt", LFS_O_WRONLY | LFS_O_CREAT);
    // 写入数据
    lfs_file_write(&lfs, &file, data, size);
    // 同步(确保写入Flash)
    lfs_file_sync(&lfs, &file);
    // 关闭文件
    lfs_file_close(&lfs, &file);
    

  2. 添加电源监控

    // 检测电压下降,立即同步
    if (battery_voltage < THRESHOLD) {
        lfs_file_sync(&lfs, &file);
    }
    

总结

通过本教程,你学习了:

  • ✅ LittleFS的设计原理和核心特性
  • ✅ 如何移植LittleFS到嵌入式系统
  • ✅ 实现块设备接口的方法
  • ✅ 文件和目录操作的完整API
  • ✅ 掉电保护机制的原理和测试
  • ✅ 性能优化的技巧和方法
  • ✅ 调试和故障排除的方法
  • ✅ 完整的数据记录器项目实现

关键要点

  1. 掉电安全
  2. 所有操作都是原子性的
  3. 元数据冗余存储
  4. 及时同步确保数据安全

  5. 资源占用

  6. RAM占用极低(几KB)
  7. 适合资源受限的MCU
  8. 可配置的缓存大小

  9. 性能优化

  10. 增大缓存提升性能
  11. 批量操作减少同步
  12. 预分配空间提高效率

  13. 可靠性

  14. CRC校验保护
  15. 自动错误恢复
  16. 磨损均衡延长寿命

进阶挑战

尝试以下挑战来巩固学习:

  1. 挑战1:实现日志轮转
  2. 自动创建新日志文件
  3. 保留最新N个文件
  4. 自动删除旧文件

  5. 挑战2:添加数据压缩

  6. 集成压缩算法(如LZ4)
  7. 压缩日志数据
  8. 节省存储空间

  9. 挑战3:实现文件加密

  10. 使用AES加密文件
  11. 保护敏感数据
  12. 密钥管理

  13. 挑战4:多线程支持

  14. 添加互斥锁保护
  15. 支持多任务访问
  16. 避免竞态条件

  17. 挑战5:实现文件系统备份

  18. 备份到SD卡
  19. 恢复文件系统
  20. 数据迁移

下一步

建议继续学习:

参考资料

官方资源: 1. LittleFS GitHub - 官方仓库 2. LittleFS设计文档 - 设计原理 3. LittleFS规范 - 技术规范

技术文章: 1. ARM Mbed OS文档 - LittleFS使用指南 2. ESP-IDF文档 - LittleFS集成 3. Zephyr RTOS文档 - 文件系统支持

相关标准: 1. POSIX文件系统接口 2. Flash存储器规范 3. 嵌入式文件系统最佳实践

开发工具: 1. littlefs-fuse - FUSE挂载工具 2. mklfs - 镜像创建工具 3. Flash编程工具 - 备份和恢复

社区资源: 1. LittleFS讨论区 2. 嵌入式开发论坛 3. Stack Overflow相关问题

附录A:完整API参考

文件系统操作

// 格式化文件系统
int lfs_format(lfs_t *lfs, const struct lfs_config *config);

// 挂载文件系统
int lfs_mount(lfs_t *lfs, const struct lfs_config *config);

// 卸载文件系统
int lfs_unmount(lfs_t *lfs);

// 获取文件系统大小(已使用的块数)
lfs_ssize_t lfs_fs_size(lfs_t *lfs);

// 遍历文件系统(用于垃圾回收)
int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);

文件操作

// 打开文件
int lfs_file_open(lfs_t *lfs, lfs_file_t *file, const char *path, int flags);

// 关闭文件
int lfs_file_close(lfs_t *lfs, lfs_file_t *file);

// 同步文件
int lfs_file_sync(lfs_t *lfs, lfs_file_t *file);

// 读取文件
lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, void *buffer, lfs_size_t size);

// 写入文件
lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, const void *buffer, lfs_size_t size);

// 定位文件
lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, lfs_soff_t off, int whence);

// 截断文件
int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size);

// 获取文件位置
lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file);

// 回到文件开头
int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file);

// 获取文件大小
lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file);

目录操作

// 创建目录
int lfs_mkdir(lfs_t *lfs, const char *path);

// 打开目录
int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path);

// 关闭目录
int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir);

// 读取目录项
int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info);

// 定位目录
int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off);

// 获取目录位置
lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir);

// 回到目录开头
int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir);

文件系统操作

// 删除文件或目录
int lfs_remove(lfs_t *lfs, const char *path);

// 重命名文件或目录
int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath);

// 获取文件信息
int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info);

// 设置属性
int lfs_setattr(lfs_t *lfs, const char *path, uint8_t type, const void *buffer, lfs_size_t size);

// 获取属性
lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, uint8_t type, void *buffer, lfs_size_t size);

// 删除属性
int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type);

附录B:错误码参考

错误码 说明
LFS_ERR_OK 0 成功
LFS_ERR_IO -5 I/O错误
LFS_ERR_CORRUPT -84 文件系统损坏
LFS_ERR_NOENT -2 文件或目录不存在
LFS_ERR_EXIST -17 文件或目录已存在
LFS_ERR_NOTDIR -20 不是目录
LFS_ERR_ISDIR -21 是目录
LFS_ERR_NOTEMPTY -39 目录非空
LFS_ERR_BADF -9 无效的文件描述符
LFS_ERR_FBIG -27 文件太大
LFS_ERR_INVAL -22 无效参数
LFS_ERR_NOSPC -28 空间不足
LFS_ERR_NOMEM -12 内存不足
LFS_ERR_NOATTR -61 属性不存在
LFS_ERR_NAMETOOLONG -36 文件名太长

附录C:配置参数说明

参数 类型 说明 推荐值
read_size uint32_t 最小读取单位 256
prog_size uint32_t 最小编程单位 256
block_size uint32_t 擦除块大小 4096
block_count uint32_t 总块数 根据Flash容量
cache_size uint32_t 缓存大小 256-512
lookahead_size uint32_t 预读大小 16-32
block_cycles int32_t 磨损均衡周期 500
name_max uint32_t 最大文件名长度 255
file_max uint32_t 最大文件大小 2147483647
attr_max uint32_t 最大属性大小 1022

反馈:如果你在学习过程中遇到问题,欢迎在评论区留言或提交Issue!

贡献:发现错误或有改进建议?欢迎提交Pull Request!

许可:本教程采用 CC BY-SA 4.0 许可协议。