使用FAL分区管理与easyflash变量管理

RTThread之FLASH操作详细介绍 FAL组件的安装在我的其他章节中已经介绍过了,主要是先要将FLASH分块(区),然后在分块(区)中去处理。->下面是我的分区,因为用到了串口升级功能,所以分了好多区(块)。->这里面的设置放在了.h文件里面-> 分块处理函数。 阅读详情

1.

FAL组件

1.1

什么是FAL

FAL (Flash Abstraction Layer) Flash 抽象层,是对 Flash 及基于 Flash 的分区进行管理、操作的抽象层,对上层统一了 Flash 及 分区操作的 API (框架图如下所示),并具有以下特性:

  • 支持静态可配置的分区表,并可关联多个 Flash 设备;

  • 分区表支持 自动装载 。避免在多固件项目,分区表被多次定义的问题;

  • 代码精简,对操作系统 无依赖 ,可运行于裸机平台,比如对资源有一定要求的 Bootloader;

  • 统一的操作接口。保证了文件系统、OTA、NVM(例如:EasyFlash https://github.com/armink-rtt-pkgs/EasyFlash) 等对 Flash 有一定依赖的组件,底层 Flash 驱动的可重用性;

  • 自带基于 Finsh/MSH 的测试命令,可以通过 Shell 按字节寻址的方式操作(读写擦) Flash 或分区,方便开发者进行调试、测试;

903a26aeccaafc689d4558e8ed898092.png

通过上图我们也可以清晰明了了,看到,FAL抽象层向下可以通过Flash硬件进行统一管理,当然也可以使用SFUD框架(串行Flash通用驱动库,这部分RT-Thread已完成官方框架的移植同时提供多个应用历程),而对上也可以使用如DFS、NVM提供的Flash硬件统一访问接口,方便用户更加直接方便对底层Flash硬件的访问操作。

注:非易失性存储器 (NVM):在芯片电源关闭期间保存存储在其中的数据。因此,它被用于没有磁盘的便携式设备中的内存,以及用于可移动存储卡等用途。主要类型有:非易失性半导体存储器 (Non-volatile semiconductor memory, NVSM) 将数据存储在浮栅存储单元中,每个单元都由一个浮栅(floating-gate) MOSFET 组成。

关于存储,可以用一张图来解释:

927621fa72de0f00e52dd7aa16843b29.png

来源:ROM、RAM、FLASH、NVM......一文搞定(https://blog.youkuaiyun.com/lianyunyouyou/article/details/118277207)

1.2

使用ENV配置FAL

在RT-Thread v4.1.0之前,FAL是作为软件包形式对用户开放使用的,而v4.1.0之后,FAL被RT-Thread官方重新定义为RTT组件的一部分,这样也能更加方便用户的开发。

我们下面正式讲解FAL组件的使用:

首先打开ENV工具,根据以下路径打开FAL使能 RT-Thread Components->[*]FAL: flash abstraction layer,由于我们后面会用到SFUD,所以这里把 FAL uses SFUD drivers 一并使能,并修改FAL设备名称为 W25Q128.

7cd1cec4dc32a29a72c5643238f105a1.png

完成上述操作后保存退出,并使用 scons --target=mdk5 重新生成MDK5文件并打开

1.3

FAL SFUD 移植

为了提供示例,我们选用 W25Q128 spi flash 作为测试模块,并且使用SFUD框架对spi flash设备进行管理和驱动。

由于目前RT-Thread的SFUD已经对 W25Q128 完成支持,根据官方的使用手册,我们仅需编写 fal_cfg.h 文件完成对 FAL_FLASH_DEV_TABLE 及 FAL_PART_TABLE 的定义即可。文件存放路径:.\rt-thread\bsp\lpc55sxx\lpc55s69_nxp_evk\board\ports\fal_cfg.h

// fal.cfg.h


/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2023-04-21     Wangyuqiang  the first version
 */
#ifndef _FAL_CFG_H_
#define _FAL_CFG_H_


#include <rtthread.h>
#include <board.h>


#ifndef FAL_USING_NOR_FLASH_DEV_NAME
#define NOR_FLASH_DEV_NAME             "norflash0"
#else
#define NOR_FLASH_DEV_NAME              FAL_USING_NOR_FLASH_DEV_NAME
#endif


/* Flash device Configuration */


extern struct fal_flash_dev nor_flash0;


/* flash device table */


#define FAL_FLASH_DEV_TABLE                                          \
{                                                                    \
    &nor_flash0,                                                     \
}


/* Partition Configuration */


#ifdef FAL_PART_HAS_TABLE_CFG


/* partition table */


#define FAL_PART_TABLE                                                                                                  \
{                                                                                                                       \
    {FAL_PART_MAGIC_WROD,  "easyflash", NOR_FLASH_DEV_NAME,                                    0,       512 * 1024, 0}, \
    {FAL_PART_MAGIC_WROD,   "download", NOR_FLASH_DEV_NAME,                           512 * 1024,      1024 * 1024, 0}, \
    {FAL_PART_MAGIC_WROD, "wifi_image", NOR_FLASH_DEV_NAME,                  (512 + 1024) * 1024,       512 * 1024, 0}, \
    {FAL_PART_MAGIC_WROD,       "font", NOR_FLASH_DEV_NAME,            (512 + 1024 + 512) * 1024,  7 * 1024 * 1024, 0}, \
    {FAL_PART_MAGIC_WROD, "filesystem", NOR_FLASH_DEV_NAME, (512 + 1024 + 512 + 7 * 1024) * 1024,  7 * 1024 * 1024, 0}, \
}
#endif /* FAL_PART_HAS_TABLE_CFG */


#endif /* _FAL_CFG_H_ */

此时编译的话是找不到该头文件的,需要我们在Keil中设置:

af776b1cf1919d965f2f7e6922ec8b7e.png

在RTT FAL组件中的SFUD提供的 fal_flash_dev 对象默认的nor_flash0参数中,flash大小默认为8M,而 W25Q128 最大最16M,我们可以选择在 .\rt-thread\components\fal\samples\porting\fal_flash_sfud_port.c文件中对struct fal_flash_dev nor_flash0 进行修改:

struct fal_flash_dev nor_flash0 =
{
    .name       = FAL_USING_NOR_FLASH_DEV_NAME,
    .addr       = 0,
    .len        = 16 * 1024 * 1024,
    .blk_size   = 4096,
    .ops        = {init, read, write, erase},
    .write_gran = 1
};

当然也可以选择不进行修改,根据大佬的原话就是因为在调用初始化接口函数init后,会从flash设备读取正确的参数更新到nor_flash0表项中,我们在使用FAL组件前都需要调用FAL初始化函数fal_init,其内调用flash设备初始化函数fal_flash_init,最后会调用注册到fal_flash_dev设备表项中的初始化函数device_table[i]->ops.init,所以nor_flash0表项参数会在FAL初始化时被更新。

同时我们需要开启SFUD框架支持,打开ENV工具,由于SFUD的使用需要指定一个spi设备,这里我选择使用最近移植好的软件spi,路径 Hardware Drivers Config->On-chip Peripheral Drivers->[*] Enable soft SPI BUS-> [*] Enable soft SPI1 BUS (software simulation) ,这里我的测试开发板是恩智浦的LPC55S69-EVK,并且这款bsp的软件模拟spi由我本人对接,关于这部分的软件spi引脚定义可以选用默认即可,当然也可以使用自定义引脚,记住不要与其他引脚产生冲突。

b8e9a23dae5b0d8ba4130a29c1affdd1.png

此时我们回到ENV主界面,进入 RT-Thread Components->Device Drivers->Using Serial Flash Universal Driver ,此时我们才可以看到SFUD选项出现(如果没有使能spi是没法看到的),使能后保持默认即可

9e37f357f2f210855a0e95ab74d102eb.png

到这里,ENV的配置暂时告一段落!

1.4

FAL SFUD 测试用例

为了验证 W25Q128 及软件模拟spi在SFUD框架上是否能够成功运行,我们在 .\rt-thread\bsp\lpc55sxx\lpc55s69_nxp_evk\board\ports\ 下新建一个 soft_spi_flash_init.c 文件,代码如下

/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2023-04-21     Wangyuqiang  the first version
 */


#include <rtthread.h>
#include "spi_flash.h"
#include "spi_flash_sfud.h"
#include "drv_soft_spi.h"
#include "drv_pin.h"
#include "rtconfig.h"


#define cs_pin  GET_PINS(1,9)


static int rt_soft_spi_flash_init(void)
{
  int result = -1;


    result = rt_hw_softspi_device_attach("sspi1", "sspi10", cs_pin);
  rt_kprintf("value is %d\n",result);
  
  if(result == RT_EOK)
  {
    rt_kprintf("rt_hw_softspi_device_attach successful!\n");
  }


    if (RT_NULL == rt_sfud_flash_probe("W25Q128", "sspi10"))
    {
        return -RT_ERROR;
    }


    return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_soft_spi_flash_init);

这里我们需要指定一个片选引脚,我暂时使用了 sspi2 的SCK引脚作为片选,这里注意不要同时打开 sspi1 和 sspi2 ,后续我会专门上传一个通用GPIO作为片选引脚,到时候就不会产生问题了。然后软件spi设备的挂载使用的是 sspi1 bus 及 sspi10 device ,并且挂载flash设备到 sspi10 。

另外我们在 .\rt-thread\bsp\lpc55sxx\lpc55s69_nxp_evk\board\ports\ 下新建 fal_sample.c 文件,并编写测试代码:

//fal_sample.c


/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2023-04-21     Wangyuqiang  the first version
 */
 
#include "rtthread.h"
#include "rtdevice.h"
#include "board.h"
#include "fal.h"


#define BUF_SIZE 1024


static int fal_test(const char *partiton_name)
{
    int ret;
    int i, j, len;
    uint8_t buf[BUF_SIZE];
    const struct fal_flash_dev *flash_dev = RT_NULL;
    const struct fal_partition *partition = RT_NULL;


    if (!partiton_name)
    {
        rt_kprintf("Input param partition name is null!\n");
        return -1;
    }


    partition = fal_partition_find(partiton_name);
    if (partition == RT_NULL)
    {
        rt_kprintf("Find partition (%s) failed!\n", partiton_name);
        ret = -1;
        return ret;
    }


    flash_dev = fal_flash_device_find(partition->flash_name);
    if (flash_dev == RT_NULL)
    {
        rt_kprintf("Find flash device (%s) failed!\n", partition->flash_name);
        ret = -1;
        return ret;
    }


    rt_kprintf("Flash device : %s   "
               "Flash size : %dK   \n"
               "Partition : %s   "
               "Partition size: %dK\n", 
                partition->flash_name, 
                flash_dev->len/1024,
                partition->name,
                partition->len/1024);


    /* erase all partition */
    ret = fal_partition_erase_all(partition);
    if (ret < 0)
    {
        rt_kprintf("Partition (%s) erase failed!\n", partition->name);
        ret = -1;
        return ret;
    }
    rt_kprintf("Erase (%s) partition finish!\n", partiton_name);


    /* read the specified partition and check data */
    for (i = 0; i < partition->len;)
    {
        rt_memset(buf, 0x00, BUF_SIZE);


        len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i);


        ret = fal_partition_read(partition, i, buf, len);
        if (ret < 0)
        {
            rt_kprintf("Partition (%s) read failed!\n", partition->name);
            ret = -1;
            return ret;
        }


        for(j = 0; j < len; j++)
        {
            if (buf[j] != 0xFF)
            {
                rt_kprintf("The erase operation did not really succeed!\n");
                ret = -1;
                return ret;
            }
        }
        i += len;
    }


    /* write 0x00 to the specified partition */
    for (i = 0; i < partition->len;)
    {
        rt_memset(buf, 0x00, BUF_SIZE);


        len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i);


        ret = fal_partition_write(partition, i, buf, len);
        if (ret < 0)
        {
            rt_kprintf("Partition (%s) write failed!\n", partition->name);
            ret = -1;
            return ret;
        }


        i += len;
    }
    rt_kprintf("Write (%s) partition finish! Write size %d(%dK).\n", partiton_name, i, i/1024);


    /* read the specified partition and check data */
    for (i = 0; i < partition->len;)
    {
        rt_memset(buf, 0xFF, BUF_SIZE);


        len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i);


        ret = fal_partition_read(partition, i, buf, len);
        if (ret < 0)
        {
            rt_kprintf("Partition (%s) read failed!\n", partition->name);
            ret = -1;
            return ret;
        }


        for(j = 0; j < len; j++)
        {
            if (buf[j] != 0x00)
            {
                rt_kprintf("The write operation did not really succeed!\n");
                ret = -1;
                return ret;
            }
        }


        i += len;
    }


    ret = 0;
    return ret;
}


