Files
STM32F4-Base/docs/STM32F4-Base存储架构说明.md
2026-07-21 00:30:59 +08:00

23 KiB
Raw Blame History

STM32F4-Base 存储架构说明

1. 概述

本项目在 GD5F2GQ5UE SPI NAND Flash256MB上实现了四层存储软件栈

┌─────────────────────────────────────────────────────────────┐
│                    应用程序 (main, freertos)                  │
├──────────────────────┬──────────────────┬──────────────────┤
│                      │                  │                  │
│    FlashDB KVDB      │   FlashDB TSDB   │   FatFs          │
│    (键值数据库)       │   (时序数据库)    │   (文件系统)      │
│                      │                  │                  │
├──────────┬───────────┴──────┬───────────┴──────────────────┤
│          │                  │                              │
│   FAL 抽象层 (分区访问)     │       dhara FTL               │
│   (Flash Abstraction Layer)│  (地址映射 · 磨损均衡          │
│   直接转发到底层驱动)       │   坏块管理 · 垃圾回收)         │
│          │                  │                              │
├──────────┴──────────────────┴──────────────────────────────┤
│              GD5F2GQ5UE NAND Flash 驱动 (底层 SPI)         │
│    硬件 SPI · 页读写 · 块擦除 · BBT · 内部 ECC 使能         │
├─────────────────────────────────────────────────────────────┤
│              SPI1 硬件外设 (PB3 SCK, PB4 MISO, PB5 MOSI)    │
│       42MHz · Mode 0 · MSB First · DMA2 (S0-RX, S3-TX)     │
│       小包轮询(≤32B) · 页数据 DMA (>32B)                    │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│                     物理分区布局 (256MB)                      │
│                                                              │
│  ┌──────────────────────┬──────────────────┬───────────────┐  │
│  │  fdb_kvdb1 (64MB)    │ fdb_tsdb1 (64MB) │ ftl_fatfs     │  │
│  │  Block  0~511        │ Block 512~1023   │ Block 1024~2047│  │
│  │  FlashDB 键值数据库   │ FlashDB 时序数据库│ dhara + FatFS │  │
│  └──────────────────────┴──────────────────┴───────────────┘  │
│  偏移: 0               64MB              128MB           256MB│
└─────────────────────────────────────────────────────────────┘

两条独立路径:

  • FlashDB 路径FlashDB → FAL → gd5f2gq5ue 驱动FAL 是薄封装层,将 FAL 读写请求直接转发给底层 NAND 驱动。FlashDB 在 FAL 之上自行管理磨损均衡和掉电安全。
  • FatFS 路径FatFS → dhara FTL → gd5f2gq5ue 驱动FTL 提供 LBA 到物理页的映射、磨损均衡、垃圾回收和坏块管理FatFS 通过标准 disk_* 接口访问 FTL 提供的块设备。

两个路径共享最底层 NAND 驱动但各自管理不同的物理分区FlashDB 管理 Block 01023FTL 管理 Block 10242047互不干扰。

初始化顺序:

SPI1_Init → gd5f2gq5ue_init() → fdb_kvdb_init() [可选FlashDB 路径]
                                     → fdb_tsdb_init() [可选FlashDB 路径]
                                     → f_mount() [触发 FTL 初始化FatFS 路径]

2. 硬件规格GD5F2GQ5UE

参数
容量 256 MB (2 Gb)
页大小 (主区) 2048 bytes (2 KB)
备校区 (Spare) 64 bytes/页
每块页数 64
块大小 128 KB (64 × 2 KB)
总块数 2048
总页面数 131072
内部 ECC 支持 (每页 8bit ECC)
接口 SPI (Mode 0, CPOL=0 CPHA=0)
最高时钟 42 MHz
读页延迟 < 60 μs
编程延迟 < 600 μs
块擦除延迟 < 3 ms

SPI 引脚分配:

信号 GPIO 说明
CS# PE0 片选 (低有效)
SCK PB3 SPI1 SCK
MISO PB4 SPI1 MISO (主入从出)
MOSI PB5 SPI1 MOSI (主出从入)
WP# PB8 写保护 (本驱动恒拉高)
HOLD# PE1 保持 (本驱动恒拉高)

3. 分区布局

三个分区物理隔离、互不重叠,配置集中在 Drivers/BSP/GD5F2GQ5UE/fal_cfg.h:18-30

