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

news2024/9/21 19:01:19

文章目录

    • 1.FAL组件
      • 1.1什么是FAL
      • 1.2 使用ENV配置FAL
      • 1.3 FAL SFUD 移植
      • 1.4 FAL SFUD 测试用例
      • 1.5 测试结果
    • 2.DFS文件系统
      • 2.1 什么是DFS
      • 2.2 DFS架构
      • 2.3 使用ENV配置DFS
      • 2.4 DFS挂载到FAL分区测试
      • 2.5 测试结果
    • 3.Easyflash移植到FAL分区
      • 3.1 简述EasyFlash
      • 3.2EasyFlash软件包使用
      • 3.3 移植easyflash
      • 3.4 编写Easyflash测试用例
      • 3.5 测试结果
    • 4.结语
    • 5.联系

1.FAL组件

1.1什么是FAL

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

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

image-20230423162047252

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

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

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

image-20230423164134689

来源:ROM、RAM、FLASH、NVM……一文搞定

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.

image-20230423164700491

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

1.3 FAL SFUD 移植

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

由于目前RT-Thread的SFUD已经对W25Q128 完成支持,根据官方的使用手册,我们仅需编写fal_cfg.h文件完成对FAL_FLASH_DEV_TABLEFAL_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中设置:

image-20230423174802203

在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引脚定义可以选用默认即可,当然也可以使用自定义引脚,记住不要与其他引脚产生冲突。

image-20230423171229953

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

image-20230423171646352

到这里,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引脚作为片选,这里注意不要同时打开sspi1sspi2,后续我会专门上传一个通用GPIO作为片选引脚,到时候就不会产生问题了。然后软件spi设备的挂载使用的是sspi1 bussspi10 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 测试结果

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

image-20230423172831146

2.DFS文件系统

2.1 什么是DFS

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

image-20230423173347702

在 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 接口层、虚拟文件系统层和设备抽象层。

image-20230423173515014

2.3 使用ENV配置DFS

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

image-20230423174113310

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

image-20230423180859035

由于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

image-20230423181825139

保存退出后使用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 测试结果

测试结果如下:

image-20230423182204922

3.Easyflash移植到FAL分区

3.1 简述EasyFlash

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

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

3.2EasyFlash软件包使用

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

image-20230423183612019

配置后退出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

第一次命令调用:

image-20230423185619472

第二次RESET开发板后调用:

image-20230423185703046

4.结语

至此本博客就到此结束,经历从移植软件模拟spi框架到LPC55S69,到移植过程中遇到不断的问题,到最后解决所有问题并提供应用示例,完成开发日记、开发笔记及应用教学,这个过程确实使我受益良多,其中感受最深的就是当然也更加感谢的是一些前辈们的指点迷津和博文记录,就目前国内嵌入式这个领域,相关开发经验相比较其他计算机行业确实有些不够包容和开放,也希望未来的朋友们能够怀揣着一颗求知及授学之心,共同建设好这个领域!

参考资料:

  • IOT-OS之RT-Thread(十一)— FAL分区管理与easyflash变量管理
  • RT-Thread文档中心:FAL组件

5.联系

  • Email :yifang.wangyq@foxmail.com
  • Github Address :https://github.com/kurisaW
  • My Website :https://kurisaw.github.io

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/453108.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker的数据管理(dockerfile等)

文章目录 一、管理docker容器中数据1、 数据卷2、数据卷容器 二、容器互联&#xff08;使用centos镜像&#xff09;三、Docker 镜像创建1、基于现有镜像创建2、基于本地模板创建3、基于Dockerfile创建 四、Dockerfile操作常用的指令:五、Dockeerfile案例 一、管理docker容器中数…

HNU数据结构与算法分析-小班7

HNU数据结构与算法分析-小班7

【Python】可视化KITTI激光雷达点云绘制立体框

前言 最近在复现PointRCNN论文过程中发现没有可视化代码&#xff0c;于是查找资料找到了&#xff1a; pointRCNN 结果可视化——tiatiatiatia 按照参考链接下载代码【轮子库】并可视化成功了 mayavi库的学习 主要是利用了mlab模块&#xff1a; Mayavi2 旨在提供一个简单…

隐私计算商业化,原语科技的开源路线是否最优解?

2023年2月&#xff0c;《数字中国建设整体布局规划》出台&#xff0c;首次对我国数字中国建设提出了宏观规划。其中&#xff0c;尤为值得关注的是隐私计算领域。 隐私计算能够实现数据的所有权和使用权分离&#xff0c;可以在保护个人数据隐私安全的基础上&#xff0c;推动数字…

《Linux0.11源码解读》理解(一) 从开机到加载bootsec