static void fal_sample(void)
{
    /* 1- init */
    fal_init();


    if (fal_test("font") == 0)
    {
        rt_kprintf("Fal partition (%s) test success!\n", "font");
    }
    else
    {
        rt_kprintf("Fal partition (%s) test failed!\n", "font");
    }


    if (fal_test("download") == 0)
    {
        rt_kprintf("Fal partition (%s) test success!\n", "download");
    }
    else
    {
        rt_kprintf("Fal partition (%s) test failed!\n", "download");
    }
}
MSH_CMD_EXPORT(fal_sample, fal sample);

1.5

测试结果

到这里就可以进行编译下载了,成功后的截图如下:

e88f9a4c3f02f9c70189c637b5daacfe.png

2.

DFS文件系统

2.1

什么是DFS

DFS 是 RT-Thread 提供的虚拟文件系统组件,全称为 Device File System,即设备虚拟文件系统,文件系统的名称使用类似 UNIX 文件、文件夹的风格,目录结构如下图所示:

f410b554be1f10dfaf19ca75bda06487.png

在 RT-Thread DFS 中,文件系统有统一的根目录,使用 / 来表示。而在根目录下的 f1.bin 文件则使用 /f1.bin 来表示,2018 目录下的 f1.bin 目录则使用 /data/2018/f1.bin 来表示。即目录的分割符号是 /,这与 UNIX/Linux 完全相同,与 Windows 则不相同(Windows 操作系统上使用 \ 来作为目录的分割符)。

