这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 行业应用 » 汽车电子 » 【S32K146】S32K146片内flash适配littleFS文件系统

共5条 1/1 1 跳转至

【S32K146】S32K146片内flash适配littleFS文件系统

高工
2025-06-21 07:11:01     打赏

【简介】

我们在上一篇基于S32K146 完成了片内flash 驱动的编写(【S32K146】S32K146片内flash编程),我们在此基础上继续适配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环境。

【LittleFS适配】

LittleFS 的适配主要是需要实现文件系统依赖物理层的实现,LittleFS  的适配物理层需要配置的结构体如下:

// Configuration provided during initialization of the littlefs
struct 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. Defaults to block_count stored
    // on disk when zero.
    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.
    lfs_size_t lookahead_size;

    // Threshold for metadata compaction during lfs_fs_gc in bytes. Metadata
    // pairs that exceed this threshold will be compacted during lfs_fs_gc.
    // Defaults to ~88% block_size when zero, though the default may change
    // in the future.
    //
    // Note this only affects lfs_fs_gc. Normal compactions still only occur
    // when full.
    //
    // Set to -1 to disable metadata compaction during lfs_fs_gc.
    lfs_size_t compact_thresh;

    // 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.
    // 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 or name_max stored on
    // disk when zero.
    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 or file_max stored
    // on disk when zero.
    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 or attr_max stored on disk 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;

    // Optional upper limit on inlined files in bytes. Inlined files live in
    // metadata and decrease storage requirements, but may be limited to
    // improve metadata-related performance. Must be <= cache_size, <=
    // attr_max, and <= block_size/8. Defaults to the largest possible
    // inline_max when zero.
    //
    // Set to -1 to disable inlined files.
    lfs_size_t inline_max;

#ifdef LFS_MULTIVERSION
    // On-disk version to use when writing in the form of 16-bit major version
    // + 16-bit minor version. This limiting metadata to what is supported by
    // older minor versions. Note that some features will be lost. Defaults to 
    // to the most recent minor version when zero.
    uint32_t disk_version;
#endif
};

我们将S32K146 的对应参数及flash 操作函数对接到此配置结构体中,对应的接口配置如下:

/* Definitions for BOARD_InitPeripherals functional group */
/* Maximum block read size definition */
#define LITTLEFS_READ_SIZE 16
/* Block count definition */
#define LITTLEFS_BLOCK_COUNT 128
/* Block cycles definition */
#define LITTLEFS_BLOCK_CYCLES 100
/* Minimum block cache size definition */
#define LITTLEFS_CACHE_SIZE 512
/* Minimum lookahead buffer size definition */
#define LITTLEFS_LOOKAHEAD_SIZE 16
/* Block starting address definition */
#define LITTLEFS_START_ADDR 0x00080000

/********************************************************************************************************
 *                                  Private Variable Definitions                                        *
 *******************************************************************************************************/
static flash_ssd_config_t Pss_InitConfig_Lfs;

/********************************************************************************************************
 *                                    Private Type Declarations                                         *
 *******************************************************************************************************/
struct lfs_mflash_ctx
{
    uint32_t start_addr;
    flash_ssd_config_t * Pss_InitConfig;
};

/********************************************************************************************************
 *                                  Private Function Declarations                                       *
 *******************************************************************************************************/
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;

    ctx = (struct lfs_mflash_ctx *)lfsc->context;
    flash_addr = ctx->start_addr + block * lfsc->block_size + off;

    memcpy(buffer, (void *)flash_addr, size);

    return LFS_ERR_OK;
}


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;
    status_t ret;

    ctx = (struct lfs_mflash_ctx *)lfsc->context;
    flash_addr = ctx->start_addr + block * lfsc->block_size + off;

    ret = FLASH_DRV_Program(ctx->Pss_InitConfig,flash_addr,size,buffer);

    if (ret != STATUS_SUCCESS)
        return LFS_ERR_IO;

    return LFS_ERR_OK;    
}

int lfs_mflash_erase(const struct lfs_config *lfsc, lfs_block_t block)
{
    struct lfs_mflash_ctx *ctx;
    uint32_t flash_addr;
    status_t ret;

    ctx = (struct lfs_mflash_ctx *)lfsc->context;
    flash_addr = ctx->start_addr + block * lfsc->block_size;
    ret = FLASH_DRV_EraseSector(ctx->Pss_InitConfig,flash_addr,FEATURE_FLS_PF_BLOCK_SECTOR_SIZE);

    if (ret != STATUS_SUCCESS)
        return LFS_ERR_IO;

    return LFS_ERR_OK;  
}

int lfs_mflash_sync(const struct lfs_config *lfsc)
{
    return LFS_ERR_OK;
}

/* LittleFS context */
static struct lfs_mflash_ctx LittleFS_ctx;
const struct lfs_config LittleFS_config = {
  .context = (void*)&LittleFS_ctx,
  .read = lfs_mflash_read,
  .prog = lfs_mflash_prog,
  .erase = lfs_mflash_erase,
  .sync = lfs_mflash_sync,
#ifdef LFS_THREADSAFE
  .lock = lfs_mflash_lock,
  .unlock = lfs_mflash_unlock,
#endif
  .read_size = LITTLEFS_READ_SIZE,
  .prog_size = FEATURE_FLS_PF_BLOCK_WRITE_UNIT_SIZE,
  .block_size = FEATURE_FLS_PF_BLOCK_SECTOR_SIZE,
  .block_count = LITTLEFS_BLOCK_COUNT,
  .block_cycles = LITTLEFS_BLOCK_CYCLES,
  .cache_size = LITTLEFS_CACHE_SIZE,
  .lookahead_size = LITTLEFS_LOOKAHEAD_SIZE
};

