谷动谷力

 找回密码
 立即注册
查看: 1317|回复: 0
打印 上一主题 下一主题
收起左侧

【RT-Thread】使用FAL分区管理与easyflash变量管理

[复制链接]
跳转到指定楼层
楼主
发表于 2024-9-30 22:07:40 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 sunsili 于 2024-9-30 22:15 编辑

【RT-Thread】使用FAL分区管理与easyflash变量管理

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 或分区,方便开发者进行调试、测试;

通过上图我们也可以清晰明了了,看到,FAL抽象层向下可以通过Flash硬件进行统一管理,当然也可以使用SFUD框架(串行Flash通用驱动库,这部分RT-Thread已完成官方框架的移植同时提供多个应用历程),而对上也可以使用如DFS、NVM提供的Flash硬件统一访问接口,方便用户更加直接方便对底层Flash硬件的访问操作。
注:非易失性存储器 (NVM):在芯片电源关闭期间保存存储在其中的数据。因此,它被用于没有磁盘的便携式设备中的内存,以及用于可移动存储卡等用途。主要类型有:非易失性半导体存储器 (Non-volatile semiconductor memory, NVSM) 将数据存储在浮栅存储单元中,每个单元都由一个浮栅(floating-gate) MOSFET 组成。
关于存储,可以用一张图来解释:
来源:ROM、RAM、FLASH、NVM......一文搞定(https://blog.csdn.net/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.
完成上述操作后保存退出,并使用 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
  1. // fal.cfg.h

  2. /*
  3. * Copyright (c) 2006-2023, RT-Thread Development Team
  4. *
  5. * SPDX-License-Identifier: Apache-2.0
  6. *
  7. * Change Logs:
  8. * Date           Author       Notes
  9. * 2023-04-21     Wangyuqiang  the first version
  10. */
  11. #ifndef _FAL_CFG_H_
  12. #define _FAL_CFG_H_

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

  15. #ifndef FAL_USING_NOR_FLASH_DEV_NAME
  16. #define NOR_FLASH_DEV_NAME             "norflash0"
  17. #else
  18. #define NOR_FLASH_DEV_NAME              FAL_USING_NOR_FLASH_DEV_NAME
  19. #endif

  20. /* Flash device Configuration */

  21. extern struct fal_flash_dev nor_flash0;

  22. /* flash device table */

  23. #define FAL_FLASH_DEV_TABLE                                          \
  24. {                                                                    \
  25.     &nor_flash0,                                                     \
  26. }

  27. /* Partition Configuration */

  28. #ifdef FAL_PART_HAS_TABLE_CFG

  29. /* partition table */

  30. #define FAL_PART_TABLE                                                                                                  \
  31. {                                                                                                                       \
  32.     {FAL_PART_MAGIC_WROD,  "easyflash", NOR_FLASH_DEV_NAME,                                    0,       512 * 1024, 0}, \
  33.     {FAL_PART_MAGIC_WROD,   "download", NOR_FLASH_DEV_NAME,                           512 * 1024,      1024 * 1024, 0}, \
  34.     {FAL_PART_MAGIC_WROD, "wifi_image", NOR_FLASH_DEV_NAME,                  (512 + 1024) * 1024,       512 * 1024, 0}, \
  35.     {FAL_PART_MAGIC_WROD,       "font", NOR_FLASH_DEV_NAME,            (512 + 1024 + 512) * 1024,  7 * 1024 * 1024, 0}, \
  36.     {FAL_PART_MAGIC_WROD, "filesystem", NOR_FLASH_DEV_NAME, (512 + 1024 + 512 + 7 * 1024) * 1024,  7 * 1024 * 1024, 0}, \
  37. }
  38. #endif /* FAL_PART_HAS_TABLE_CFG */

  39. #endif /* _FAL_CFG_H_ */
复制代码

此时编译的话是找不到该头文件的,需要我们在Keil中设置:
在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 进行修改:
  1. struct fal_flash_dev nor_flash0 =
  2. {
  3.     .name       = FAL_USING_NOR_FLASH_DEV_NAME,
  4.     .addr       = 0,
  5.     .len        = 16 * 1024 * 1024,
  6.     .blk_size   = 4096,
  7.     .ops        = {init, read, write, erase},
  8.     .write_gran = 1
  9. };
复制代码

当然也可以选择不进行修改,根据大佬的原话就是因为在调用初始化接口函数init后,会从flash设备读取正确的参数更新到nor_flash0表项中,我们在使用FAL组件前都需要调用FAL初始化函数fal_init,其内调用flash设备初始化函数fal_flash_init,最后会调用注册到fal_flash_dev设备表项中的初始化函数device_table->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引脚定义可以选用默认即可,当然也可以使用自定义引脚,记住不要与其他引脚产生冲突。
此时我们回到ENV主界面,进入 RT-Thread Components->Device Drivers->Using Serial Flash Universal Driver ,此时我们才可以看到SFUD选项出现(如果没有使能spi是没法看到的),使能后保持默认即可
到这里,ENV的配置暂时告一段落!

1.4 FAL SFUD 测试用例

为了验证 W25Q128 及软件模拟spi在SFUD框架上是否能够成功运行,我们在 .\rt-thread\bsp\lpc55sxx\lpc55s69_nxp_evk\board\ports\ 下新建一个 soft_spi_flash_init.c 文件,代码如下
  1. /*
  2. * Copyright (c) 2006-2023, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date           Author       Notes
  8. * 2023-04-21     Wangyuqiang  the first version
  9. */

  10. #include <rtthread.h>
  11. #include "spi_flash.h"
  12. #include "spi_flash_sfud.h"
  13. #include "drv_soft_spi.h"
  14. #include "drv_pin.h"
  15. #include "rtconfig.h"

  16. #define cs_pin  GET_PINS(1,9)

  17. static int rt_soft_spi_flash_init(void)
  18. {
  19.   int result = -1;

  20.     result = rt_hw_softspi_device_attach("sspi1", "sspi10", cs_pin);
  21.   rt_kprintf("value is %d\n",result);
  22.   
  23.   if(result == RT_EOK)
  24.   {
  25.     rt_kprintf("rt_hw_softspi_device_attach successful!\n");
  26.   }

  27.     if (RT_NULL == rt_sfud_flash_probe("W25Q128", "sspi10"))
  28.     {
  29.         return -RT_ERROR;
  30.     }

  31.     return RT_EOK;
  32. }
  33. 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 文件,并编写测试代码:
  1. //fal_sample.c

  2. /*
  3. * Copyright (c) 2006-2023, RT-Thread Development Team
  4. *
  5. * SPDX-License-Identifier: Apache-2.0
  6. *
  7. * Change Logs:
  8. * Date           Author       Notes
  9. * 2023-04-21     Wangyuqiang  the first version
  10. */
  11. #include "rtthread.h"
  12. #include "rtdevice.h"
  13. #include "board.h"
  14. #include "fal.h"

  15. #define BUF_SIZE 1024

  16. static int fal_test(const char *partiton_name)
  17. {
  18.     int ret;
  19.     int i, j, len;
  20.     uint8_t buf[BUF_SIZE];
  21.     const struct fal_flash_dev *flash_dev = RT_NULL;
  22.     const struct fal_partition *partition = RT_NULL;

  23.     if (!partiton_name)
  24.     {
  25.         rt_kprintf("Input param partition name is null!\n");
  26.         return -1;
  27.     }

  28.     partition = fal_partition_find(partiton_name);
  29.     if (partition == RT_NULL)
  30.     {
  31.         rt_kprintf("Find partition (%s) failed!\n", partiton_name);
  32.         ret = -1;
  33.         return ret;
  34.     }

  35.     flash_dev = fal_flash_device_find(partition->flash_name);
  36.     if (flash_dev == RT_NULL)
  37.     {
  38.         rt_kprintf("Find flash device (%s) failed!\n", partition->flash_name);
  39.         ret = -1;
  40.         return ret;
  41.     }

  42.     rt_kprintf("Flash device : %s   "
  43.                "Flash size : %dK   \n"
  44.                "Partition : %s   "
  45.                "Partition size: %dK\n",
  46.                 partition->flash_name,
  47.                 flash_dev->len/1024,
  48.                 partition->name,
  49.                 partition->len/1024);

  50.     /* erase all partition */
  51.     ret = fal_partition_erase_all(partition);
  52.     if (ret < 0)
  53.     {
  54.         rt_kprintf("Partition (%s) erase failed!\n", partition->name);
  55.         ret = -1;
  56.         return ret;
  57.     }
  58.     rt_kprintf("Erase (%s) partition finish!\n", partiton_name);

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

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

  64.         ret = fal_partition_read(partition, i, buf, len);
  65.         if (ret < 0)
  66.         {
  67.             rt_kprintf("Partition (%s) read failed!\n", partition->name);
  68.             ret = -1;
  69.             return ret;
  70.         }

  71.         for(j = 0; j < len; j++)
  72.         {
  73.             if (buf[j] != 0xFF)
  74.             {
  75.                 rt_kprintf("The erase operation did not really succeed!\n");
  76.                 ret = -1;
  77.                 return ret;
  78.             }
  79.         }
  80.         i += len;
  81.     }

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

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

  87.         ret = fal_partition_write(partition, i, buf, len);
  88.         if (ret < 0)
  89.         {
  90.             rt_kprintf("Partition (%s) write failed!\n", partition->name);
  91.             ret = -1;
  92.             return ret;
  93.         }

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

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

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

  102.         ret = fal_partition_read(partition, i, buf, len);
  103.         if (ret < 0)
  104.         {
  105.             rt_kprintf("Partition (%s) read failed!\n", partition->name);
  106.             ret = -1;
  107.             return ret;
  108.         }

  109.         for(j = 0; j < len; j++)
  110.         {
  111.             if (buf[j] != 0x00)
  112.             {
  113.                 rt_kprintf("The write operation did not really succeed!\n");
  114.                 ret = -1;
  115.                 return ret;
  116.             }
  117.         }

  118.         i += len;
  119.     }

  120.     ret = 0;
  121.     return ret;
  122. }

  123. static void fal_sample(void)
  124. {
  125.     /* 1- init */
  126.     fal_init();

  127.     if (fal_test("font") == 0)
  128.     {
  129.         rt_kprintf("Fal partition (%s) test success!\n", "font");
  130.     }
  131.     else
  132.     {
  133.         rt_kprintf("Fal partition (%s) test failed!\n", "font");
  134.     }

  135.     if (fal_test("download") == 0)
  136.     {
  137.         rt_kprintf("Fal partition (%s) test success!\n", "download");
  138.     }
  139.     else
  140.     {
  141.         rt_kprintf("Fal partition (%s) test failed!\n", "download");
  142.     }
  143. }
  144. MSH_CMD_EXPORT(fal_sample, fal sample);