2.2

DFS架构

RT-Thread DFS 组件的主要功能特点有:

  • 为应用程序提供统一的 POSIX 文件和目录操作接口:read、write、poll/select 等。

  • 支持多种类型的文件系统,如 FatFS、RomFS、DevFS 等,并提供普通文件、设备文件、网络文件描述符的管理。

  • 支持多种类型的存储设备,如 SD Card、SPI Flash、Nand Flash 等。

DFS 的层次架构如下图所示,主要分为 POSIX 接口层、虚拟文件系统层和设备抽象层。

c5d36ea97ecd478de4dc3b093a38c3ba.png

2.3

使用ENV配置DFS

打开ENV,进入路径 RT-Thread Components → DFS: device virtual file system ,使能 [*] DFS: device virtual file system 

74b8e73ee67f3d45294bfcad8b42cd60.png

由于DFS使用的是POSIX接口,而dfs_posix.h已经在新版本中被移除了,如果想要兼容老版本,可以在menuconfig中使能 RT-Thread Components->[*] Support legacy version for compatibility 

cd5920d4b15121d0f2b69225262e57ba.png

由于elmfat文件系统默认最大扇区大小为512,但我们使用的flash模块 W25Q128 的Flash扇区大小为4096,为了将elmfat文件系统挂载到W25Q128上,这里的 Maximum sector size 需要和W25Q128扇区大小保持一致,修改为4096,路径:RT-Thread Components → DFS: device virtual file system → [*] Enable elm-chan fatfs / elm-chan's FatFs, Generic FAT Filesystem Module

276fc3e065932ba5e90f78b8000c3494.png

保存退出后使用 scons --target=mdk5 生成MDK5工程。

2.4

DFS挂载到FAL分区测试

这里增加FAL flash抽象层,我们将elmfat文件系统挂载到W25Q128 flash设备的filesystem分区上,由于FAL管理的filesystem分区不是块设备,需要先使用FAL分区转BLK设备接口函数将filesystem分区转换为块设备,然后再将DFS elmfat文件系统挂载到filesystem块设备上。

我们接着修改fal_sample.c文件,修改后代码:

/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2023-04-21     Wangyuqiang  the first version
 */
 
#include "rtthread.h"
#include "rtdevice.h"
#include "board.h"
#include "fal.h"


#include <dfs_posix.h>


#define FS_PARTITION_NAME  "filesystem"


#define BUF_SIZE 1024