#define FDB_KVDB1_OFFSET    0
#define FDB_KVDB1_SIZE      (64 * 1024 * 1024)     // 64MB
#define FDB_TSDB1_OFFSET    (FDB_KVDB1_OFFSET + FDB_KVDB1_SIZE)
#define FDB_TSDB1_SIZE      (64 * 1024 * 1024)     // 64MB
#define FTL_FATFS_OFFSET    (FDB_TSDB1_OFFSET + FDB_TSDB1_SIZE)  // 128MB
#define FTL_FATFS_SIZE      (128 * 1024 * 1024)    // 128MB

3.1 分区对照表

分区名 偏移 大小 物理块范围 逻辑用途
fdb_kvdb1 0 64 MB Block 0~511 FlashDB 键值数据库 (KVDB)
fdb_tsdb1 64 MB 64 MB Block 512~1023 FlashDB 时序数据库 (TSDB)
ftl_fatfs 128 MB 128 MB Block 1024~2047 dhara FTL + FatFS

3.2 配置联动

所有分区边界在 fal_cfg.h 定义后,自动传播到各子模块:

  • FAL 分区表FAL_PART_TABLE 宏直接引用 FDB_KVDB1_OFFSET/SIZEFDB_TSDB1_OFFSET/SIZE
  • FTLnand_ftl.c 通过 #include "fal_cfg.h" 引用 FTL_FATFS_OFFSET / GD5F_BLOCK_SIZE 计算 FTL_START_BLOCK = 1024
  • FatFS — 通过 disk_ioctl(GET_SECTOR_COUNT) 获取 FTL 管理的扇区数

调整分区大小时只需修改 fal_cfg.h 顶部 6 个宏,所有下游模块自动适配。


4. 驱动层gd5f2gq5ue.c/h

4.1 SPI 命令集

命令 编码 用途
WRITE_ENABLE 0x06 写使能 (每个写/擦除前必须发送)
WRITE_DISABLE 0x04 写禁止
GET_FEATURE 0x0F 读状态/feature 寄存器
SET_FEATURE 0x1F 写 feature 寄存器 (前需写使能)
READ_ID 0x9F 读芯片 ID
PAGE_READ 0x13 将页数据从存储阵列载入内部缓存
READ_FROM_CACHE 0x0B 从内部缓存读取数据
PROGRAM_LOAD 0x02 将数据写入内部缓存
PROGRAM_EXEC 0x10 将缓存数据编程到存储阵列
BLOCK_ERASE 0xD8 块擦除 (128KB)
RESET 0xFF 芯片复位

4.2 Feature 寄存器

地址 名称 说明
0xA0 Protect 块保护 (写入 0x00 解除全部保护)
0xB0 Feature ECC 使能 (bit4=1 启用)
0xC0 Status 状态标志 (OIP/WEL/E_FAIL/P_FAIL/ECC)
0xD0 Driver 驱动强度

4.3 初始化序列

1. RESET (FFh)
    └─ 等待 5ms
2. READ_ID (9Fh)
    └─ 校验 MID=0xC8, DID=0x52
3. BBT 扫描
    └─ 读每块最后一页 (page 63) spare byte 0
    └─ 非 0xFF 即出厂坏块,写入 s_bbt[]
4. SET_FEATURE (B0h=10h) — 使能内部 8bit ECC
5. SET_FEATURE (A0h=00h) — 解除全部块保护

4.4 读写擦除操作

页读取(任何字节偏移均可,驱动自动定位到页):

PAGE_READ (13h + 3字节行地址) → 等待 OIP 清零 → READ_FROM_CACHE (0Bh + 2字节列地址 + dummy)

页编程NAND 只能将 bit 从 1→0 翻转,编程前必须擦除):

WRITE_ENABLE → PROGRAM_LOAD (02h + 列地址 + 数据) → PROGRAM_EXEC (10h + 行地址) → 等待完成 → 检查 P_FAIL

块擦除(最小擦除单位 128KB参数是字节地址而非块编号

WRITE_ENABLE → BLOCK_ERASE (D8h + 3字节字节地址) → 等待完成 → 检查 E_FAIL

4.5 DMA 传输策略2026-07-21 新增)

SPI1 使用 DMA2 实现页数据级别的不阻塞传输,配置如下:

通道 DMA 通道 方向 优先级 模式
SPI1_RX DMA2 Stream 0 CH3 外设→内存 LOW NORMAL
SPI1_TX DMA2 Stream 3 CH3 内存→外设 LOW NORMAL

传输策略:

  • 小包轮询(≤ 32 bytes:命令字、地址、状态寄存器等短数据使用 HAL_SPI_Transmit/Receive 轮询模式,避免 DMA 初始化开销
  • 页数据 DMA> 32 bytesREAD_FROM_CACHEPROGRAM_LOAD 的页数据段使用 HAL_SPI_Receive_DMA / HAL_SPI_Transmit_DMA

HAL 内部路由说明:

HAL_SPI_Receive_DMA() 在 2 线 Master 模式内部调用 HAL_SPI_TransmitReceive_DMA(),但由于 SPI 状态设置为 HAL_SPI_STATE_BUSY_RXHAL 的完成回调为 HAL_SPI_RxCpltCallback(而非 TxRxCpltCallback),需确保该回调在 gd5f2gq5ue.c 中实现。

完整回调链条:

操作 触发回调 实现位置
PROGRAM_LOAD TX DMA HAL_SPI_TxCpltCallback gd5f2gq5ue.c
READ_FROM_CACHE RX DMA HAL_SPI_RxCpltCallback gd5f2gq5ue.c
任意外设错误 HAL_SPI_ErrorCallback ch395f.c(含 SPI1 分支)

性能(顺序 128KB 读写FatFS + FTL

模式
轮询(改造前) 330 KB/s 547 KB/s
DMA改造后 831 KB/s 1855 KB/s
42MHz SPI 理论极限 ~5.25 MB/s受 NAND tPROG ≈ 500μs/页 限制) ~5.25 MB/s

DMA 消除了轮询模式下 SPI 状态寄存器查检的逐字节 CPU 开销,读写性能分别提升 2.5×3.4×。进一步优化需考虑 Cache Read 模式(重叠 NAND 内部 tR 延迟)或批量编程(减少 tPROG 次数)。

4.6 BBT (Bad Block Table)

  • 初始化时扫描全部 2048 块最后一页的 spare byte 0
  • s_bbt[256] 位图数组1 bit 标识 1 个块 (0=好, 1=坏)
  • gd5f2gq5ue_is_block_bad(block) — 查询坏块状态
  • gd5f2gq5ue_mark_block_bad(block) — 标记坏块 (FTL 层在擦除/编程失败时调用)

5. FAL 抽象层

FAL (Flash Abstraction Layer) 是 FlashDB 自带的 flash 抽象层,仅 FlashDB 路径使用。它是一个薄封装层,将 FAL 的 read/write/erase 请求直接转发给底层 NAND 驱动,不做地址映射或磨损均衡(这些由 FlashDB 自身在 FAL 之上完成)。

FAL 不参与 FatFS/FTL 路径。FTL 有自己的 HAL 回调直接对接底层 NAND 驱动,与 FAL 无关。

FlashDB ──→ FAL ──→ gd5f2gq5ue_read/write/erase (直接透传)
FatFs   ──→ dhara FTL ──→ dhara_nand_* 回调 ──→ gd5f2gq5ue_*  (独立路径)

5.1 设备注册 (fal_flash_gd5f2gq5ue.c)

const struct fal_flash_dev g_gd5f2gq5ue_flash = {
    .name       = "gd5f2gq5ue",
    .addr       = 0,
    .len        = GD5F_TOTAL_SIZE,     // 256MB
    .blk_size   = GD5F_BLOCK_SIZE,     // 128KB
    .ops        = { .init, .read, .write, .erase },
    .write_gran = 8,                    // byte programmable
};

5.2 分区表 (fal_cfg.h)

#define FAL_PART_TABLE \
{                                                                                \
    {FAL_PART_MAGIC_WORD, "fdb_kvdb1", "gd5f2gq5ue", FDB_KVDB1_OFFSET, FDB_KVDB1_SIZE, 0}, \
    {FAL_PART_MAGIC_WORD, "fdb_tsdb1", "gd5f2gq5ue", FDB_TSDB1_OFFSET, FDB_TSDB1_SIZE, 0}, \
}

FlashDB 通过分区名 ("fdb_kvdb1", "fdb_tsdb1") 绑定到 FAL 分区。


6. FlashDB 数据库

6.1 配置 (fdb_cfg.h)

#define FDB_USING_KVDB             // 启用键值数据库
#define FDB_USING_TSDB             // 启用时序数据库
#define FDB_USING_FAL_MODE         // 使用 FAL 存储模式
#define FDB_WRITE_GRAN  8          // 写粒度 8bit (字节可编程)

6.2 查询接口

FlashDB 通过 FAL 分区名查找对应的分区设备,无需 FlashDB 驱动额外的设备注册。

6.3 KVDB 键值数据库

适用于存储配置参数、运行状态等少量键值对。

特点:

  • 掉电安全 (写操作带 CRC 校验)
  • 支持 blob (二进制数据块)
  • 自动磨损均衡
  • 支持默认值 (首次启动自动写入)

6.4 TSDB 时序数据库

适用于存储采样数据、日志记录等时间序列数据。

特点:

  • 固定长度记录
  • 时间戳索引 (需提供 get_time 回调)
  • 自动擦除 (新数据覆盖最旧数据)
  • 支持按时间范围查询

7. dhara FTL (Flash Translation Layer)

7.1 概述

FTL 是 NAND Flash 上方最重要的组件,功能包括:

功能 说明
地址映射 将 FatFS 的逻辑扇区号 (LBA) 映射到 NAND 物理页
磨损均衡 均匀分布擦除次数,延长芯片寿命
垃圾回收 回收无效页面空间 (GC)
坏块管理 擦除/编程失败时自动跳过并标记坏块
ECC 处理 检测和上报硬件 ECC 不可纠正错误

7.2 数据结构

struct dhara_nand {
    int  log2_page_size;      // 页大小对数 (2KB → 11)
    int  log2_ppb;            // 每块页数对数 (64 → 6)
    int  num_blocks;          // 管理的物理块数 (1024)
};

struct dhara_map {
    // 内部状态GC 队列、journal、映射表等
};

7.3 NAND HAL (nand_ftl.c)

dhara 通过 7 个回调函数访问物理 NAND

回调 功能 对应底层函数
dhara_nand_is_bad 查询坏块 gd5f2gq5ue_is_block_bad(block + 1024)
dhara_nand_mark_bad 标记坏块 gd5f2gq5ue_mark_block_bad(block + 1024)
dhara_nand_erase 擦除块 nand_block_erase(block + 1024)
dhara_nand_prog 写页 nand_program_load + nand_program_exec
dhara_nand_read 读页 nand_page_read_to_cache + nand_read_from_cache
dhara_nand_is_free 检查页空闲 读前 64 字节判断全为 0xFF
dhara_nand_copy 页拷贝 (GC 用) read + prog 组合

所有回调自动将 dhara 逻辑块/页加 FTL_START_BLOCK 偏移转换为物理地址。

7.4 初始化流程 (disk_initialize)

1. 设置 nand 参数 (log2_page_size=11, log2_ppb=6, num_blocks=1024)
2. dhara_map_init(&s_map, &s_nand, s_page_buf, 4)
   ├─ s_page_buf: dhara 内部使用的 2KB 工作缓冲区
   └─ 4: journal 页面数量 (影响 GC 效率, 增大可减少写入放大)
3. dhara_map_resume(&s_map)
   ├─ 成功: 加载已有映射表
   └─ 失败: dhara_map_clear 创建空映射表
4. 页面缓存初始化 (s_cache_buf, s_cached_lpn, s_cache_dirty)

7.5 页面缓存策略

FTL 之上还有一个 单页写回缓存 (write-back cache)

  • 读命中: 直接返回 s_cache_buf 数据
  • 读未命中: 刷出脏页 → 读新页到缓存
  • : 写入缓存 → 标记脏
  • 全页写入: 直接刷出 (跳过缓存)
  • 同步 (CTRL_SYNC): 刷出脏页 + dhara_map_sync

8. FatFS 集成

8.1 配置 (ffconf.h)

#define FF_FS_READONLY   0       // 读写模式
#define FF_USE_MKFS      1       // 启用格式化
#define FF_MIN_SS        512     // 最小扇区大小
#define FF_MAX_SS        512     // 最大扇区大小
#define FF_VOLUMES       1       // 单卷
#define FF_FS_TINY       0       // 非 tiny 模式
#define FF_FS_NORTC      1       // 无 RTC (固定时间戳)

8.2 disk I/O 接口

函数 功能 关键实现
disk_initialize 初始化 FTL 见 7.4 节
disk_status 查询状态 返回初始化状态
disk_read 读扇区 通过 FTL 映射读物理页
disk_write 写扇区 通过 FTL 映射写 (缓存优化)
disk_ioctl 控制命令 GET_SECTOR_COUNT/SIZE/BLOCK_SIZE + CTRL_SYNC

8.3 容量计算

FTL 管理块数 = 1024 (Block 1024~2047)
每块页数 = 64
每页扇区数 (512B) = 4
总扇区数 = 1024 × 64 × 4 = 262144
总容量 = 262144 × 512 = 128MB (原始容量)
FTL 开销后 ≈ 93 MB (随 GC 和 journal 使用量波动)

9. 完整数据流

9.1 写文件流程

f_write("hello.txt")
  └─ FatFS: 计算 LBA, 写扇区
       └─ disk_write(0, data, sector=100, count=2)
            ├─ 计算 LPN = sector / 4 = 25
            ├─ 缓存未命中 → ftl_flush_cache() → ftl_read_page(25)
            ├─ 拷贝数据到 s_cache_buf → 标记脏
            └─ 全页面写入 → ftl_flush_cache()
                 └─ dhara_map_write(&s_map, 25, s_cache_buf)
                      ├─ 查找页映射 (或分配新页)
                      ├─ dhara_nand_prog(pg, data) → 物理写
                      └─ 更新映射表

f_close → disk_ioctl(CTRL_SYNC)
  └─ ftl_flush_cache() → dhara_map_sync()
       └─ 写 journal 到 NAND (持久化映射表)

9.2 f_mkfs 格式化流程

f_mkfs("", &opts, work, size)
  ├─ disk_initialize(0) → FTL 初始化
  ├─ disk_write: 写入引导扇区 (MBR/PBR)
  ├─ disk_write: 写入 FAT 表
  ├─ disk_write: 创建根目录
  └─ disk_ioctl(CTRL_SYNC) → FTL sync

10. API 参考

10.1 底层 NAND 驱动 (gd5f2gq5ue.h)

函数 说明
gd5f2gq5ue_init() 初始化 NAND (含 ECC 使能、块保护解除)
gd5f2gq5ue_read_id(mid, did) 读芯片 ID
gd5f2gq5ue_read(offset, buf, size) 读数据 (支持跨页)
gd5f2gq5ue_write(offset, buf, size) 写数据 (支持跨页)
gd5f2gq5ue_erase(offset, size) 块擦除 (需块对齐)
gd5f2gq5ue_reset() 复位芯片
gd5f2gq5ue_is_block_bad(block) 查询坏块
gd5f2gq5ue_mark_block_bad(block) 标记坏块

10.2 FAL 接口 (via fal_flash_gd5f2gq5ue.c)

函数 说明
g_gd5f2gq5ue_flash FAL 设备实例
fal_flash_device_find("gd5f2gq5ue") 查找 Flash 设备
fal_partition_find("fdb_kvdb1") 查找分区
fal_partition_read/write/erase 分区读写擦除

10.3 FlashDB (via FlashDB 库)

函数 说明
fdb_kvdb_init(&db, "kvdb1", "fdb_kvdb1", NULL, NULL) 初始化 KVDB
fdb_kvdb_set(&db, "key", "value") 写键值 (字符串)
fdb_kvdb_get(&db, "key", &data) 读键值
fdb_kvdb_del(&db, "key") 删除键值
fdb_kvdb_set_blob(&db, "key", &blob) 写二进制 blob
fdb_tsdb_init(&db, "tsdb1", "fdb_tsdb1", get_time, 1024, NULL) 初始化 TSDB
fdb_tsl_append(&db, data) 追加时序记录
fdb_tsl_iter_by_time(&db, from, to, cb, cb_arg) 时间范围查询

10.4 FTL/FatFS (via nand_ftl.c / ff.h)

函数 说明
f_mount(&fs, "", 1) 挂载文件系统 (首次调用自动初始化 FTL)
f_mkfs("", &opts, work, size) 格式化 FAT32
f_open/f_close/f_read/f_write 标准文件操作
f_unlink("test.txt") 删除文件
disk_initialize(0) 显式初始化 FTL (通常由 f_mount 自动调用)
nand_ftl_format() 直接格式化 FTL (清空映射表, 慎用)

11. 使用示例

11.1 KVDB 初始化与使用

#include "flashdb.h"
#include "fdb_cfg.h"

/* KVDB 实例 */
static fdb_kvdb_t g_kvdb;

static int kvdb_init(void) {
    fdb_err_t ret = fdb_kvdb_init(&g_kvdb, "kvdb1", "fdb_kvdb1", NULL, NULL);
    return (ret == FDB_NO_ERR) ? 0 : -1;
}

static void kvdb_example(void) {
    /* 写字符串 */
    fdb_kvdb_set(&g_kvdb, "device_id", "STM32F407-001");

    /* 读字符串 */
    char buf[64];
    fdb_kvdb_get(&g_kvdb, "device_id", buf, sizeof(buf));

    /* 写二进制 blob */
    struct fdb_blob blob;
    uint32_t value = 42;
    fdb_blob_make(&blob, &value, sizeof(value));
    fdb_kvdb_set_blob(&g_kvdb, "counter", &blob);
}

11.2 TSDB 初始化与使用

static fdb_tsdb_t g_tsdb;

static time_t get_timestamp(void) {
    return sys_clock_now();  // 从 RTC 获取 Unix 时间戳
}

static int tsdb_init(void) {
    fdb_err_t ret = fdb_tsdb_init(&g_tsdb, "tsdb1", "fdb_tsdb1",
                                   get_timestamp, 1024, NULL);
    return (ret == FDB_NO_ERR) ? 0 : -1;
}

typedef struct {
    float voltage;
    float current;
    float temperature;
} sensor_data_t;

static void tsdb_example(void) {
    sensor_data_t data = {3.3f, 0.5f, 25.6f};
    fdb_tsl_append(&g_tsdb, &data);
}

11.3 FatFS 文件操作

#include "ff.h"

static FATFS fs;

static int fatfs_init(void) {
    FRESULT res = f_mount(&fs, "", 1);
    if (res == FR_NO_FILESYSTEM) {
        /* 首次使用需格式化 */
        MKFS_PARM opts = {FM_FAT32, 0, 0, 0, 0};
        uint8_t work[512];
        res = f_mkfs("", &opts, work, sizeof(work));
        if (res != FR_OK) return -1;
        res = f_mount(&fs, "", 1);
    }
    return (res == FR_OK) ? 0 : -1;
}

static void fatfs_write_read(void) {
    FIL fil;
    UINT bw, br;
    const char *msg = "Hello Storage!";
    char buf[32];

    /* 写文件 */
    f_open(&fil, "data.txt", FA_CREATE_ALWAYS | FA_WRITE);
    f_write(&fil, msg, strlen(msg), &bw);
    f_close(&fil);

    /* 读文件 */
    f_open(&fil, "data.txt", FA_READ);
    f_read(&fil, buf, sizeof(buf), &br);
    buf[br] = '\0';
    f_close(&fil);
}

11.4 FTL 格式化

#include "nand_ftl.h"

/* 注意: 此操作将清空 FTL 分区全部数据, 慎用 */
if (nand_ftl_format() == 0) {
    DBG_INFO("FTL formatted");
}

12. 注意事项

  1. 擦除对齐gd5f2gq5ue_erase() 的 offset 和 size 必须严格按 GD5F_BLOCK_SIZE (128KB) 对齐和整数倍。
  2. 写前擦除 — NAND 不能原地覆写FTL 和 FlashDB 内部自动管理擦除,但直接调用 gd5f2gq5ue_write() 前必须确保目标块已擦除。
  3. FTL 首个扇区dhara_map_init 的第 5 个参数 (journal 页数) 影响 GC 效率,当前为 4增大可减少写入放大但占用更多内存。
  4. 坏块传播 — FTL 在擦除/编程失败后自动调用 dhara_nand_mark_badgd5f2gq5ue_mark_block_badBBT 在 RAM 中更新,下次复位后重新扫描出厂坏块并叠加运行时坏块。
  5. 功耗 — 擦除操作最大耗时约 5ms (驱动超时设为 5s),页编程约 600ms (超时 1s),读写操作快。在低功耗场景需注意合理安排操作时序。
  6. 分区隔离 — 三个分区物理隔离FlashDB 操作不会影响 FTL 数据,反之亦然。修改分区布局时需同步更新 fal_cfg.h 和确认 FTL 宏自动适配。
  7. 缓存一致性 — 单页缓存 (s_cache_buf) 仅对 FatFS 层可见,多任务读写同一文件需在应用层同步。