复制代码

1.5
测试结果
到这里就可以进行编译下载了,成功后的截图如下:
2.
DFS文件系统
2.1
什么是DFS
DFS 是 RT-Thread 提供的虚拟文件系统组件,全称为 Device File System,即设备虚拟文件系统,文件系统的名称使用类似 UNIX 文件、文件夹的风格,目录结构如下图所示:
在 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 接口层、虚拟文件系统层和设备抽象层。
2.3
使用ENV配置DFS
打开ENV,进入路径 RT-Thread Components → DFS: device virtual file system ,使能 DFS: device virtual file system
由于DFS使用的是POSIX接口,而dfs_posix.h已经在新版本中被移除了,如果想要兼容老版本,可以在menuconfig中使能 RT-Thread Components-> Support legacy version for compatibility
由于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
保存退出后使用 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文件,修改后代码:
  1. /*
  2. * Copyright (c) 2006-2023, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date           Author       Notes
  8. * 2023-04-21     Wangyuqiang  the first version
  9. */
  10. #include "rtthread.h"
  11. #include "rtdevice.h"
  12. #include "board.h"
  13. #include "fal.h"

  14. #include <dfs_posix.h>

  15. #define FS_PARTITION_NAME  "filesystem"

  16. #define BUF_SIZE 1024

  17. static int fal_test(const char *partiton_name)
  18. {
  19.     int ret;
  20.     int i, j, len;
  21.     uint8_t buf[BUF_SIZE];
  22.     const struct fal_flash_dev *flash_dev = RT_NULL;
  23.     const struct fal_partition *partition = RT_NULL;

  24.     if (!partiton_name)
  25.     {
  26.         rt_kprintf("Input param partition name is null!\n");
  27.         return -1;
  28.     }

  29.     partition = fal_partition_find(partiton_name);
  30.     if (partition == RT_NULL)
  31.     {
  32.         rt_kprintf("Find partition (%s) failed!\n", partiton_name);
  33.         ret = -1;
  34.         return ret;
  35.     }

  36.     flash_dev = fal_flash_device_find(partition->flash_name);
  37.     if (flash_dev == RT_NULL)
  38.     {
  39.         rt_kprintf("Find flash device (%s) failed!\n", partition->flash_name);
  40.         ret = -1;
  41.         return ret;
  42.     }

  43.     rt_kprintf("Flash device : %s   "
  44.                "Flash size : %dK   \n"
  45.                "Partition : %s   "
  46.                "Partition size: %dK\n",
  47.                 partition->flash_name,
  48.                 flash_dev->len/1024,
  49.                 partition->name,
  50.                 partition->len/1024);

  51.     /* erase all partition */
  52.     ret = fal_partition_erase_all(partition);
  53.     if (ret < 0)
  54.     {
  55.         rt_kprintf("Partition (%s) erase failed!\n", partition->name);
  56.         ret = -1;
  57.         return ret;
  58.     }
  59.     rt_kprintf("Erase (%s) partition finish!\n", partiton_name);

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

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

  65.         ret = fal_partition_read(partition, i, buf, len);
  66.         if (ret < 0)
  67.         {
  68.             rt_kprintf("Partition (%s) read failed!\n", partition->name);
  69.             ret = -1;
  70.             return ret;
  71.         }

  72.         for(j = 0; j < len; j++)
  73.         {
  74.             if (buf[j] != 0xFF)
  75.             {
  76.                 rt_kprintf("The erase operation did not really succeed!\n");
  77.                 ret = -1;
  78.                 return ret;
  79.             }
  80.         }
  81.         i += len;
  82.     }

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

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

  88.         ret = fal_partition_write(partition, i, buf, len);
  89.         if (ret < 0)
  90.         {
  91.             rt_kprintf("Partition (%s) write failed!\n", partition->name);
  92.             ret = -1;
  93.             return ret;
  94.         }

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

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

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

  103.         ret = fal_partition_read(partition, i, buf, len);
  104.         if (ret < 0)
  105.         {
  106.             rt_kprintf("Partition (%s) read failed!\n", partition->name);
  107.             ret = -1;
  108.             return ret;
  109.         }

  110.         for(j = 0; j < len; j++)
  111.         {
  112.             if (buf[j] != 0x00)
  113.             {
  114.                 rt_kprintf("The write operation did not really succeed!\n");
  115.                 ret = -1;
  116.                 return ret;
  117.             }
  118.         }

  119.         i += len;
  120.     }

  121.     ret = 0;
  122.     return ret;
  123. }

  124. static void fal_sample(void)
  125. {
  126.     /* 1- init */
  127.     fal_init();

  128.     if (fal_test("font") == 0)
  129.     {
  130.         rt_kprintf("Fal partition (%s) test success!\n", "font");
  131.     }
  132.     else
  133.     {
  134.         rt_kprintf("Fal partition (%s) test failed!\n", "font");
  135.     }

  136.     if (fal_test("download") == 0)
  137.     {
  138.         rt_kprintf("Fal partition (%s) test success!\n", "download");
  139.     }
  140.     else
  141.     {
  142.         rt_kprintf("Fal partition (%s) test failed!\n", "download");
  143.     }
  144. }
  145. MSH_CMD_EXPORT(fal_sample, fal sample);

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

  152.     /* fal init */
  153.     fal_init();

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

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

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

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

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

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

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

  179.         close(fd);   
  180.     }

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

  186.         close(fd);

  187.         if(size == sizeof(str))
  188.             rt_kprintf("Read data from file test.txt(size: %d): %s \n", size, buf);
  189.     }
  190. }
  191. MSH_CMD_EXPORT_ALIAS(fal_elmfat_sample, fal_elmfat,fal elmfat sample);