static int fal_test(const char *partiton_name)
{
    int ret;
    int i, j, len;
    uint8_t buf[BUF_SIZE];
    const struct fal_flash_dev *flash_dev = RT_NULL;
    const struct fal_partition *partition = RT_NULL;


    if (!partiton_name)
    {
        rt_kprintf("Input param partition name is null!\n");
        return -1;
    }


    partition = fal_partition_find(partiton_name);
    if (partition == RT_NULL)
    {
        rt_kprintf("Find partition (%s) failed!\n", partiton_name);
        ret = -1;
        return ret;
    }


    flash_dev = fal_flash_device_find(partition->flash_name);
    if (flash_dev == RT_NULL)
    {
        rt_kprintf("Find flash device (%s) failed!\n", partition->flash_name);
        ret = -1;
        return ret;
    }


    rt_kprintf("Flash device : %s   "
               "Flash size : %dK   \n"
               "Partition : %s   "
               "Partition size: %dK\n", 
                partition->flash_name, 
                flash_dev->len/1024,
                partition->name,
                partition->len/1024);


    /* erase all partition */
    ret = fal_partition_erase_all(partition);
    if (ret < 0)
    {
        rt_kprintf("Partition (%s) erase failed!\n", partition->name);
        ret = -1;
        return ret;
    }
    rt_kprintf("Erase (%s) partition finish!\n", partiton_name);


    /* read the specified partition and check data */
    for (i = 0; i < partition->len;)
    {
        rt_memset(buf, 0x00, BUF_SIZE);


        len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i);


        ret = fal_partition_read(partition, i, buf, len);
        if (ret < 0)
        {
            rt_kprintf("Partition (%s) read failed!\n", partition->name);
            ret = -1;
            return ret;
        }


        for(j = 0; j < len; j++)
        {
            if (buf[j] != 0xFF)
            {
                rt_kprintf("The erase operation did not really succeed!\n");
                ret = -1;
                return ret;
            }
        }
        i += len;
    }


    /* write 0x00 to the specified partition */
    for (i = 0; i < partition->len;)
    {
        rt_memset(buf, 0x00, BUF_SIZE);


        len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i);


        ret = fal_partition_write(partition, i, buf, len);
        if (ret < 0)
        {
            rt_kprintf("Partition (%s) write failed!\n", partition->name);
            ret = -1;
            return ret;
        }


        i += len;
    }
    rt_kprintf("Write (%s) partition finish! Write size %d(%dK).\n", partiton_name, i, i/1024);


    /* read the specified partition and check data */
    for (i = 0; i < partition->len;)
    {
        rt_memset(buf, 0xFF, BUF_SIZE);


        len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i);


        ret = fal_partition_read(partition, i, buf, len);
        if (ret < 0)
        {
            rt_kprintf("Partition (%s) read failed!\n", partition->name);
            ret = -1;
            return ret;
        }


        for(j = 0; j < len; j++)
        {
            if (buf[j] != 0x00)
            {
                rt_kprintf("The write operation did not really succeed!\n");
                ret = -1;
                return ret;
            }
        }


        i += len;
    }


    ret = 0;
    return ret;
}


static void fal_sample(void)
{
    /* 1- init */
    fal_init();


    if (fal_test("font") == 0)
    {
        rt_kprintf("Fal partition (%s) test success!\n", "font");
    }
    else
    {
        rt_kprintf("Fal partition (%s) test failed!\n", "font");
    }


    if (fal_test("download") == 0)
    {
        rt_kprintf("Fal partition (%s) test success!\n", "download");
    }
    else
    {
        rt_kprintf("Fal partition (%s) test failed!\n", "download");
    }
}
MSH_CMD_EXPORT(fal_sample, fal sample);


static void fal_elmfat_sample(void)
{
    int fd, size;
    struct statfs elm_stat;
    struct fal_blk_device *blk_dev;
    char str[] = "elmfat mount to W25Q flash.", buf[80];


    /* fal init */
    fal_init();


    /* create block device */
    blk_dev = (struct fal_blk_device *)fal_blk_device_create(FS_PARTITION_NAME);
    if(blk_dev == RT_NULL)
        rt_kprintf("Can't create a block device on '%s' partition.\n", FS_PARTITION_NAME);
    else
        rt_kprintf("Create a block device on the %s partition of flash successful.\n", FS_PARTITION_NAME);


    /* make a elmfat format filesystem */
    if(dfs_mkfs("elm", FS_PARTITION_NAME) == 0)
        rt_kprintf("make elmfat filesystem success.\n");


    /* mount elmfat file system to FS_PARTITION_NAME */
    if(dfs_mount(FS_PARTITION_NAME, "/", "elm", 0, 0) == 0)
        rt_kprintf("elmfat filesystem mount success.\n");


    /* Get elmfat file system statistics */
    if(statfs("/", &elm_stat) == 0)
        rt_kprintf("elmfat filesystem block size: %d, total blocks: %d, free blocks: %d.\n", 
                    elm_stat.f_bsize, elm_stat.f_blocks, elm_stat.f_bfree);


    if(mkdir("/user", 0x777) == 0)
        rt_kprintf("make a directory: '/user'.\n");


    rt_kprintf("Write string '%s' to /user/test.txt.\n", str);


    /* Open the file in create and read-write mode, create the file if it does not exist*/
    fd = open("/user/test.txt", O_WRONLY | O_CREAT);
    if (fd >= 0)
    {
        if(write(fd, str, sizeof(str)) == sizeof(str))
            rt_kprintf("Write data done.\n");


        close(fd);   
    }


    /* Open file in read-only mode */
    fd = open("/user/test.txt", O_RDONLY);
    if (fd >= 0)
    {
        size = read(fd, buf, sizeof(buf));


        close(fd);


        if(size == sizeof(str))
            rt_kprintf("Read data from file test.txt(size: %d): %s \n", size, buf);
    }
}
MSH_CMD_EXPORT_ALIAS(fal_elmfat_sample, fal_elmfat,fal elmfat sample);

