FAL (Flash Abstraction Layer) 是一个 Flash 抽象层,提供统一的 Flash 设备与分区管理接口, 屏蔽不同 Flash 芯片/控制器(SPI NOR、片上 Flash)的差异。
本组件基于 armink/FlashDB → port/fal 移植而来,原始版权归 RT-Thread Development Team 与 armink 所有(Apache-2.0)。
针对本项目的实际需求,对原始库做了如下裁剪与调整:
- 移除 RT-Thread 相关代码
- 移除
rt_assert/rt_kprintf/rt_mutex等 RTT 依赖 - 日志输出统一走
components/logging/log.h(FAL_ASSERT/FAL_ERROR/FAL_INFO/FAL_DEBUG宏由port/fal_cfg.h映射) - 互斥锁由
struct fal_flash_dev的lock()/unlock()函数指针按设备提供,无锁系统可置 NULL
- 移除
- 移除动态加载分区表
- 原始库支持
fal_partition_table_set()动态注册分区表,本仓库已移除 - 分区表固定为编译期静态表(
FAL_PART_TABLE,定义于port/fal_cfg.h)
- 原始库支持
- 新增
struct fal_flash_dev.initial_ok字段:记录设备初始化结果,未初始化成功的设备在执行fal_flash_read/write/erase时会被拦截并返回FAL_ERR_UNINIT
components/FAL/
├── fal.h # 公开 API
├── fal_def.h # 类型定义(设备/分区结构体、错误码)
├── fal.c # fal_init() 初始化入口
├── fal_flash.c # Flash 设备管理(设备表初始化、find、读写擦)
├── fal_partition.c # 分区管理(分区表初始化、find、读写擦)
├── port/
│ ├── fal_cfg.h # 平台配置(日志宏、设备表、分区表)
│ ├── fal_flash_sfud_port.c # SPI NOR (SFUD) 移植
│ ├── fal_flash_stm32f4_port.c # STM32F4 片上 Flash 移植
│ └── fal_flash_at32_port.c # AT32F415 片上 Flash 移植
└── README.md
#include "fal.h"
void app_init(void)
{
int ret = fal_init(); /* 扫描并初始化所有设备与分区 */
if (ret != FAL_OK) {
/* 至少有一个设备初始化失败,或分区表校验失败 */
}
const struct fal_partition *part = fal_partition_find("download1");
if (part == NULL) {
return;
}
fal_partition_erase(part, 0, 4096); /* 先擦除 */
fal_partition_write(part, 0, data, sizeof(data)); /* 再写入 */
fal_partition_read(part, 0, readback, sizeof(readback)); /* 回读校验 */
}port/fal_cfg.h 中通过宏开关选择启用的 Flash 设备:
| 宏 | 说明 |
|---|---|
FAL_USING_SFUD_PORT |
启用 SPI NOR(SFUD 框架)设备 |
FAL_USING_PORT_STM32F4 |
启用 STM32F4 片上 Flash |
FAL_USING_PORT_AT32 |
启用 AT32F415 片上 Flash |
FAL_DBG_ENABLE |
1 启用日志/断言,0 全部空宏 |
设备表(FAL_FLASH_DEV_TABLE)与分区表(FAL_PART_TABLE)均在 fal_cfg.h 中定义,
由启用的设备宏自动拼装。
| 错误码 | 值 | 含义 |
|---|---|---|
FAL_OK |
0 | 成功 |
FAL_ERR_INIT_FAILED |
-1 | Flash 初始化失败 |
FAL_ERR_FLASH_READ_OOB |
-2 | Flash 读超出容量 |
FAL_ERR_FLASH_READ_OPS_FAILED |
-3 | ops.read 执行失败 |
FAL_ERR_FLASH_WRITE_OOB |
-4 | Flash 写超出容量 |
FAL_ERR_FLASH_WRITE_OPS_FAILED |
-5 | ops.write 执行失败 |
FAL_ERR_FLASH_ERASE_OOB |
-6 | Flash 擦除超出容量 |
FAL_ERR_FLASH_ERASE_OPS_FAILED |
-7 | ops.erase 执行失败 |
FAL_ERR_DEV_NOT_FOUND |
-8 | Flash 设备未找到 |
FAL_ERR_PART_READ_OOB |
-9 | 分区读超出范围 |
FAL_ERR_PART_WRITE_OOB |
-10 | 分区写超出范围 |
FAL_ERR_PART_ERASE_OOB |
-11 | 分区擦除超出范围 |
FAL_ERR_LOCK_FAILED |
-12 | 获取互斥锁失败 |
FAL_ERR_UNINIT |
-13 | 设备未初始化或初始化失败 |
以下为代码审查发现的遗留问题,当前版本尚未修复,使用时需注意:
fal_partition_init()永不返回失败:分区表 magic word 校验失败仅打印日志, 不返回错误码,导致fal_init()中if (fal_partition_init() < 0)分支为永假。 分区表全部无效时初始化仍报告成功。fal_partition_find()缺少FAL_ASSERT(name):release 构建(FAL_ASSERT为空宏) 下传入NULL会导致strcmp崩溃。fal_partition_erase_all()无 NULL 防护:release 构建下传入NULL直接解引用崩溃。- 未校验分区容量与设备容量一致性:
fal_partition_init()不检查part->offset + part->len <= flash_dev->len,分区超出设备容量时仅在实际操作时 由越界检查拦截,初始化阶段不报错。
size == 0空操作直达驱动:fal_flash_read/write/erase及对应分区 API 不拦截零长度操作,会直接调用底层驱动。- 初始化失败设备仍可通过
fal_flash_device_find()查到:initial_ok == 0的设备 只会在 read/write/erase 时被拦截(返回FAL_ERR_UNINIT),find本身不区分成败。
fal.c的sg_fal_init_ok仅用于防重复日志:只写不读,fal_init()重复调用 时会重复初始化(底层有各自幂等保护)。- 错误码覆盖:
fal_partition_init()若未来改为可返回失败,会用FAL_ERR_INIT_FAILED覆盖fal_flash_init()的具体错误码,丢失底层信息。
- STM32F4 扇区不均一:F411 扇区为 16KB×4 + 64KB + 128KB×3,
erase按扇区向上取整, 分区未按扇区对齐时会出现超擦(如app1=96KB 实际擦除 128KB)。当前分区表[96K,128K)、[224K,256K)为空洞,暂无风险,后续新增分区需注意。 app1从偏移 0 开始:若 0x08000000 处存在 bootloader,擦除app1会连同 bootloader 一起抹除,请确认这是有意设计。write_gran仅校验数值合法性:FAL 层不校验写入 offset/size 与写粒度的对齐关系, 对齐约束由各 port 驱动内部处理(stm32f4/at32 已支持任意偏移与任意长度写入)。