复制代码

2.5 测试结果

测试结果如下:

3. Easyflash移植到FAL分区

3.1 简述EasyFlash

关于EasyFlash的来源我们已经讲过了,此处不再赘述。EasyFlash(https://gitee.com/link?target=ht ... Farmink%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. ,选择软件包版本为最新版。
配置后退出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 ,双击打开该文件,完成以下修改:
  1. // 修改 FAL_EF_PART_NAME 为 easyflash
  2. #define FAL_EF_PART_NAME               "easyflash"
  3. // 修改环境变量内容为 {"boot_times", "0"},这里我们先只设置一个开机次数
  4. static const ef_env default_env_set[] = {
  5.         {"boot_times", "0"},
  6. };
复制代码

3.4 编写Easyflash测试用例
  1. /*
  2. * Copyright (c) 2006-2023, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date           Author       Notes
  8. * 2023-04-21     Wangyuqiang  the first version
  9. */
  10. #include "rtthread.h"
  11. #include "rtdevice.h"
  12. #include "board.h"
  13. #include "fal.h"

  14. #include <dfs_posix.h>

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

  17. #define FS_PARTITION_NAME  "filesystem"

  18. #define BUF_SIZE 1024

  19. static int fal_test(const char *partiton_name)
  20. {
  21.     int ret;
  22.     int i, j, len;
  23.     uint8_t buf[BUF_SIZE];
  24.     const struct fal_flash_dev *flash_dev = RT_NULL;
  25.     const struct fal_partition *partition = RT_NULL;

  26.     if (!partiton_name)
  27.     {
  28.         rt_kprintf("Input param partition name is null!\n");
  29.         return -1;
  30.     }

  31.     partition = fal_partition_find(partiton_name);
  32.     if (partition == RT_NULL)
  33.     {
  34.         rt_kprintf("Find partition (%s) failed!\n", partiton_name);
  35.         ret = -1;
  36.         return ret;
  37.     }

  38.     flash_dev = fal_flash_device_find(partition->flash_name);
  39.     if (flash_dev == RT_NULL)
  40.     {
  41.         rt_kprintf("Find flash device (%s) failed!\n", partition->flash_name);
  42.         ret = -1;
  43.         return ret;
  44.     }

  45.     rt_kprintf("Flash device : %s   "
  46.                "Flash size : %dK   \n"
  47.                "Partition : %s   "
  48.                "Partition size: %dK\n",
  49.                 partition->flash_name,
  50.                 flash_dev->len/1024,
  51.                 partition->name,
  52.                 partition->len/1024);

  53.     /* erase all partition */
  54.     ret = fal_partition_erase_all(partition);
  55.     if (ret < 0)
  56.     {
  57.         rt_kprintf("Partition (%s) erase failed!\n", partition->name);
  58.         ret = -1;
  59.         return ret;
  60.     }
  61.     rt_kprintf("Erase (%s) partition finish!\n", partiton_name);

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

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

  67.         ret = fal_partition_read(partition, i, buf, len);
  68.         if (ret < 0)
  69.         {
  70.             rt_kprintf("Partition (%s) read failed!\n", partition->name);
  71.             ret = -1;
  72.             return ret;
  73.         }

  74.         for(j = 0; j < len; j++)
  75.         {
  76.             if (buf[j] != 0xFF)
  77.             {
  78.                 rt_kprintf("The erase operation did not really succeed!\n");
  79.                 ret = -1;
  80.                 return ret;
  81.             }
  82.         }
  83.         i += len;
  84.     }

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

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

  90.         ret = fal_partition_write(partition, i, buf, len);
  91.         if (ret < 0)
  92.         {
  93.             rt_kprintf("Partition (%s) write failed!\n", partition->name);
  94.             ret = -1;
  95.             return ret;
  96.         }

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

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

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

  105.         ret = fal_partition_read(partition, i, buf, len);
  106.         if (ret < 0)
  107.         {
  108.             rt_kprintf("Partition (%s) read failed!\n", partition->name);
  109.             ret = -1;
  110.             return ret;
  111.         }

  112.         for(j = 0; j < len; j++)
  113.         {
  114.             if (buf[j] != 0x00)
  115.             {
  116.                 rt_kprintf("The write operation did not really succeed!\n");
  117.                 ret = -1;
  118.                 return ret;
  119.             }
  120.         }

  121.         i += len;
  122.     }

  123.     ret = 0;
  124.     return ret;
  125. }

  126. static void fal_sample(void)
  127. {
  128.     /* 1- init */
  129.     fal_init();

  130.     if (fal_test("font") == 0)
  131.     {
  132.         rt_kprintf("Fal partition (%s) test success!\n", "font");
  133.     }
  134.     else
  135.     {
  136.         rt_kprintf("Fal partition (%s) test failed!\n", "font");
  137.     }

  138.     if (fal_test("download") == 0)
  139.     {
  140.         rt_kprintf("Fal partition (%s) test success!\n", "download");
  141.     }
  142.     else
  143.     {
  144.         rt_kprintf("Fal partition (%s) test failed!\n", "download");
  145.     }
  146. }
  147. MSH_CMD_EXPORT(fal_sample, fal sample);

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

  154.     /* fal init */
  155.     fal_init();

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

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

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

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

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

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

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

  181.         close(fd);   
  182.     }

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

  188.         close(fd);

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

  194. static void easyflash_sample(void)
  195. {
  196.     /* fal init */
  197.     fal_init();

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

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

  208.         i_boot_times = atol(c_old_boot_times);
  209.         /* boot count +1 */
  210.         i_boot_times ++;
  211.         rt_kprintf("===============================================\n");
  212.         rt_kprintf("The system now boot %d times\n", i_boot_times);
  213.         rt_kprintf("===============================================\n");
  214.         /* interger to string */
  215.         sprintf(c_new_boot_times, "%d", i_boot_times);
  216.         /* set and store the boot count number to Env */
  217.         ef_set_env("boot_times", c_new_boot_times);
  218.         ef_save_env();
  219.     }
  220. }
  221. MSH_CMD_EXPORT(easyflash_sample, easyflash sample);
复制代码

3.5 测试结果

打开串口助手,输入命令:
  1. msh />easyflash_sample
复制代码

第一次命令调用:
第二次RESET开发板后调用:


+12

最近谁赞过

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|深圳市光明谷科技有限公司|光明谷商城|Sunshine Silicon Corpporation ( 粤ICP备14060730号|Sitemap

GMT+8, 2024-12-27 08:26 , Processed in 0.100258 second(s), 43 queries .

Powered by Discuz! X3.2 Licensed

© 2001-2013 Comsenz Inc.

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