2.5

测试结果

测试结果如下:

ff77512b13fa615ddd6908e0c7431940.png

3.

Easyflash移植到FAL分区

3.1

简述EasyFlash

关于EasyFlash的来源我们已经讲过了,此处不再赘述。EasyFlash(https://gitee.com/link?target=https%3A%2F%2Fgithub.com%2Farmink%2FEasyFlash)是一款开源的轻量级嵌入式Flash存储器库,方便开发者更加轻松的实现基于Flash存储器的常见应用开发。非常适合智能家居、可穿戴、工控、医疗、物联网等需要断电存储功能的产品,资源占用极低,支持各种 MCU 片上存储器。

EasyFlash不仅能够实现对产品的 设定参数 或 运行日志 等信息的掉电保存功能,还封装了简洁的 增加、删除、修改及查询 方法, 降低了开发者对产品参数的处理难度,也保证了产品在后期升级时拥有更好的扩展性。让Flash变为NoSQL(非关系型数据库)模型的小型键值(Key-Value)存储数据库。

3.2

EasyFlash软件包使用

打开ENV进入路径:RT-Thread online packages → tools packages → EasyFlash: Lightweight embedded flash memory library. ,选择软件包版本为最新版。

0cdf5f78f933d7f75f9d726816d800bb.png

配置后退出ENV,同时使用 pkgs --update 下载软件包,然后再使用 scons --target=mdk5 重新生成MDK5文件

3.3

移植easyflash

下载完easyflash软件包后,我们复制 .\rt-thread\bsp\lpc55sxx\lpc55s69_nxp_evk\packages\EasyFlash-latest\ports\ef_fal_port.c 到目录 .\rt-thread\bsp\lpc55sxx\lpc55s69_nxp_evk\board\ports\easyflash\ef_fal_port.c ,双击打开该文件,完成以下修改:

// 修改 FAL_EF_PART_NAME 为 easyflash
#define FAL_EF_PART_NAME               "easyflash"
// 修改环境变量内容为 {"boot_times", "0"},这里我们先只设置一个开机次数
static const ef_env default_env_set[] = {
        {"boot_times", "0"},
};

3.4

编写Easyflash测试用例

/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2023-04-21     Wangyuqiang  the first version
 */
 
#include "rtthread.h"
#include "rtdevice.h"
#include "board.h"
#include "fal.h"


#include <dfs_posix.h>


#include "easyflash.h"
#include <stdlib.h>


#define FS_PARTITION_NAME  "filesystem"


#define BUF_SIZE 1024


static int fal_test(const char *partiton_name)
{
    int ret;
    int i, j, len;
    uint8_t buf[BUF_SIZE];
    const struct fal_flash_dev *flash_dev = RT_NULL;
    const struct fal_partition *partition = RT_NULL;


    if (!partiton_name)
    {
        rt_kprintf("Input param partition name is null!\n");
        return -1;
    }


    partition = fal_partition_find(partiton_name);
    if (partition == RT_NULL)
    {
        rt_kprintf("Find partition (%s) failed!\n", partiton_name);
        ret = -1;
        return ret;
    }


    flash_dev = fal_flash_device_find(partition->flash_name);
    if (flash_dev == RT_NULL)
    {
        rt_kprintf("Find flash device (%s) failed!\n", partition->flash_name);
        ret = -1;
        return ret;
    }


    rt_kprintf("Flash device : %s   "
               "Flash size : %dK   \n"
               "Partition : %s   "
               "Partition size: %dK\n", 
                partition->flash_name, 
                flash_dev->len/1024,
                partition->name,
                partition->len/1024);


    /* erase all partition */
    ret = fal_partition_erase_all(partition);
    if (ret < 0)
    {
        rt_kprintf("Partition (%s) erase failed!\n", partition->name);
        ret = -1;
        return ret;
    }
    rt_kprintf("Erase (%s) partition finish!\n", partiton_name);


    /* read the specified partition and check data */
    for (i = 0; i < partition->len;)
    {
        rt_memset(buf, 0x00, BUF_SIZE);


        len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i);


        ret = fal_partition_read(partition, i, buf, len);
        if (ret < 0)
        {
            rt_kprintf("Partition (%s) read failed!\n", partition->name);
            ret = -1;
            return ret;
        }


        for(j = 0; j < len; j++)
        {
            if (buf[j] != 0xFF)
            {
                rt_kprintf("The erase operation did not really succeed!\n");
                ret = -1;
                return ret;
            }
        }
        i += len;
    }


    /* write 0x00 to the specified partition */
    for (i = 0; i < partition->len;)
    {
        rt_memset(buf, 0x00, BUF_SIZE);


        len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i);


        ret = fal_partition_write(partition, i, buf, len);
        if (ret < 0)
        {
            rt_kprintf("Partition (%s) write failed!\n", partition->name);
            ret = -1;
            return ret;
        }


        i += len;
    }
    rt_kprintf("Write (%s) partition finish! Write size %d(%dK).\n", partiton_name, i, i/1024);


    /* read the specified partition and check data */
    for (i = 0; i < partition->len;)
    {
        rt_memset(buf, 0xFF, BUF_SIZE);


        len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i);


        ret = fal_partition_read(partition, i, buf, len);
        if (ret < 0)
        {
            rt_kprintf("Partition (%s) read failed!\n", partition->name);
            ret = -1;
            return ret;
        }


        for(j = 0; j < len; j++)
        {
            if (buf[j] != 0x00)
            {
                rt_kprintf("The write operation did not really succeed!\n");
                ret = -1;
                return ret;
            }
        }


        i += len;
    }


    ret = 0;
    return ret;
}


