【简介】
我们在上一篇基于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 获取

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


我要赚赏金