计算机启动时, 内存(RAM)没有任何东西, 自然也无法跑操作系统. 但是可以执行固化在ROM里面的BIOS程序. 在按下电源键的一刻. CPU的cs和ip寄存器硬件被设置为0xf000和0xfff0, 于是cs:ip也就指向0xffff0这个地址, 而这个地址正是指向了ROM的BIOS范围(这里是0xfe000~0xfffff, 20根…

Zuul源码解析(二)

Zuul 的自动配置 ZuulProxyAutoConfiguration 如何触发 如上图&#xff0c;在 spring.factory 中配置 ZuulProxyAutoConfiguration 自动配置了&#xff0c;直接点进去 如上图所示&#xff0c;发现这有个条件注解&#xff0c;需要有 org.springframework.cloud.netflix.zuul.Zu…

Kubernetes 核心对象

Kubernetes 中的所有内容都被抽象为“资源”&#xff0c;如 Pod、Service、Node 等都是资源。“对象”就是“资源”的实例&#xff0c;是持久化的实体。Kubernetes 使用这些实体去表示整个集群的状态。它们主要描述了如下信息&#xff1a; 哪些容器化应用正在运行&#xff08;…

ObjectARX中的坐标系以及坐标转换

1 AutoCAD中的坐标系种类 WCS World Coordinate System. The “reference” coordinate system. All other coordinate systems are defined relative to the WCS, which never changes. Values measured relative to the WCS are stable across changes to other coordinate s…

Nginx之正则表达式、location匹配简介以及rewrite重写

引言 在Nginx已经成为很多公司作为前端反向代理服务器的首选&#xff0c;在实际工作中往往会遇到很多跳转(重写URL)的需求。比如更换域名后需要保持旧的域名能跳转到新的域名上、某网页发生改变需要跳转到新的页面、网站防盗链等等需求。如果在后端使用的Apache服务器&#xff…

力扣---LeetCode203. 移除链表元素

文章目录 前言203. 移除链表元素链接方法一&#xff1a;直接删除1.1 注意&#xff1a;1.2 各种情况的流程图&#xff1a;方法二&#xff1a;遍历将非val的节点尾插2.1 流程图&#xff1a;2.2 注意&#xff1a; 总结 前言 你坚持的东西 总有一天会反过来拥抱你 本章的内容是力扣…

智安网络|数据安全问题频发,首推云墙·网站综合防御系统

近期&#xff0c;企业数据安全问题频发&#xff0c;数据安全依旧是企业目前面临的重大风险之一。作为网络安全公司&#xff0c;智安网络以专业的技术团队和丰富的行业经验&#xff0c;为企业和个人提供安全可靠的数据保护服务 案例一&#xff1a; 大学生学习软件超星学习通(下称…

JUC高级十二-ReentrantLock、ReentrantReadWriteLock、StampedLock

无锁→独占锁→读写锁→邮戳锁 1. 关于锁的大厂面试题 你知道Java里面有哪些锁?你说你用过读写锁&#xff0c;锁饥饿问题是什么&#xff1f;有没有比读写锁更快的锁&#xff1f;StampedLock知道吗?(邮戳锁/票据锁)ReentrantReadWriteLock有锁降级机制策略你知道吗&#xff1…

Spring的创建与Bean对象的存取

文章目录&#xff1a;一.Spring项目的创建1.先创建maven项目 2.添加国内源 3.添加spring依赖 4.创建spring配置文件 5.创建启动类 二.Bean对象的创建和读取1.Bean对象的创建与存储方式&#xff08;1&#xff09;类注解 &#xff08;2&#xff09;方法注解 &#xff08;3&#x…

Java中的容器大杂烩-集合

Java中的容器大杂烩-集合 一、 集合引入二、集合框架体系三 、Collection 接口四 、List集合4.1 ArrayList类4.2 LinkedList类4.3 Vector类4.4 ArrayList 、 LinkedList 和 Vector区别 五 、Set集合5.1 HashSet类5.2 TreeSet5.3 LinkedHashSet类 六、List和Set区别七 、Map集合…

制造业巨头遭黑客勒索400万美元,企业如何防范勒索病毒攻击?

4月7日&#xff0c;台湾电脑制造商微星&#xff08;简称MSI&#xff09;发布公开声明&#xff0c;证实其部分网络信息系统遭受了勒索病毒攻击。一个名为“Money Message”的新网络黑客团伙称其从微星的网络系统中窃取了1.5TB的数据&#xff0c;其中包括微星数据库的屏幕截图、源…

YAML /Excel /CSV?自动化测试测试数据管理应用,测试老鸟总结...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 自动化测试无论是…

webhub123 前端技术社区和技术交流学习网站导航

整理了学习前端技术可以参考学习和技术交流的一些网站集合&#xff0c;全部收录到 webhub123 前端技术社区和技术交流学习网站导航http://​www.webhub123.com/#/home/detail?projectHashid30929575&ownerUserid22053727 整理后的效果如下&#xff0c;我们已经按照不同类…

React Props

state 和 props 主要的区别在于 props 是不可变的&#xff0c;而 state 可以根据与用户交互来改变。 所以&#xff0c;有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据。 props 使用 Demo.js &#xff1a; import React from reactfunct…

智能学习 | MATLAB实现ACO-BP多变量时间序列预测(蚁群算法优化BP神经网络)

智能学习 | MATLAB实现ACO-BP多变量时间序列预测(蚁群算法优化BP神经网络) 目录 智能学习 | MATLAB实现ACO-BP多变量时间序列预测(蚁群算法优化BP神经网络)预测效果基本介绍程序设计参考资料预测效果 基本介绍 MATLAB实现ACO-BP多变量时间序列预测(蚁群算法优化BP神经网络…

数据集合注入

集合注入 前面我们已经能完成引入数据类型和简单数据类型的注入&#xff0c;但是还有一种数据类型集合&#xff0c;集合中既可 以装简单数据类型也可以装引用数据类型&#xff0c;对于集合&#xff0c;在Spring中该如何注入呢? 先来回顾下&#xff0c;常见的集合类型有哪些…