static void fal_sample(void)
{
    /* 1- init */
    fal_init();


    if (fal_test("font") == 0)
    {
        rt_kprintf("Fal partition (%s) test success!\n", "font");
    }
    else
    {
        rt_kprintf("Fal partition (%s) test failed!\n", "font");
    }


    if (fal_test("download") == 0)
    {
        rt_kprintf("Fal partition (%s) test success!\n", "download");
    }
    else
    {
        rt_kprintf("Fal partition (%s) test failed!\n", "download");
    }
}
MSH_CMD_EXPORT(fal_sample, fal sample);


static void fal_elmfat_sample(void)
{
    int fd, size;
    struct statfs elm_stat;
    struct fal_blk_device *blk_dev;
    char str[] = "elmfat mount to W25Q flash.", buf[80];


    /* fal init */
    fal_init();


    /* create block device */
    blk_dev = (struct fal_blk_device *)fal_blk_device_create(FS_PARTITION_NAME);
    if(blk_dev == RT_NULL)
        rt_kprintf("Can't create a block device on '%s' partition.\n", FS_PARTITION_NAME);
    else
        rt_kprintf("Create a block device on the %s partition of flash successful.\n", FS_PARTITION_NAME);


    /* make a elmfat format filesystem */
    if(dfs_mkfs("elm", FS_PARTITION_NAME) == 0)
        rt_kprintf("make elmfat filesystem success.\n");


    /* mount elmfat file system to FS_PARTITION_NAME */
    if(dfs_mount(FS_PARTITION_NAME, "/", "elm", 0, 0) == 0)
        rt_kprintf("elmfat filesystem mount success.\n");


    /* Get elmfat file system statistics */
    if(statfs("/", &elm_stat) == 0)
        rt_kprintf("elmfat filesystem block size: %d, total blocks: %d, free blocks: %d.\n", 
                    elm_stat.f_bsize, elm_stat.f_blocks, elm_stat.f_bfree);


    if(mkdir("/user", 0x777) == 0)
        rt_kprintf("make a directory: '/user'.\n");


    rt_kprintf("Write string '%s' to /user/test.txt.\n", str);


    /* Open the file in create and read-write mode, create the file if it does not exist*/
    fd = open("/user/test.txt", O_WRONLY | O_CREAT);
    if (fd >= 0)
    {
        if(write(fd, str, sizeof(str)) == sizeof(str))
            rt_kprintf("Write data done.\n");


        close(fd);   
    }


    /* Open file in read-only mode */
    fd = open("/user/test.txt", O_RDONLY);
    if (fd >= 0)
    {
        size = read(fd, buf, sizeof(buf));


        close(fd);


        if(size == sizeof(str))
            rt_kprintf("Read data from file test.txt(size: %d): %s \n", size, buf);
    }
}
MSH_CMD_EXPORT_ALIAS(fal_elmfat_sample, fal_elmfat,fal elmfat sample);


static void easyflash_sample(void)
{
    /* fal init */
    fal_init();


    /* easyflash init */
    if(easyflash_init() == EF_NO_ERR)
    {
        uint32_t i_boot_times = NULL;
        char *c_old_boot_times, c_new_boot_times[11] = {0};


        /* get the boot count number from Env */
        c_old_boot_times = ef_get_env("boot_times");
        /* get the boot count number failed */
        if (c_old_boot_times == RT_NULL)
            c_old_boot_times[0] = '0';


        i_boot_times = atol(c_old_boot_times);
        /* boot count +1 */
        i_boot_times ++;
        rt_kprintf("===============================================\n");
        rt_kprintf("The system now boot %d times\n", i_boot_times);
        rt_kprintf("===============================================\n");
        /* interger to string */
        sprintf(c_new_boot_times, "%d", i_boot_times);
        /* set and store the boot count number to Env */
        ef_set_env("boot_times", c_new_boot_times);
        ef_save_env();
    }
}
MSH_CMD_EXPORT(easyflash_sample, easyflash sample);

3.5

测试结果

打开串口助手,输入命令:

msh />easyflash_sample

第一次命令调用:

32842d617bc0585edaff437698408448.png

第二次RESET开发板后调用:

be4034cdb390ef5d6996ca07f51908d5.png

——————End——————

4c2153fba08fdd005d1d420914f6d232.jpeg

6de33ef30b44803558ac6bcace7d41dd.gif

想要在RT-Thread平台或社区投放内容?或想参与相关直播活动及赛事?RT-Thread已开放对接窗口,请通过邮件与我们取得联系,期待合作!

合作邮箱: tongfangyi@rt-thread.com 

stm32f103rc片内Flash使用FAL库配置说明 1.参考文件 https://github.com/RT-Thread-packages/fal https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/README.md https://blog.youkuaiyun.com/m0_37621078/article/details/102689903 2.ENV配置 (1)... 阅读详情

相关推荐

【RT-Thread FAL组件】(Flash abstraction layer)Flash抽象层源码解读

本文摘抄了RT-Thread的FAL层源码实现,对其实现机制进行了大致的梳理,FAL层属于flash的抽象层,为后续使用flash提供了一个更加方便的接口。

Stay_Hun_forward的博客 1848

RT_Thread_使用FAL组件配置stm32f407片上flash读写

先看下struct fal_flash_dev的具体成员,有flash设备名字、flash起始地址、flash大小、块大小(执行擦除时的最小颗粒)、操作函数(init、read、write、erase)、写操作最小颗粒度;size_t len;第一部分:宏定义各扇区的起始地址第二部分:读、写、擦除第三部分:使用了FAL之后的FLASH设备定义以及对于的读、写、擦除操作函数。

人非生而知之者 4185

IOT-OS之RT-Thread(十一)--- FAL分区管理与easyflash变量管理

