删除FlashDB

This commit is contained in:
2026-07-21 21:48:23 +08:00
parent 66a1e8fd6a
commit d48be81478
11 changed files with 102 additions and 971 deletions

View File

@@ -2,28 +2,24 @@
## 1. 概述
本项目在 GD5F2GQ5UE SPI NAND Flash256MB上实现了层存储软件栈:
项目在 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)
FatFs (文件系统)
│ │
├─────────────────────────────────────────────────────────────┤
│ dhara FTL │
│ (地址映射 · 磨损均衡 · 坏块管理 · 垃圾回收) │
├─────────────────────────────────────────────────────────────┤
│ 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) │
└─────────────────────────────────────────────────────────────┘
```
@@ -32,20 +28,17 @@
┌─────────────────────────────────────────────────────────────┐
│ 物理分区布局 (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│
│ ┌─────────────────────────────────────┬───────────────────┐ │
│ │ 预留 (128MB) │ ftl_fatfs (128MB) │ │
│ │ Block 0~1023 │ Block 1024~2047 │ │
│ │ 未使用 │ dhara FTL + FatFS │ │
│ └─────────────────────────────────────┴───────────────────┘ │
│ 偏移: 0 128MB 256MB
└─────────────────────────────────────────────────────────────┘
```
**两条独立路径:**
- **FlashDB 路径**`FlashDB → FAL → gd5f2gq5ue 驱动`FAL 是薄封装层,将 FAL 读写请求直接转发给底层 NAND 驱动。FlashDB 在 FAL 之上自行管理磨损均衡和掉电安全
- **FatFS 路径**`FatFS → dhara FTL → gd5f2gq5ue 驱动`FTL 提供 LBA 到物理页的映射、磨损均衡、垃圾回收和坏块管理FatFS 通过标准 `disk_*` 接口访问 FTL 提供的块设备。
两个路径共享最底层 NAND 驱动但各自管理不同的物理分区FlashDB 管理 Block 0~1023FTL 管理 Block 1024~2047互不干扰。
**数据路径:**
- **应用** → `FatFS``dhara FTL``gd5f2gq5ue 驱动`。FTL 提供 LBA 到物理页的映射、磨损均衡、坏块管理和垃圾回收FatFS 通过标准 `disk_*` 接口访问 FTL 提供的块设备
**初始化顺序:**
```
@@ -53,11 +46,8 @@ HAL_Init → SystemClock_Config → MX_GPIO_Init → MX_USART1_UART_Init
→ MX_SPI2_Init → MX_SPI1_Init
→ gd5f2gq5ue_init() // NAND 底层驱动 (含 BBT 扫描 + ECC 使能)
→ app_main_init() // 应用层初始化
├─ FlashDB: fdb_kvdb_init() // KVDB (可选)
├─ FlashDB: fdb_tsdb_init() // TSDB (可选)
└─ f_mount() // 触发 FTL 初始化和 FatFS 挂载
→ osKernelStart() // FreeRTOS 启动
├─ defaultTask → 存储测试 + 业务
├─ defaultTask → f_mount() → 存储测试 + 业务
├─ netTask → 网络协议栈轮询
├─ adcTask → ADC 采集
└─ rs485Task → RS485 通信
@@ -98,34 +88,26 @@ HAL_Init → SystemClock_Config → MX_GPIO_Init → MX_USART1_UART_Init
## 3. 分区布局
个分区物理隔离、互不重叠,配置集中`Drivers/BSP/GD5F2GQ5UE/fal_cfg.h:18-30`
个分区,配置在 `Drivers/BSP/GD5F2GQ5UE/nand_ftl.c`
```c
#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
#define FTL_FATFS_OFFSET (128U * 1024U * 1024U) // 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) |
| 预留 | 0 | 128 MB | Block 0~1023 | 未使用 |
| `ftl_fatfs` | 128 MB | 128 MB | Block 1024~2047 | dhara FTL + FatFS |
### 3.2 配置联动
所有分区边界在 `fal_cfg.h` 定义后,自动传播到各子模块:
- **FAL 分区表** — `FAL_PART_TABLE` 宏直接引用 `FDB_KVDB1_OFFSET/SIZE``FDB_TSDB1_OFFSET/SIZE`
- **FTL** — `nand_ftl.c:29` 通过 `#define FTL_START_BLOCK (FTL_FATFS_OFFSET / GD5F_BLOCK_SIZE)` 自动计算起始块号(当前 = 1024`FTL_NUM_BLOCKS` 自动适配剩余块数
- **FTL** — `nand_ftl.c``FTL_START_BLOCK = FTL_FATFS_OFFSET / GD5F_BLOCK_SIZE = 1024``FTL_NUM_BLOCKS` 自动适配剩余块数
- **FatFS** — 通过 `disk_ioctl(GET_SECTOR_COUNT)` 获取 FTL 管理的扇区数
> 调整分区大小时只需修改 `fal_cfg.h` 顶部 6 个宏,所有下游模块自动适配。
> 调整分区大小时只需修改 `nand_ftl.c` 中的 `FTL_FATFS_OFFSET` 宏,下游模块自动适配。
---
@@ -237,94 +219,9 @@ DMA 消除了轮询模式下 SPI 状态寄存器查检的逐字节 CPU 开销,
---
## 5. FAL 抽象层
## 5. dhara FTL (Flash Translation Layer)
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)
```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)
```c
#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)
```c
#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 驱动额外的设备注册。
#### 已知 Bug 修复:状态读取偏移
**问题**`Lib/FlashDB/src/fdb_utils.c``_fdb_get_status()` 函数曾有一处错误的 off-by-one 修复(在第 130-139 行额外多减了一次 `status_num`),导致:
- 循环少检查最高状态位(只查 `byte[1,0]` 而非 `byte[2,1,0]`
- `EMPTY`(状态 1被错误地读成 `USING`(状态 2
- 所有空扇区触发 KV 遍历,在全 `0xFF` 区域发现 `len=0xFFFFFFFF`,产生 `"The KV @0xXXXX0014 length has an error"` 报错
**修复**2026-07-21 移除 `_fdb_get_status()` 中多余的 `status_num--`,恢复原始正确的循环边界。详见 `Lib/FlashDB/src/fdb_utils.c:126-144`
### 6.3 KVDB 键值数据库
适用于存储配置参数、运行状态等少量键值对。
**特点:**
- 掉电安全 (写操作带 CRC 校验)
- 支持 blob (二进制数据块)
- 自动磨损均衡
- 支持默认值 (首次启动自动写入)
- 支持 select/poll I/O 多路复用(`net_select.c`
### 6.4 TSDB 时序数据库
适用于存储采样数据、日志记录等时间序列数据。
**特点:**
- 固定长度记录
- 时间戳索引 (需提供 `get_time` 回调)
- 自动擦除 (新数据覆盖最旧数据)
- 支持按时间范围查询
---
## 7. dhara FTL (Flash Translation Layer)
### 7.1 概述
### 5.1 概述
FTL 是 NAND Flash 上方最重要的组件,功能包括:
@@ -336,7 +233,7 @@ FTL 是 NAND Flash 上方最重要的组件,功能包括:
| **坏块管理** | 擦除/编程失败时自动跳过并标记坏块 |
| **ECC 处理** | 检测和上报硬件 ECC 不可纠正错误 |
### 7.2 数据结构
### 5.2 数据结构
```c
struct dhara_nand {
@@ -350,7 +247,7 @@ struct dhara_map {
};
```
### 7.3 NAND HAL (nand_ftl.c)
### 5.3 NAND HAL (nand_ftl.c)
dhara 通过 7 个回调函数访问物理 NAND
@@ -366,7 +263,7 @@ dhara 通过 7 个回调函数访问物理 NAND
所有回调自动将 dhara 逻辑块/页加 `FTL_START_BLOCK` 偏移转换为物理地址。
### 7.4 初始化流程 (disk_initialize)
### 5.4 初始化流程 (disk_initialize)
```
1. 设置 nand 参数 (log2_page_size=11, log2_ppb=6, num_blocks=1024)
@@ -379,7 +276,7 @@ dhara 通过 7 个回调函数访问物理 NAND
4. 页面缓存初始化 (s_cache_buf, s_cached_lpn, s_cache_dirty)
```
### 7.5 页面缓存策略
### 5.5 页面缓存策略
FTL 之上还有一个 **单页写回缓存 (write-back cache)**
@@ -391,9 +288,9 @@ FTL 之上还有一个 **单页写回缓存 (write-back cache)**
---
## 8. FatFS 集成
## 6. FatFS 集成
### 8.1 配置 (ffconf.h)
### 6.1 配置 (ffconf.h)
```c
#define FF_FS_READONLY 0 // 读写模式
@@ -405,7 +302,7 @@ FTL 之上还有一个 **单页写回缓存 (write-back cache)**
#define FF_FS_NORTC 1 // 无 RTC (固定时间戳)
```
### 8.2 disk I/O 接口
### 6.2 disk I/O 接口
| 函数 | 功能 | 关键实现 |
|------|------|----------|
@@ -415,7 +312,7 @@ FTL 之上还有一个 **单页写回缓存 (write-back cache)**
| `disk_write` | 写扇区 | 通过 FTL 映射写 (缓存优化) |
| `disk_ioctl` | 控制命令 | GET_SECTOR_COUNT/SIZE/BLOCK_SIZE + CTRL_SYNC |
### 8.3 容量计算
### 6.3 容量计算
```
FTL 管理块数 = 1024 (Block 1024~2047)
@@ -428,9 +325,9 @@ FTL 开销后 ≈ 93 MB (随 GC 和 journal 使用量波动)
---
## 9. 完整数据流
## 7. 完整数据流
### 9.1 写文件流程
### 7.1 写文件流程
```
f_write("hello.txt")
@@ -450,7 +347,7 @@ f_close → disk_ioctl(CTRL_SYNC)
└─ 写 journal 到 NAND (持久化映射表)
```
### 9.2 f_mkfs 格式化流程
### 7.2 f_mkfs 格式化流程
```
f_mkfs("", &opts, work, size)
@@ -463,9 +360,9 @@ f_mkfs("", &opts, work, size)
---
## 10. API 参考
## 8. API 参考
### 10.1 底层 NAND 驱动 (gd5f2gq5ue.h)
### 8.1 底层 NAND 驱动 (gd5f2gq5ue.h)
| 函数 | 说明 |
|------|------|
@@ -495,31 +392,7 @@ f_mkfs("", &opts, work, size)
| `gd5f_block_erase(block_addr)` | 块擦除 (D8h) |
| `gd5f_check_ecc()` | 检查 ECC 状态位 |
### 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_kv_set(&db, "key", "value")` | 写键值 (字符串) |
| `fdb_kv_get(&db, "key")` | 读键值 (返回 char*) |
| `fdb_kv_del(&db, "key")` | 删除键值 |
| `fdb_kv_set_blob(&db, "key", &blob)` | 写二进制 blob |
| `fdb_kv_get_blob(&db, "key", &blob)` | 读二进制 blob |
| `fdb_kv_set_default(&db)` | 格式化所有 sector 并写入默认值 |
| `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)
### 8.2 FTL/FatFS (via nand_ftl.c / ff.h)
| 函数 | 说明 |
|------|------|
@@ -532,75 +405,9 @@ f_mkfs("", &opts, work, size)
---
## 11. 使用示例
## 9. 使用示例
### 11.1 KVDB 初始化与使用
```c
#include "flashdb.h"
#include "fdb_cfg.h"
/* KVDB 实例 */
static struct fdb_kvdb 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_kv_set(&g_kvdb, "device_id", "STM32F407-001");
/* 读字符串 */
const char *val = fdb_kv_get(&g_kvdb, "device_id");
/* 写二进制 blob */
struct fdb_blob blob;
uint32_t value = 42;
fdb_blob_make(&blob, &value, sizeof(value));
fdb_kv_set_blob(&g_kvdb, "counter", &blob);
/* 读二进制 blob */
uint32_t read_val;
fdb_blob_make(&blob, &read_val, sizeof(read_val));
fdb_kv_get_blob(&g_kvdb, "counter", &blob);
/* 格式化(清除所有数据) */
fdb_kv_set_default(&g_kvdb);
}
```
### 11.2 TSDB 初始化与使用
```c
static struct fdb_tsdb 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};
struct fdb_blob blob;
fdb_blob_make(&blob, &data, sizeof(data));
fdb_tsl_append(&g_tsdb, &blob);
}
```
### 11.3 FatFS 文件操作
### 9.1 FatFS 文件操作
```c
#include "ff.h"
@@ -639,7 +446,7 @@ static void fatfs_write_read(void) {
}
```
### 11.4 FTL 格式化
### 9.2 FTL 格式化
```c
#include "nand_ftl.h"
@@ -652,12 +459,11 @@ if (nand_ftl_format() == 0) {
---
## 12. 注意事项
## 10. 注意事项
1. **擦除对齐**`gd5f2gq5ue_erase()` 的 offset 和 size 必须严格按 GD5F_BLOCK_SIZE (128KB) 对齐和整数倍。
2. **写前擦除** — NAND 不能原地覆写FTL 和 FlashDB 内部自动管理擦除,但直接调用 `gd5f2gq5ue_write()` 前必须确保目标块已擦除。
2. **写前擦除** — NAND 不能原地覆写FTL 内部自动管理擦除,但直接调用 `gd5f2gq5ue_write()` 前必须确保目标块已擦除。
3. **FTL 首个扇区**`dhara_map_init` 的第 5 个参数 (journal 页数) 影响 GC 效率,当前为 4增大可减少写入放大但占用更多内存。
4. **坏块传播** — FTL 在擦除/编程失败后自动调用 `dhara_nand_mark_bad``gd5f2gq5ue_mark_block_bad`BBT 在 RAM 中更新,下次复位后重新扫描出厂坏块并叠加运行时坏块。
5. **功耗** — 擦除操作最大耗时约 5ms (驱动超时设为 5s),页编程约 600ms (超时 1s),读写操作快。在低功耗场景需注意合理安排操作时序。
6. **分区隔离**三个分区物理隔离FlashDB 操作不会影响 FTL 数据,反之亦然。修改分区布局时需同步更新 `fal_cfg.h` 和确认 FTL 宏自动适配。
7. **缓存一致性** — 单页缓存 (s_cache_buf) 仅对 FatFS 层可见,多任务读写同一文件需在应用层同步。
6. **缓存一致性**单页缓存 (s_cache_buf) 仅对 FatFS 层可见,多任务读写同一文件需在应用层同步。