潘多拉 RT-Thread Flash 分区管理
实验概述
本例程演示如何通过 RT-Thread 提供的 FAL 软件包对 Flash 进行分区管理操作。例程中,通过调用 FAL 接口完成了对指定分区的测试工作,完成了对 Flash 读、写、擦的测试,同时也通过该例程完成了对 Flash 驱动的基本测试。
FAL 是 Flash Abstraction Layer 的缩写,即 Flash 抽象层。FAL 是 RT-Thread 的一个软件包,用于对 Flash 及基于 Flash 的分区进行管理、操作的抽象层,对上层统一了 Flash 及分区操作的 API,并具有以下特性:
- 支持静态可配置的分区表,并可关联多个 Flash 设备;
- 分区表支持 自动装载。避免在多固件项目,分区表被多次定义的问题;
- 代码精简,对操作系统 无依赖,可运行于裸机平台,比如对资源有一定要求的 bootloader;
- 统一的操作接口。保证了文件系统、OTA、NVM 等对 Flash 有一定依赖的组件,底层 Flash 驱动的可重用性;
- 自带基于 Finsh/MSH 的测试命令,可以通过 Shell 按字节寻址的方式操作(读写擦)Flash 或分区,方便开发者进行调试、测试;
本文将演示如何使用 fal 管理多个 Flash 设备,指导用户通过 fal 分区表操作 Flash 设备。这也是后续 OTA、easyflash 等例程的基础,所以务必掌握。
硬件连接
本例程使用到的硬件资源如下所示:
- UART1(Tx:PA9;Rx:PA10)
- 片内 FLASH(512 KB)
- 片外 Nor Flash(16 MB)
示例代码
参考《潘多拉 IoT Board 开发环境》创建工程,在 applications/main.c 中输入如下代码。
#include <rtthread.h>
#include <fal.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#define BUF_SIZE 1024
static int fal_test(const char *partiton_name);
int main(void)
{
fal_init();
if (fal_test("param") == 0) {
LOG_I("Fal partition (%s) test success!", "param");
}
else {
LOG_E("Fal partition (%s) test failed!", "param");
}
if (fal_test("download") == 0) {
LOG_I("Fal partition (%s) test success!", "download");
}
else {
LOG_E("Fal partition (%s) test failed!", "download");
}
return 0;
}
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) {
LOG_E("Input param partition name is null!");
return -1;
}
partition = fal_partition_find(partiton_name);
if (partition == RT_NULL) {
LOG_E("Find partition (%s) failed!", partiton_name);
ret = -1;
return ret;
}
flash_dev = fal_flash_device_find(partition->flash_name);
if (flash_dev == RT_NULL) {
LOG_E("Find flash device (%s) failed!", partition->flash_name);
ret = -1;
return ret;
}
LOG_I("Flash device : %s, Flash size : %dK, Partition : %s, Partition size: %dK",
partition->flash_name, flash_dev->len/1024, partition->name, partition->len/1024);
/* 擦除 `partition` 分区上的全部数据 */
ret = fal_partition_erase_all(partition);
if (ret < 0) {
LOG_E("Partition (%s) erase failed!", partition->name);
ret = -1;
return ret;
}
LOG_I("Erase (%s) partition finish!", partiton_name);
/* 循环读取整个分区的数据,并对内容进行检验 */
for (i = 0; i < partition->len;) {
rt_memset(buf, 0x00, BUF_SIZE);
len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i);
/* 从 Flash 读取 len 长度的数据到 buf 缓冲区 */
ret = fal_partition_read(partition, i, buf, len);
if (ret < 0) {
LOG_E("Partition (%s) read failed!", partition->name);
ret = -1;
return ret;
}
for(j = 0; j < len; j++) {
/* 校验数据内容是否为 0xFF */
if (buf[j] != 0xFF) {
LOG_E("The erase operation did not really succeed!");
ret = -1;
return ret;
}
}
i += len;
}
/* 把 0 写入指定分区 */
for (i = 0; i < partition->len;) {
/* 设置写入的数据 0x00 */
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) {
LOG_E("Partition (%s) write failed!", partition->name);
ret = -1;
return ret;
}
i += len;
}
LOG_I("Write (%s) partition finish! Write size %d(%dK).", partiton_name, i, i / 1024);
/* 从指定的分区读取数据并校验数据 */
for (i = 0; i < partition->len;) {
/* 清空读缓冲区,以 0xFF 填充 */
rt_memset(buf, 0xFF, BUF_SIZE);
len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i);
/* 读取数据到 buf 缓冲区 */
ret = fal_partition_read(partition, i, buf, len);
if (ret < 0) {
LOG_E("Partition (%s) read failed!", partition->name);
ret = -1;
return ret;
}
for(j = 0; j < len; j++) {
/* 校验读取的数据是否为步骤 3 中写入的数据 0x00 */
if (buf[j] != 0x00) {
LOG_E("The write operation did not really succeed!");
ret = -1;
return ret;
}
}
i += len;
}
ret = 0;
return ret;
}
完整代码:13_component_fal
代码说明
本实验除了 applications/main.c 代码,还依赖 fal 软件包(提供 Flash 抽象层实现)以及 port/fal 目录下的几个文件(通常 ports 存放移植文件)。
文件 | 说明 |
---|---|
fal_cfg.h | fal 配置文件(Flash 设备配置和分区表配置) |
fal_flash_sfud_port.c | fal 操作片外 Nor Flash 的移植文件(将 Flash 读写擦接口注册到 fal) |
fal_flash_stm32l4_port.c | fal 操作片内 Flash 的移植文件(将 Flash 读写擦接口注册到 fal) |
fal_cfg.h 文件中,主要包括 Flash 设备的配置、分区表的配置。
本例程的 Flash 设备列表定义如下所示:
extern const struct fal_flash_dev stm32_onchip_flash;
extern const struct fal_flash_dev nor_flash0;
/* flash device table */
#define FAL_FLASH_DEV_TABLE \
{ \
&stm32_onchip_flash, \
&nor_flash0, \
}
其中,stm32l4_onchip_flash
是 STM32L4 片内 Flash 设备,定义在 ports/fal_flash_stm32l4_port.c 文件中;nor_flash0
是外部扩展的 Nor FLASH 设备,定义在 ports/fal_flash_sfud_port.c 文件中。
分区表配置如下:
/* ====================== Partition Configuration ========================== */
#ifdef FAL_PART_HAS_TABLE_CFG
/* partition table */
#define FAL_PART_TABLE \
{ \
{FAL_PART_MAGIC_WROD, "app", "onchip_flash", 0, 384 * 1024, 0}, \
{FAL_PART_MAGIC_WROD, "param", "onchip_flash", 384 * 1024, 128 * 1024, 0}, \
{FAL_PART_MAGIC_WROD, "easyflash", "nor_flash", 0, 512 * 1024, 0}, \
{FAL_PART_MAGIC_WROD, "download", "nor_flash", 512 * 1024, 1024 * 1024, 0}, \
{FAL_PART_MAGIC_WROD, "wifi_image", "nor_flash", (512 + 1024) * 1024, 512 * 1024, 0}, \
{FAL_PART_MAGIC_WROD, "font", "nor_flash", (512 + 1024 + 512) * 1024, 7 * 1024 * 1024, 0}, \
{FAL_PART_MAGIC_WROD, "filesystem", "nor_flash", (512 + 1024 + 512 + 7 * 1024) * 1024, 7 * 1024 * 1024, 0}, \
}
#endif /* FAL_PART_HAS_TABLE_CFG */
这里有一个宏定义 FAL_PART_HAS_TABLE_CFG
,如果有定义则表示应用程序使用 fal_cfg.h 文件中定义的分区表。
是否使用 fal_cfg.h 文件中定义的分区表,有这样一个准则:
- 如果使用 bootloader 则不定义
FAL_PART_HAS_TABLE_CFG
宏,而使用 bootloader 中定义的分区表; - 如果不使用 bootloader 则需要用户定义
FAL_PART_HAS_TABLE_CFG
宏,从而使用 fal_cfg.h 文件中定义的分区表。
fal_cfg.h 文件中定义的分区表最终会注册到 fal_partition
结构体数组中。结构体定义如下:
struct fal_partition
{
uint32_t magic_word;
/* partition name */
char name[FAL_DEV_NAME_MAX];
/* flash device name for partition */
char flash_name[FAL_DEV_NAME_MAX];
/* partition offset address on flash device */
long offset;
size_t len;
uint32_t reserved;
};
fal_partition
结构体成员简要介绍如下:
成员变量 | 说明 |
---|---|
magic_word | 魔法数,系统使用,用户无需关心 |
name | 分区名字,最大 23 个 ASCII 字符 |
flash_name | 分区所属的 Flash 设备名字,最大 23 个 ASCII 字符 |
offset | 分区起始地址相对 Flash 设备起始地址的偏移量 |
len | 分区大小,单位字节 |
reserved | 保留项 |
Flash 设备对接说明
fal 是 Flash 抽象层,要操作 Flash 设备必然要将 Flash 的读、写、擦接口对接到 fal 抽象层中。在 fal 中,使用 fal_flash_dev
结构体来让用户注册该 Flash 设备的操作接口。结构体定义如下:
struct fal_flash_dev
{
char name[FAL_DEV_NAME_MAX];
/* flash device start address and len */
uint32_t addr;
size_t len;
/* the block size in the flash for erase minimum granularity */
size_t blk_size;
struct
{
int (*init)(void);
int (*read)(long offset, uint8_t *buf, size_t size);
int (*write)(long offset, const uint8_t *buf, size_t size);
int (*erase)(long offset, size_t size);
} ops;
/* write minimum granularity, unit: bit.
1(nor flash)/ 8(stm32f4)/ 32(stm32f1)/ 64(stm32l4)
0 will not take effect. */
size_t write_gran;
};
fal_flash_dev
结构体成员简要介绍如下:
成员变量 | 说明 |
---|---|
name | Flash 设备名字,最大 23 个 ASCII 字符 |
addr | Flash 设备的起始地址(片内 Flash 为 0x08000000,片外 Flash 为 0x00) |
len | Flash 设备容量,单位字节 |
blk_size | Flash 设备最小擦除单元的大小,单位字节 |
ops | Flash 设备的操作函数(init:初始化,read:读取,write:写入,erase:擦除) |
write_gran | Flash 设备的写入最小粒度 |
片内 Flash 设备实例定义在 ports/fal_flash_stm32l4_port.c 文件中,Flash 设备名称为 onchip_flash ,设备容量为 512K,最小擦除单元为 2K, 无初始化接口。
const struct fal_flash_dev stm32l4_onchip_flash = { \
"onchip_flash", \
0x08000000, \
(512 * 1024), \
2048, \
{NULL, read, write, erase} \
};
片外 Nor Flash 设备实例定义在 ports/fal_flash_sfud_port.c 文件中,使用了 RT-Thread 内置的 SFUD 框架。Flash 设备名称为 nor_flash,设备容量为 16M,最小擦除单元为 4K。这里使用的 read、write、erase 接口最终调用 SFUD 框架中的接口,无需用户进行驱动开发。
const struct fal_flash_dev nor_flash0 = { \
"nor_flash", \
0, \
(16 * 1024 * 1024), \
4096, \
{fal_sfud_init, read, write, erase} \
};
编译运行
使能 QSPI Flash 选项