查看: 2452|回复: 1

[原创] 【S32K146 RT-thread】基于内部PFLASH的littlefs适配

[复制链接]
  • TA的每日心情
    奋斗
    前天 21:18
  • 签到天数: 856 天

    连续签到: 1 天

    [LV.10]以坛为家III

    69

    主题

    3257

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    10338
    最后登录
    2025-7-25
    发表于 2024-1-28 08:41:24 | 显示全部楼层 |阅读模式
    本帖最后由 andeyqi 于 2024-2-1 10:01 编辑

    LittleFS是一个应用于单片机内部flash和外挂NOR flash的文件系统。由于它相比传统的FAT文件系统更适合于小型嵌入式系统,具有以下特点:
    • 掉电恢复能力: 设计用于处理随机电源故障。所有文件操作都有很强的写时拷贝保证,如果断电,文件系统将恢复到上一次已知的良好状态。
    • 动态磨损均衡: 设计考虑到闪存,并提供动态块磨损均衡。此外,littlefs可以检测坏块并在它们周围工作。
    • 有限RAM/ROM: 被设计为使用少量内存。RAM的使用是严格限制的,这意味着RAM的使用不会随着文件系统的增长而改变。文件系统不包含无界递归,动态内存仅限于可静态提供的可配置缓冲区。
    官方的详细介绍参照此链接(https://github.com/littlefs-project/littlefs/
    S32K146 内部的Pflash 资源大小为1M,这个大小对普通的嵌入式开发资源是有很大的空闲的,本次试验基于内部的pflash 将后512K资源划分为文件系统分区,使用littlefs 进行管理,我们修改链接脚本把后512K资源保留出来给文件系统使用,本次试验使用的IAR环境,link file 修改如下:

    17690.jpg


    27952.jpg


    littlefs 移植适配依赖物理层的配置结构体如下:
    1. // Configuration provided during initialization of the littlefs
    2. struct lfs_config {
    3.     // Opaque user provided context that can be used to pass
    4.     // information to the block device operations
    5.     void *context;

    6.     // Read a region in a block. Negative error codes are propagated
    7.     // to the user.
    8.     int (*read)(const struct lfs_config *c, lfs_block_t block,
    9.             lfs_off_t off, void *buffer, lfs_size_t size);

    10.     // Program a region in a block. The block must have previously
    11.     // been erased. Negative error codes are propagated to the user.
    12.     // May return LFS_ERR_CORRUPT if the block should be considered bad.
    13.     int (*prog)(const struct lfs_config *c, lfs_block_t block,
    14.             lfs_off_t off, const void *buffer, lfs_size_t size);

    15.     // Erase a block. A block must be erased before being programmed.
    16.     // The state of an erased block is undefined. Negative error codes
    17.     // are propagated to the user.
    18.     // May return LFS_ERR_CORRUPT if the block should be considered bad.
    19.     int (*erase)(const struct lfs_config *c, lfs_block_t block);

    20.     // Sync the state of the underlying block device. Negative error codes
    21.     // are propagated to the user.
    22.     int (*sync)(const struct lfs_config *c);

    23. #ifdef LFS_THREADSAFE
    24.     // Lock the underlying block device. Negative error codes
    25.     // are propagated to the user.
    26.     int (*lock)(const struct lfs_config *c);

    27.     // Unlock the underlying block device. Negative error codes
    28.     // are propagated to the user.
    29.     int (*unlock)(const struct lfs_config *c);
    30. #endif

    31.     // Minimum size of a block read in bytes. All read operations will be a
    32.     // multiple of this value.
    33.     lfs_size_t read_size;

    34.     // Minimum size of a block program in bytes. All program operations will be
    35.     // a multiple of this value.
    36.     lfs_size_t prog_size;

    37.     // Size of an erasable block in bytes. This does not impact ram consumption
    38.     // and may be larger than the physical erase size. However, non-inlined
    39.     // files take up at minimum one block. Must be a multiple of the read and
    40.     // program sizes.
    41.     lfs_size_t block_size;

    42.     // Number of erasable blocks on the device.
    43.     lfs_size_t block_count;

    44.     // Number of erase cycles before littlefs evicts metadata logs and moves
    45.     // the metadata to another block. Suggested values are in the
    46.     // range 100-1000, with large values having better performance at the cost
    47.     // of less consistent wear distribution.
    48.     //
    49.     // Set to -1 to disable block-level wear-leveling.
    50.     int32_t block_cycles;

    51.     // Size of block caches in bytes. Each cache buffers a portion of a block in
    52.     // RAM. The littlefs needs a read cache, a program cache, and one additional
    53.     // cache per file. Larger caches can improve performance by storing more
    54.     // data and reducing the number of disk accesses. Must be a multiple of the
    55.     // read and program sizes, and a factor of the block size.
    56.     lfs_size_t cache_size;

    57.     // Size of the lookahead buffer in bytes. A larger lookahead buffer
    58.     // increases the number of blocks found during an allocation pass. The
    59.     // lookahead buffer is stored as a compact bitmap, so each byte of RAM
    60.     // can track 8 blocks. Must be a multiple of 8.
    61.     lfs_size_t lookahead_size;

    62.     // Optional statically allocated read buffer. Must be cache_size.
    63.     // By default lfs_malloc is used to allocate this buffer.
    64.     void *read_buffer;

    65.     // Optional statically allocated program buffer. Must be cache_size.
    66.     // By default lfs_malloc is used to allocate this buffer.
    67.     void *prog_buffer;

    68.     // Optional statically allocated lookahead buffer. Must be lookahead_size
    69.     // and aligned to a 32-bit boundary. By default lfs_malloc is used to
    70.     // allocate this buffer.
    71.     void *lookahead_buffer;

    72.     // Optional upper limit on length of file names in bytes. No downside for
    73.     // larger names except the size of the info struct which is controlled by
    74.     // the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in
    75.     // superblock and must be respected by other littlefs drivers.
    76.     lfs_size_t name_max;

    77.     // Optional upper limit on files in bytes. No downside for larger files
    78.     // but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored
    79.     // in superblock and must be respected by other littlefs drivers.
    80.     lfs_size_t file_max;

    81.     // Optional upper limit on custom attributes in bytes. No downside for
    82.     // larger attributes size but must be <= LFS_ATTR_MAX. Defaults to
    83.     // LFS_ATTR_MAX when zero.
    84.     lfs_size_t attr_max;

    85.     // Optional upper limit on total space given to metadata pairs in bytes. On
    86.     // devices with large blocks (e.g. 128kB) setting this to a low size (2-8kB)
    87.     // can help bound the metadata compaction time. Must be <= block_size.
    88.     // Defaults to block_size when zero.
    89.     lfs_size_t metadata_max;
    90. };
    复制代码

    主要包含物理层设备的读写/擦除,及FLASH最小编程块属性配置,查看S32K146 的features 可以知道,最小擦除的sector 为4096字节,最小写操作size 为8字节,我们按照littlefs 依赖的配置结构实现对应的而函数。
    • read 接口实现:


    1. int lfs_mflash_read(const struct lfs_config *lfsc, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size)
    2. {
    3.     struct lfs_mflash_ctx *ctx;
    4.     uint32_t flash_addr;

    5.     assert(lfsc);
    6.     ctx = (struct lfs_mflash_ctx *)lfsc->context;
    7.     assert(ctx);

    8.     flash_addr = ctx->start_addr + block * lfsc->block_size + off;
    9.    
    10.     for(lfs_size_t i=0; i < size; i++)
    11.     {
    12.         ((int8_t *)buffer)[i] = *((__IO int8_t*)flash_addr);
    13.         flash_addr++;
    14.     }

    15.     return LFS_ERR_OK;
    16. }
    复制代码
    • prog接口实现:

    1. int32_t mflash_drv_program(uint32_t addr,uint32_t size,uint8_t * pdata)
    2. {
    3.     status_t ret;

    4.     ret = FLASH_DRV_Program(&pSSDConfig,addr,size,pdata);

    5.     return ret == STATUS_SUCCESS ? 0 : -1;
    6. }

    7. int lfs_mflash_prog(
    8.     const struct lfs_config *lfsc, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size)
    9. {
    10.     struct lfs_mflash_ctx *ctx;
    11.     uint32_t flash_addr;
    12.     int32_t ret;

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

    14.     flash_addr = ctx->start_addr + block * lfsc->block_size + off;
    15.    
    16.     ret = mflash_drv_program(flash_addr,size,(uint8_t *)buffer);
    17.    
    18.     return (ret == 0) ? LFS_ERR_OK : LFS_ERR_IO;
    19. }
    复制代码
    • erase 接口实现;
    1. <blockquote>int32_t mflash_drv_erase(uint32_t dest,uint32_t size)
    复制代码
    对应的配置结构如下:
    22543.jpg


    适配接口已经对应完成,littlefs 会依赖动态malloc/free 内存接口,本次是基于RT-thread 系统lfs_util.h需做如下修改:

    31364.jpg

    至此Littfs 依赖的适配接口已经完成,我们追加shell  测试命令来验证littlefs的基本创建删除文件及文件夹及读写删除试验,对应的shell 测试命令代码如下:
    1. #include "drv_mflash.h"
    2. #include <stdio.h>
    3. #include <stdlib.h>
    4. #include <stdint.h>
    5. #include <string.h>
    6. #include <rtthread.h>

    7. #define SHELL_Printf rt_kprintf
    8. #define PRINTF    rt_kprintf

    9. /*******************************************************************************
    10. * Variables
    11. ******************************************************************************/

    12. lfs_t lfs;
    13. struct lfs_config cfg;
    14. int lfs_mounted;


    15. static void format(int argc, char **argv)
    16. {
    17.     int res;

    18.     if (lfs_mounted)
    19.     {
    20.         SHELL_Printf("LFS is mounted, please unmount it first.\r\n");
    21.         return;
    22.     }

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

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

    33.     return;
    34. }

    35. MSH_CMD_EXPORT(format,"lfs format api");

    36. static void  mount(int argc, char **argv)
    37. {
    38.     int res;

    39.     if (lfs_mounted)
    40.     {
    41.         SHELL_Printf("LFS already mounted\r\n");
    42.         return;
    43.     }

    44.     res = lfs_mount(&lfs, &cfg);
    45.     if (res)
    46.     {
    47.         PRINTF("\rError mounting LFS\r\n");
    48.     }
    49.     else
    50.     {
    51.         lfs_mounted = 1;
    52.     }

    53.     return;
    54. }
    55. MSH_CMD_EXPORT(mount,lfs mount api);

    56. static void unmount(int argc, char **argv)
    57. {
    58.     int res;

    59.     if (!lfs_mounted)
    60.     {
    61.         SHELL_Printf("LFS not mounted\r\n");
    62.         return;
    63.     }

    64.     res = lfs_unmount(&lfs);
    65.     if (res)
    66.     {
    67.         PRINTF("\rError unmounting LFS: %i\r\n", res);
    68.     }

    69.     lfs_mounted = 0;
    70.     return;
    71. }
    72. MSH_CMD_EXPORT(unmount,lfs unmount api);

    73. static void cd(int argc, char **argv)
    74. {
    75.     SHELL_Printf(
    76.         "There is no concept of current directory in this example.\r\nPlease always specify the full path.\r\n");
    77.     return;
    78. }
    79. MSH_CMD_EXPORT(cd,lfs cd api);


    80. static void lls(int argc,  char **argv)
    81. {
    82.     int res;
    83.     char *path;
    84.     lfs_dir_t dir;
    85.     struct lfs_info info;
    86.     int files;
    87.     int dirs;

    88.     if (!lfs_mounted)
    89.     {
    90.         SHELL_Printf("LFS not mounted\r\n");
    91.         return;
    92.     }

    93.     if (argc > 2)
    94.     {
    95.         SHELL_Printf("Invalid number of parameters\r\n");
    96.         return;
    97.     }

    98.     if (argc < 2)
    99.     {
    100.         path = "/";
    101.     }
    102.     else
    103.     {
    104.         path = argv[1];
    105.     }

    106.     /* open the directory */
    107.     res = lfs_dir_open(&lfs, &dir, path);
    108.     if (res)
    109.     {
    110.         PRINTF("\rError opening directory: %i\r\n", res);
    111.         return;
    112.     }

    113.     PRINTF(" Directory of %s\r\n", path);
    114.     files = 0;
    115.     dirs  = 0;

    116.     /* iterate until end of directory */
    117.     while ((res = lfs_dir_read(&lfs, &dir, &info)) != 0)
    118.     {
    119.         if (res < 0)
    120.         {
    121.             /* break the loop in case of an error */
    122.             PRINTF("\rError reading directory: %i\r\n", res);
    123.             break;
    124.         }

    125.         if (info.type == LFS_TYPE_REG)
    126.         {
    127.             SHELL_Printf("%8d %s\r\n", info.size, info.name);
    128.             files++;
    129.         }
    130.         else if (info.type == LFS_TYPE_DIR)
    131.         {
    132.             SHELL_Printf("%     DIR %s\r\n", info.name);
    133.             dirs++;
    134.         }
    135.         else
    136.         {
    137.             SHELL_Printf("%???\r\n");
    138.         }
    139.     }

    140.     res = lfs_dir_close(&lfs, &dir);
    141.     if (res)
    142.     {
    143.         PRINTF("\rError closing directory: %i\r\n", res);
    144.         return;
    145.     }

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

    147.     return;
    148. }
    149. MSH_CMD_EXPORT(lls,lfs ls api);


    150. static void rm(int argc, char **argv)
    151. {
    152.     int res;

    153.     if (!lfs_mounted)
    154.     {
    155.         SHELL_Printf("LFS not mounted\r\n");
    156.         return;
    157.     }

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

    159.     if (res)
    160.     {
    161.         PRINTF("\rError while removing: %i\r\n", res);
    162.     }

    163.     return;
    164. }
    165. MSH_CMD_EXPORT(rm,lfs rm api);


    166. static void lmkdir(int argc, char **argv)
    167. {
    168.     int res;

    169.     if (!lfs_mounted)
    170.     {
    171.         SHELL_Printf("LFS not mounted\r\n");
    172.         return;
    173.     }

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

    175.     if (res)
    176.     {
    177.         PRINTF("\rError creating directory: %i\r\n", res);
    178.     }

    179.     return;
    180. }
    181. MSH_CMD_EXPORT(lmkdir,lfs mkdir api);

    182. static void write(int argc, char **argv)
    183. {
    184.     int res;
    185.     lfs_file_t file;

    186.     if (!lfs_mounted)
    187.     {
    188.         SHELL_Printf("LFS not mounted\r\n");
    189.         return;
    190.     }

    191.     res = lfs_file_open(&lfs, &file, argv[1], LFS_O_WRONLY | LFS_O_APPEND | LFS_O_CREAT);
    192.     if (res)
    193.     {
    194.         PRINTF("\rError opening file: %i\r\n", res);
    195.         return;
    196.     }

    197.     res = lfs_file_write(&lfs, &file, argv[2], strlen(argv[2]));
    198.     if (res > 0)
    199.         res = lfs_file_write(&lfs, &file, "\r\n", 2);

    200.     if (res < 0)
    201.     {
    202.         PRINTF("\rError writing file: %i\r\n", res);
    203.     }

    204.     res = lfs_file_close(&lfs, &file);
    205.     if (res)
    206.     {
    207.         PRINTF("\rError closing file: %i\r\n", res);
    208.     }

    209.     return;
    210. }
    211. MSH_CMD_EXPORT(write,lfs write api);


    212. static void cat(int argc, char **argv)
    213. {
    214.     int res;
    215.     lfs_file_t file;
    216.     uint8_t buf[16];

    217.     if (!lfs_mounted)
    218.     {
    219.         SHELL_Printf("LFS not mounted\r\n");
    220.         return;
    221.     }

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

    228.     do
    229.     {
    230.         res = lfs_file_read(&lfs, &file, buf, sizeof(buf));
    231.         if (res < 0)
    232.         {
    233.             PRINTF("\rError reading file: %i\r\n", res);
    234.             break;
    235.         }
    236.         if(res > 0)
    237.         {
    238.             buf[res] = '\0';
    239.             PRINTF("%s",(char *)buf);
    240.         }
    241.     } while (res);

    242.     res = lfs_file_close(&lfs, &file);
    243.     if (res)
    244.     {
    245.         PRINTF("\rError closing file: %i\r\n", res);
    246.     }

    247.     return;
    248. }
    249. MSH_CMD_EXPORT(cat,lfs cat api);

    250. static void lfsinit(int argc, char **argv)
    251. {
    252.     mflash_drv_init();
    253.     lfs_get_default_config(&cfg);
    254.     return;
    255. }
    256. MSH_CMD_EXPORT(lfsinit,lfs init api);

    257. static void df(int argc, char **argv)
    258. {
    259.     printf("used block %d total %d\r\n",lfs_fs_size(&lfs),cfg.block_count);
    260.     return;
    261. }
    262. MSH_CMD_EXPORT(df,lfs init api);
    复制代码
    至此准备工作已经完成,我们通过shell 来验证文件系统的功能,shell 命令验证文件系统已经可以通过操作文件文件夹。
    20240128-083943.gif
    RT-thread 软件包也有littlefs 的软件包支持,本次实验暂未使用软件包,后续使用RT-thread 软件包来验证littlefs功能。
    该会员没有填写今日想说内容.
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2025-6-11 09:06
  • 签到天数: 83 天

    连续签到: 1 天

    [LV.6]常住居民II

    7

    主题

    3446

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    3045
    最后登录
    2025-7-15
    发表于 2024-1-30 09:53:15 | 显示全部楼层
    该会员没有填写今日想说内容.
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /3 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2025-7-27 20:00 , Processed in 0.093650 second(s), 21 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表