FAL (Flash Abstraction Layer) Flash 抽象层,是对 Flash 及基于 Flash 的分区进行管理、操作的抽象层,对上层统一了 Flash 及 分区操作的 API。 FAL抽象层位于SFUD框架的上层,可以将多个Flash硬件(包括片内Flash和片外Flash)统一进行管理,并向上层比如DFS文件系统层提供对底层多个Flash硬件的统一访问接口,方便上层应用对底层硬件的访问操作。

流云 1万+

RTthread的FAL组件介绍

FAL (Flash Abstraction Layer) Flash 抽象层,是对 Flash 及基于 Flash 的分区进行管理、操作的抽象层,对上层统一了 Flash 及 分区操作的 API (框架图如下所示)。废话不多说,开始!分区路径packages\fal-v1.0.0\samples\porting\fal_cfg.h。 这里解释一下:以 {FAL_PART_MAGIC_WORD, “app”, “stm32_onchip”, 641024, 7041024, 0}这

LuDvei的博客 3196

RT-Thread实战:基于FAL与SFUD的W25Q128分区管理与EasyFlash环境变量存储

本文详细介绍了基于RT-Thread的W25Q128存储方案,通过FAL、SFUD和EasyFlash三大组件的协同工作,实现高效的分区管理与环境变量存储。文章涵盖硬件配置、驱动适配、性能优化等实战技巧,帮助开发者快速掌握嵌入式存储系统的设计与实现。

weixin_42628280的博客 194

RT-Thread Studio实战:如何用FAL和EasyFlash管理SPI Flash分区(STM32F405RG版)

本文以STM32F405RG平台为例,详细介绍了如何在RT-Thread Studio中利用FAL(Flash抽象层)和EasyFlash软件包高效管理SPI Flash分区。通过实战演示了从底层驱动配置、分区表设计到环境变量存储的完整流程,为嵌入式设备构建可靠、易维护的数据存储架构提供了清晰指南。

weixin_29181861的博客 335

【LPC55s69】使用FAL分区管理与easyflash变量管理

支持静态可配置的分区表,并可关联多个 Flash 设备;分区表支持自动装载。避免在多固件项目,分区表被多次定义的问题;代码精简,对操作系统无依赖,可运行于裸机平台,比如对资源有一定要求的 Bootloader;统一的操作接口。EasyFlash) 等对 Flash 有一定依赖的组件,底层 Flash 驱动的可重用性;自带基于 Finsh/MSH 的测试命令,可以通过 Shell 按字节寻址的方式操作(读写擦) Flash 或分区,方便开发者进行调试、测试;

以放的博客 1905

RT-Thread 实战指南:基于FAL与SFUD的W25Q128分区管理与EasyFlash应用

本文详细介绍了在RT-Thread系统中基于FAL与SFUD实现W25Q128分区管理及EasyFlash应用的实战指南。通过硬件驱动配置、分区规划优化和EasyFlash深度应用,解决物联网设备开发中的Flash擦写寿命、管理复杂度和多组件冲突等问题,提升系统稳定性和存储效率。

weixin_28729205的博客 391

RT-Thread Studio中FAL分区与EasyFlash环境变量的高效管理实践

本文详细介绍了在RT-Thread Studio中,如何利用FAL分区与EasyFlash高效管理SPI Flash环境变量的完整实践。通过配置SFUD驱动、规划FAL分区表、适配EasyFlash存储区,实现了一套结构清晰、稳定可靠的非易失性数据存储方案,适用于设备配置、Wi-Fi参数等关键数据的掉电保存。

lake5的博客 510

基于RT-Thread Studio的FAL,EasyFlash,文件系统移植

一、新建工程 ​ 1.2、配置时钟 1.3、修改main文件 #include <rtthread.h> #include <rtdevice.h> #include <board.h> #define DBG_TAG "main" #define DBG_LVL DBG_LOG #include <rtdbg.h> #define LED0 GET_PIN(C, 3) int main(void) { int count = 1;

weixin_43745583的博客 2946

RT-Thread驱动STM32 SPI Flash:从SFUD探测到FAL分区与EasyFlash应用实战

本文详细介绍了如何在RT-Thread操作系统下驱动STM32 SPI Flash,从SFUD探测到FAL分区与EasyFlash应用实战。通过环境准备、SPI外设配置、SFUD集成、FAL闪存抽象层配置以及EasyFlash应用等步骤,帮助开发者快速掌握SPI Flash驱动开发技巧,并解决常见问题。

weixin_30266885的博客 348

RT-Thread—FAL与EasyFlash组件移植

一、FAL管理与示例   FAL (Flash Abstraction Layer) Flash 抽象层,是对 Flash 及基于 Flash 的分区进行管理、操作的抽象层,对上层统一了 Flash 及 分区操作的 API ,FAL 框架图如下:   从上图可以看出FAL抽象层位于SFUD框架的上层,可以将多个Flash硬件(包括片内Flash和片外Flash)统一进行管理,并向上层比如DFS文件系统层提供对底层多个Flash硬件的统一访问接口,方便上层应用对底层

鸿墨轩 2225

RT-Thread SFUD驱动W25Q128避坑指南:从SPI初始化失败到FAL分区表配置

本文详细介绍了在RT-Thread环境下使用SFUD驱动W25Q128芯片的完整流程,包括SPI初始化、SFUD驱动配置、FAL分区表设置以及EasyFlash集成。针对常见的SPI初始化失败、SFUD探测问题、分区表配置错误等提供了具体解决方案和调试技巧,帮助开发者高效完成嵌入式存储系统开发。

weixin_30319153的博客 254

从零构建嵌入式存储生态:FAL与EasyFlash在RT-Thread中的协同设计哲学