调用一下接口初始化S32K146 FLASH

/********************************************************************************************************
 *                                  Global Function Declarations                                        *
 *******************************************************************************************************/

static int lfs_flash_init(void)
{
    status_t ret;
    /* Init lpi2c master */
    ret = FLASH_DRV_Init(&Flash_InitConfig0,&Pss_InitConfig_Lfs);

    if(ret != STATUS_SUCCESS)
    {
        LOG_E("lfs FTFC init failed %x.",ret);
    }
    else
    {
        LOG_I("lfs FTFC init OK.");
    }

    LittleFS_ctx.Pss_InitConfig = &Pss_InitConfig_Lfs;
    LittleFS_ctx.start_addr = LITTLEFS_START_ADDR;

    return 1;
}

INIT_BOARD_EXPORT_LEVEL1(lfs_flash_init);

【添加测试命令】

适配完LittleFS 我们添加如下的测试命令来验证LittleFS的功能。

添加format 命令来格式化内部flash

static unsigned int format(char argc, char **argv)
{
    int res;

    if (lfs_mounted)
    {
        printf("LFS is mounted, please unmount it first.\r\n");
        return 1;
    }

    if (argc != 2 || strcmp(argv[1], "yes"))
    {
        printf("Are you sure? Please issue command \"format yes\" to proceed.\r\n");
        return 1;
    }

    res = lfs_format(&lfs, &LittleFS_config);
    if (res)
    {
        printf("\rError formatting LFS: %d\r\n", res);
    }

    return 0;
}

添加mount /unmout 来配置flash 的挂载路径

static unsigned int mount(char argc, char **argv)
{
    int res;

    if (lfs_mounted)
    {
        printf("LFS already mounted\r\n");
        return 0;
    }

    res = lfs_mount(&lfs, &LittleFS_config);
    if (res)
    {
        printf("\rError mounting LFS\r\n");
    }
    else
    {
        lfs_mounted = 1;
    }

    return 0;
}

static unsigned int unmount(char argc, char **argv)
{
    int res;

    if (!lfs_mounted)
    {
        printf("LFS not mounted\r\n");
        return 0;
    }

    res = lfs_unmount(&lfs);
    if (res)
    {
        printf("\rError unmounting LFS: %i\r\n", res);
    }

    lfs_mounted = 0;
    return 0;
}

添加 ls 来查看目录下的文件信息

static unsigned int ls(char argc, char **argv)
{
    int res;
    char *path;
    lfs_dir_t dir;
    struct lfs_info info;
    int files;
    int dirs;

    if (!lfs_mounted)
    {
        printf("LFS not mounted\r\n");
        return 0;
    }

    if (argc > 2)
    {
        printf("Invalid number of parameters\r\n");
        return 1;
    }

    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 0;
    }

    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)
        {
            printf("%8d %s\r\n", info.size, info.name);
            files++;
        }
        else if (info.type == LFS_TYPE_DIR)
        {
            printf("     DIR %s\r\n", info.name);
            dirs++;
        }
        else
        {
            printf("%???\r\n");
        }
    }

    res = lfs_dir_close(&lfs, &dir);
    if (res)
    {
        printf("\rError closing directory: %i\r\n", res);
        return 0;
    }

    printf(" %d File(s), %d Dir(s)\r\n", files, dirs);

    return 0;
}


添加rm 来删除文件文件夹

static unsigned int rm(char argc, char **argv)
{
    int res;

    if (!lfs_mounted)
    {
        printf("LFS not mounted\r\n");
        return 0;
    }

    res = lfs_remove(&lfs, argv[1]);

    if (res)
    {
        printf("\rError while removing: %i\r\n", res);
    }

    return 0;
}

mkdir 来创建文件夹

static unsigned int mkdir(char argc, char **argv)
{
    int res;

    if (!lfs_mounted)
    {
        printf("LFS not mounted\r\n");
        return 0;
    }

    res = lfs_mkdir(&lfs, argv[1]);

    if (res)
    {
        printf("\rError creating directory: %i\r\n", res);
    }

    return 0;
}

write/cat 来读写文件

static unsigned int write(char argc, char **argv)
{
    int res;
    lfs_file_t file;

    if (!lfs_mounted)
    {
        printf("LFS not mounted\r\n");
        return 0;
    }

    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 0;
    }

    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 0;
}

static unsigned int cat(char argc, char **argv)
{
    int res;
    lfs_file_t file;
    uint8_t buf[16];

    if (!lfs_mounted)
    {
        printf("LFS not mounted\r\n");
        return 0;
    }

    res = lfs_file_open(&lfs, &file, argv[1], LFS_O_RDONLY);
    if (res)
    {
        printf("\rError opening file: %i\r\n", res);
        return 0;
    }

    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)
            printf("%s",(char *)buf);
    } while (res);

    res = lfs_file_close(&lfs, &file);
    if (res)
    {
        printf("\rError closing file: %i\r\n", res);
    }

    return 0;
}

【功能验证】

通过上述的测试命令格式化flash 后 挂载到文件系统后通过mkdir 创建文夹后ls 查看当前目录文件,结果如下,创建的文件夹可以通过ls 获取

image.png

通过 write 创建文件并写入数据通过cat 读取验证,写入数据和读取的一致,文件按照预期的写入和读取了

image.png

image.png





专家
2025-06-21 08:24:43     打赏
2楼

谢谢分享


专家
2025-06-21 17:39:21     打赏
3楼

感谢分享


专家
2025-06-21 18:05:29     打赏
4楼

感谢分享


专家
2025-06-21 18:06:39     打赏
5楼

感谢分享


共5条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]