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

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

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 组成。

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

来源: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.

完成上述操作后保存退出,并使用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中设置:

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

此时我们回到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文件,代码如下

/*
 * 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 测试结果

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

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工程。

目录
相关文章
|
6月前
|
存储 安全 编译器
RT-Thread框架中利用FAL组件实现STM32F407内置Flash的读写操作
通过以上步骤,可以在RT-Thread框架下使用FAL组件实现STM32F407内置Flash的高效读写操作。这不仅提高了代码的可移植性和可维护性,还使得Flash的管理更加灵活和安全。
664 99
|
存储 缓存 监控
【嵌入式SD NAND】基于FATFS/Littlefs文件系统的日志框架实现
综上所述,构建一个基于FATFS/Littlefs文件系统的日志框架需要对文件系统的操作有深入理解,并以此为基础设计一套完整的日志处理机制。这样的框架不仅能够确保日志数据的完整性和系统的鲁棒性,同时还能够满足嵌入式系统对于性能和资源使用的严格要求。
759 4
|
Ubuntu
vscode配置clang-format自动格式化代码
vscode配置clang-format自动格式化代码
3880 0
|
前端开发 Java 应用服务中间件
【Java Web编程 七】Servlet基本概念和生命周期
【Java Web编程 七】Servlet基本概念和生命周期
480 0
|
消息中间件 传感器 缓存
RT-Thread记录(十二、I/O 设备模型之UART设备 — 使用测试)
从 UART 设备开始学会使用 RT-Thread I/O 设备模型 。
964 1
RT-Thread记录(十二、I/O 设备模型之UART设备 — 使用测试)
|
Windows
DOS 批处理 setlocal命令、endlocal命令详解
setlocal这是一个命令,它开始局部化环境更改,通常在批处理文件中使用,以确保在脚本中所做的任何环境更改(例如设置或修改环境变量)不会影响到调用此批处理的上下文或其他批处理文件
874 14
|
存储
STM32--ADC模数转换
STM32--ADC模数转换
650 0
|
JSON API 数据安全/隐私保护
95%开发者不知道的调试黑科技:Apipost让WebSocket开发效率翻倍的秘密
在现代Web开发中,WebSocket提供全双工通信,适用于实时交互场景,如IM系统、聊天和客服系统。尽管调试工具众多,但文档设计一直是其短板。本文介绍如何使用Apipost实现WebSocket的高效调试与文档设计。Apipost不仅简化了连接建立、消息发送等调试操作,还通过分组功能优化了消息管理。其文档设计功能支持在同一endpoint下区分业务逻辑,生成清晰易维护的文档,并可一键分享。此外,文章还提供了WebSocket实战技巧,涵盖连接保持、消息格式选择、错误处理及安全性保障等内容,助力开发者提升开发效率。
|
7月前
|
存储 搜索推荐 Java
基于springboot的教育资源共享管理系统
本研究聚焦教育资源共享管理系统,旨在通过Java、Spring Boot、MySQL与Vue技术构建高效平台,促进教育公平,提升教学质量,优化资源配置,推动教育信息化发展。
|
NoSQL 安全 Linux
ARM深入理解-hypervisor调试方法一(异常寄存器分析)
ARM深入理解-hypervisor调试方法一(异常寄存器分析)