本文探讨了在RT-Thread操作系统中,FAL(Flash抽象层)与EasyFlash的协同设计如何解决嵌入式存储管理的核心挑战。通过FAL的统一硬件接口和分区管理,结合EasyFlash的键值存储与数据一致性保障,为资源受限的物联网设备提供了高效、可靠的存储解决方案,显著简化了开发复杂度并提升了系统可维护性。

egg99的博客 1017

如何解决ipa包上传App Store Connect时出现的无效二进制文件问题

本文详细解析了ipa包上传App Store Connect时出现无效二进制文件问题的常见原因及解决方案。从Scheme配置、架构支持到权限声明,提供了全面的检查步骤和实用技巧,帮助开发者快速定位并解决上传问题,确保应用顺利通过审核。

weixin_28833069的博客 316

从BAM到bigWig:用UCSC工具链完成测序数据可视化的完整流程

本文详细介绍了从BAM文件到bigWig格式的基因组数据可视化完整流程,涵盖UCSC工具链配置、文件格式转换、参数优化及常见问题解决方案。通过实战指南帮助研究人员高效完成测序数据可视化,特别适合ChIP-seq和RNA-seq数据分析。

weixin_29309213的博客 709

STM32开发者必看:EasyFlash V4.1.0环境变量存储实战指南(附避坑技巧)

本文为STM32开发者提供EasyFlash V4.1.0环境变量存储的实战指南,详细解析了EasyFlash的写平衡机制、掉电保护等核心功能,并附有硬件配置、结构体存储方案及避坑技巧。通过源码分析和实战案例,帮助开发者高效实现嵌入式参数存储,提升系统可靠性。

onion的博客 779

VC++实现工业级RS232/RS485串口通信:从Win32 API到多线程封装

串口通信作为设备间数据交换的基础物理层协议,其核心在于通过特定的电气标准(如RS232的点对点电压传输、RS485的差分信号总线)实现稳定可靠的数据传输。在Windows平台下,其技术实现主要依托Win32 API,通过CreateFile、ReadFile、WriteFile等函数进行端口操作,并利用重叠I/O(Overlapped I/O)模型实现异步非阻塞,以提升程序响应能力与CPU效率。这对于构建高可靠性的工业控制、嵌入式调试及自动化设备软件具有关键价值。在实际应用场景中,开发者常需处理RS485的

weixin_33937499的博客 447

RT-Thread Studio实战:给STM32F429外挂W25Q256 SPI Flash,从SFUD驱动到EasyFlash配置全流程

本文详细介绍了在RT-Thread Studio开发环境中,为STM32F429芯片配置W25Q256 SPI Flash存储器的全流程开发指南。从SPI接口配置、SFUD驱动集成到FAL抽象层和EasyFlash组件的应用,提供了完整的存储解决方案,帮助开发者快速实现嵌入式系统的外部存储扩展。

weixin_30856725的博客 338

什么是Merkle树?用“文件柜”类比理解区块链的核心技术

Merkle树是一种用于区块链的数据结构,像一个高效的“文件柜”,用于存储和验证大量交易数据。比特币通过Merkle树将交易哈希组织成树状结构,层层汇总,最终生成一个唯一的根哈希,存储在区块头中。这种设计大幅减少数据存储和验证成本,支持快速校验交易的完整性和存在性。Merkle树通过密码学保证数据不可篡改,广泛应用于比特币、区块链及其他分布式系统。它的优势在于高效、轻量和安全,尤其适合轻节点验证和数据同步。理解Merkle树就像掌握区块链的“索引秘籍”,是Web3世界的关键技术。

在路上的专栏 1193

SAP固定资产原值与净值取值逻辑全解析:从数据表到ABAP实战

在企业资源计划(ERP)系统中,固定资产的价值管理是财务会计的核心模块,其关键在于准确核算资产原值与净值。从技术原理上看,这涉及对资产全生命周期业务(如购置、折旧、转移、报废)的财务建模,以及底层数据表(如ANLC、ANEP)的精准关联与计算。掌握这一逻辑的技术价值在于,能够确保财务报表数据的准确性,满足合规性要求,并为管理决策提供可靠依据。在SAP ECC或S/4HANA系统的应用场景中,ABAP开发者与财务顾问常需通过编写报表程序,从复杂的数据结构中提取资产价值信息。本文聚焦于SAP资产会计模块,深入剖

weixin_30984001的博客 397

RT-Thread 实战指南:FAL+SFUD驱动W25Q128与EasyFlash环境变量存储

本文详细介绍了在RT-Thread嵌入式系统中,如何通过FAL+SFUD驱动W25Q128 Flash芯片并集成EasyFlash实现环境变量存储。从硬件SPI配置、RT-Thread Settings组件启用,到工程文件移植与多线程测试,提供了完整的实战指南,确保数据可靠存储与掉电保护。

tech5的博客 915

OpenClaw进阶实战(二十一):工作流6:跨平台舆情监控与自动响应

摘要: 本文介绍基于OpenClaw构建自动化舆情监控系统,实现多平台数据采集、AI情感分析与智能预警。系统架构包含数据采集(微博/小红书/抖音)、消息队列、情感分析(SnowNLP/大模型API)、分级预警和自动响应模块。通过技能化开发(如微博API采集、小红书模拟登录)实现7×24小时监控,结合情感强度与传播速度分级处理负面舆情,形成"采集-分析-预警-处置"闭环,适用于企业私有化部署。

PeterPan的博客 301
上一篇: 【成都】9月21日RT-Thread巡回线下培训-OpenMV机器视觉
下一篇: 睿赛德科技正式推出RT-Thread开源 4+服务,助力企业用户发展
RT-Thread物联网操作系统
博客等级 码龄6年 2万+粉丝 392原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值