查看: 3167|回复: 46

[原创] 【LPC55s69】使用FAL分区管理与easyflash变量管理

[复制链接]

该用户从未签到

4

主题

9

帖子

0

注册会员

Rank: 2

积分
120
最后登录
2024-3-6
发表于 2023-4-23 19:24:54 | 显示全部楼层 |阅读模式
本帖最后由 以放 于 2023-4-25 14:53 编辑

本博文作为我本人在移植RT-Thread软件spi框架成功后所提供的配套应用测试开发文档,文中部分测试用例已上传RT-Thread仓库,而目前LPC55s69系列与RT-Thread的对接已完善大部分,欢迎大家测试。

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

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

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.   
  12. #include "rtthread.h"
  13. #include "rtdevice.h"
  14. #include "board.h"
  15. #include "fal.h"

  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);
复制代码

1.5 测试结果

打开串口工具,输入命令:
  1. msh />fal_sample
复制代码
到这里就可以进行编译下载了,成功后的截图如下:

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.   
  11. #include "rtthread.h"
  12. #include "rtdevice.h"
  13. #include "board.h"
  14. #include "fal.h"

  15. #include <dfs_posix.h>

  16. #define FS_PARTITION_NAME  "filesystem"

  17. #define BUF_SIZE 1024

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  120.          i += len;
  121.      }

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

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

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

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

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

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

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

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

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

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

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

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

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

  180.          close(fd);   
  181.      }

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

  187.          close(fd);

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

2.5 测试结果

打开串口工具,输入命令:
  1. msh />fal_elmfat_sample
复制代码
测试结果如下:

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.,选择软件包版本为最新版。
配置后退出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.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.   
  11. #include "rtthread.h"
  12. #include "rtdevice.h"
  13. #include "board.h"
  14. #include "fal.h"

  15. #include <dfs_posix.h>

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

  18. #define FS_PARTITION_NAME  "filesystem"

  19. #define BUF_SIZE 1024

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  122.          i += len;
  123.      }

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

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

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

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

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

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

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

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

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

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

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

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

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

  182.          close(fd);   
  183.      }

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

  189.          close(fd);

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

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

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

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

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

3.5 测试结果

打开串口助手,输入命令:
  1. msh />easyflash_sample
复制代码
第一次命令调用:
第二次RESET开发板后调用:

4.结语

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

5.联系

参考资料:







回复

使用道具 举报

  • TA的每日心情
    开心
    2024-3-26 15:16
  • 签到天数: 266 天

    [LV.8]以坛为家I

    3300

    主题

    6547

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    32030
    最后登录
    2024-4-26
    发表于 2023-4-24 09:35:51 | 显示全部楼层
    赞一下,非常详细啊!
    签到签到
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    5 小时前
  • 签到天数: 681 天

    [LV.9]以坛为家II

    12

    主题

    3038

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    3671
    最后登录
    2024-4-26
    发表于 2023-4-24 10:54:40 | 显示全部楼层
    学习。之前对恩智浦的开发板不熟悉(老早以前用过iMX280,Linux下的开发,不知道是不是恩智浦的),有了楼主共享的资料,有机会要尝试下。谢谢!
    哎...今天够累的,签到来了~
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    2016-12-2 08:40
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    97

    主题

    836

    帖子

    7

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    3975
    最后登录
    2024-4-16
    发表于 2023-4-24 11:16:52 | 显示全部楼层
    感谢分享~
    回复

    使用道具 举报

    该用户从未签到

    4

    主题

    9

    帖子

    0

    注册会员

    Rank: 2

    积分
    120
    最后登录
    2024-3-6
     楼主| 发表于 2023-4-24 11:44:01 来自手机 | 显示全部楼层
    本帖最后由 以放 于 2023-4-24 11:45 编辑

    目前lpcsxx系列已完成软件spi框架的对接,用户可以在RT-Thread的设备框架下驱动软件spi了,也欢迎大家积极使用RTT,开发过程中的相关问题可随时与我取得联系!
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    4

    主题

    9

    帖子

    0

    注册会员

    Rank: 2

    积分
    120
    最后登录
    2024-3-6
     楼主| 发表于 2023-4-24 11:54:56 | 显示全部楼层
    NXP管管 发表于 2023-4-24 09:35
    赞一下,非常详细啊!

    感谢支持
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    4

    主题

    9

    帖子

    0

    注册会员

    Rank: 2

    积分
    120
    最后登录
    2024-3-6
     楼主| 发表于 2023-4-24 11:55:59 | 显示全部楼层
    suncat0504 发表于 2023-4-24 10:54
    学习。之前对恩智浦的开发板不熟悉(老早以前用过iMX280,Linux下的开发,不知道是不是恩智浦的),有了楼 ...

    欢迎评测,如果开发过程中有遇到相关问题可以相互探讨交流哇!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    7 小时前
  • 签到天数: 130 天

    [LV.7]常住居民III

    9

    主题

    290

    帖子

    0

    高级会员

    Rank: 4

    积分
    677
    最后登录
    2024-4-26
    发表于 2023-4-25 07:21:41 | 显示全部楼层
    辛苦了,我想参与项目,通过什么试呢?
    今天天气不错!签到!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    6 小时前
  • 签到天数: 1296 天

    [LV.10]以坛为家III

    7

    主题

    6321

    帖子

    1

    金牌会员

    Rank: 6Rank: 6

    积分
    10538
    最后登录
    2024-4-26
    发表于 2023-4-25 09:05:30 | 显示全部楼层
    感谢分享
    回复

    使用道具 举报

  • TA的每日心情
    奋斗
    6 小时前
  • 签到天数: 1277 天

    [LV.10]以坛为家III

    2

    主题

    5766

    帖子

    1

    金牌会员

    Rank: 6Rank: 6

    积分
    9720
    最后登录
    2024-4-26
    发表于 2023-4-25 09:37:09 | 显示全部楼层
    不错,支持一下!
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

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

    本版积分规则

    关闭

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

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

    GMT+8, 2024-4-26 14:26 , Processed in 0.151883 second(s), 28 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

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