请选择 进入手机版 | 继续访问电脑版

你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【STM32MP135-DK】裸机开发适配Llittlefs

[复制链接]
andey 发布时间:2024-1-31 11:10

littlefs 一个为微控制器设计的小故障安全文件系统,具有以下特点:

  • 掉电恢复能力: 设计用于处理随机电源故障。所有文件操作都有很强的写时拷贝保证,如果断电,文件系统将恢复到上一次已知的良好状态。
  • 动态磨损均衡: 设计考虑到闪存,并提供动态块磨损均衡。此外,littlefs可以检测坏块并在它们周围工作。
  • 有限RAM/ROM: 被设计为使用少量内存。RAM的使用是严格限制的,这意味着RAM的使用不会随着文件系统的增长而改变。文件系统不包含无界递归,动态内存仅限于可静态提供的可配置缓冲区。

官方的详细介绍参照此链接(https://github.com/littlefs-project/littlefs/) 本试验目的主要适配littlefs MP135板子上上自带了512M 的DDR资源,我们修改链接脚本把最后的2M的资源分配给Littlefs 进行管理,little 需要使用堆资源同时把对资源也扩大点,链接脚本修改如下:

微信截图_20240131111650.png

移植适配

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.
    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. Must be a multiple of 8.
    lfs_size_t lookahead_size;

    // 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
    // and aligned to a 32-bit boundary. 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 when zero. Stored in
    // superblock and must be respected by other littlefs drivers.
    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 when zero. Stored
    // in superblock and must be respected by other littlefs drivers.
    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 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;
};

主要包含物理层设备的读写/擦除最小编程块属性配置,本地使用ddr模拟flash,最小擦除的sector 配置为4096字节,最小读写操作1字节,我们按照littlefs 依赖的配置结构实现对应的而函数。

  • read 接口实现:
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;

    assert(lfsc);
    ctx = (struct lfs_mflash_ctx *)lfsc->context;
    assert(ctx);

    flash_addr = ctx->start_addr + block * lfsc->block_size + off;

    for(lfs_size_t i=0; i < size; i++)
    {
        ((int8_t *)buffer)[i] = *((volatile int8_t*)flash_addr);
        flash_addr++;
    }

    return LFS_ERR_OK;
}
  • prog 接口实现:
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;

    assert(lfsc);
    ctx = (struct lfs_mflash_ctx *)lfsc->context;
    assert(ctx);

    flash_addr = ctx->start_addr + block * lfsc->block_size + off;

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

    return LFS_ERR_OK;
}
  • erase 接口实现:
int lfs_mflash_erase(const struct lfs_config *lfsc, lfs_block_t block)
{ 
    uint32_t page_addr;
    struct lfs_mflash_ctx *ctx;

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

    memset(((void *)page_addr),0xff,4096);

    return LFS_ERR_OK;

}

对应配置如下:

微信截图_20240131112817.png

测试验证

我们基于https://shequ.stmicroelectronics.cn/forum.php?mod=viewthread&tid=642247&page=1&extra=#pid2647582 shell 基础上添加测试命令来验证littlefs 功能,测试命令代码如下:

#include "lfs_ddr.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "littleshell.h"

#define SHELL_Printf printf
#define PRINTF    printf

/*******************************************************************************
 * Variables
 ******************************************************************************/

lfs_t lfs;
struct lfs_config cfg;
int lfs_mounted;


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

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

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

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

    return 1;
}

LTSH_FUNCTION_EXPORT(format,"lfs format api");

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

    if (lfs_mounted)
    {
        SHELL_Printf("LFS already mounted\r\n");
        return 1;
    }

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

    return 1;
}
LTSH_FUNCTION_EXPORT(mount,"lfs mount api");

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

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

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

    lfs_mounted = 0;
    return 1;
}
LTSH_FUNCTION_EXPORT(unmount,"lfs unmount api");

static unsigned int cd(char argc, char **argv)
{
    SHELL_Printf(
        "There is no concept of current directory in this example.\r\nPlease always specify the full path.\r\n");
    return 1;
}
LTSH_FUNCTION_EXPORT(cd,"lfs cd api");


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

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

    if (argc > 2)
    {
        SHELL_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 1;
    }

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

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

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

    return 1;
}
LTSH_FUNCTION_EXPORT(lls,"lfs ls api");


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

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

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

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

    return 1;
}
LTSH_FUNCTION_EXPORT(rm,"lfs rm api");


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

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

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

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

    return 1;
}
LTSH_FUNCTION_EXPORT(lmkdir,"lfs mkdir api");

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

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

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

    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 1;
}
LTSH_FUNCTION_EXPORT(write,"lfs write api");


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

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

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

    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)
        {
            buf[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 1;
}
LTSH_FUNCTION_EXPORT(cat,"lfs cat api");

static unsigned int lfsinit(char argc, char **argv)
{
    lfs_get_default_config(&cfg);
    return 1;
}
LTSH_FUNCTION_EXPORT(lfsinit,"lfs init api");


static unsigned int df(char argc, char **argv)
{
    printf("used block %d\r\n",lfs_fs_size(&lfs));
    return 1;
}
LTSH_FUNCTION_EXPORT(df,"lfs init api");

 void getcwd(char * buff, int len)
 {
     if(lfs_mounted)
     {  
        buff[0] = '/';
        buff[1] = ' ';
        buff[2] = '\0';
     }
 }


 void fs_init(void)
 {
    int res;
    lfs_get_default_config(&cfg);

    res = lfs_mount(&lfs, &cfg);
    if (res)
    {
        PRINTF("\rError mounting LFS\r\n");

        res = lfs_format(&lfs, &cfg);
        if (res)
        {
            PRINTF("\rError formatting LFS: %d\r\n", res);
        }
        else
        {
            res = lfs_mount(&lfs, &cfg);
            if(res)
              PRINTF("\rError mounting LFS\r\n");
        }
    }
    else
    {
        lfs_mounted = 1;
    }
 };

代码对应完后工程下整体的littlefs 相关代码如下:

微信截图_20240131113323.png

将测试程序下载到板子中运行,执行测试命令格式化文件系统,mount操作创建文件夹及文件的写入后回读验证都是ok 的,基于DDR的littlefs适配基本已经完成。

20240131-114535.gif

代码路径:

[https://gitee.com/andeyqi/stm32-mp135/tree/master/STM32Cube_FW_MP13_V1.0.0/Projects/STM32MP135C-DK/Examples/UART/UART_Receive_Transmit_Console

收藏 评论0 发布时间:2024-1-31 11:10

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版