Files
STM32F4-Base/docs/FlashDB使用说明.md

12 KiB
Raw Blame History

FlashDB 使用说明

1. 概述

本项目使用 FlashDB 嵌入式数据库,提供两种数据库:

  • KVDB(键值数据库):存储键值对,类似 Redis 简化版
  • TSDB(时序数据库):存储带时间戳的日志记录,支持按时间范围查询

底层存储介质为 GD5F2GQ5UE SPI NAND Flash256MB通过 FALFlash Abstraction Layer抽象层访问。

2. 软件架构

┌─────────────────────────────────────────┐
│           用户应用main.c              │
├──────────────┬──────────────────────────┤
│   fdb_kvdb   │      fdb_tsdb            │
│  (键值数据库) │    (时序数据库)          │
├──────────────┴──────────────────────────┤
│              FlashDB 核心                │
│    Lib/FlashDB/src/                     │
├─────────────────────────────────────────┤
│    FAL 抽象层                           │
│    Lib/FlashDB/port/fal/src/            │
├─────────────────────────────────────────┤
│    FAL 设备适配层                        │
│    fal_flash_gd5f2gq5ue.c              │
├─────────────────────────────────────────┤
│    GD5F2GQ5UE SPI NAND 驱动             │
│    gd5f2gq5ue.c                         │
├─────────────────────────────────────────┤
│    SPI1 硬件外设 + HAL 驱动              │
└─────────────────────────────────────────┘

3. 文件清单

文件 说明 是否自编
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h NAND 驱动头文件
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c NAND 驱动实现
Drivers/BSP/GD5F2GQ5UE/fal_flash_gd5f2gq5ue.c FAL 设备适配
Drivers/BSP/GD5F2GQ5UE/fal_cfg.h FAL 设备表 + 分区表
Drivers/BSP/GD5F2GQ5UE/fdb_cfg.h FlashDB 功能配置
Lib/FlashDB/src/ FlashDB 核心源码
Lib/FlashDB/port/fal/src/ FAL 硬件抽象框架源码

4. 硬件配置

4.1 Flash 芯片参数

参数
型号 GD5F2GQ5UEGigaDevice
总容量 2Gbit = 256MB
页大小 2048 字节
Spare 区 64 字节
每块页数 64 页
块大小 128KB64 × 2048
总块数 2048
SPI 模式 Mode 0CPOL=0, CPHA=0
SPI 时钟 42MHzAPB2=84MHz, 分频=2
MID 0xC8
DID 0x52

4.2 引脚连接

信号 STM32 引脚 方向 说明
CS# PE0 OUT 片选,低有效
SCLK PB3 OUT SPI1 时钟
MOSI PB5 OUT SPI1 主出从入
MISO PB4 IN SPI1 主入从出
WP# PB8 OUT 写保护,低有效(拉高禁用)
HOLD# PE1 OUT 保持,低有效(拉高禁用)

4.3 GPIO 初始化

CubeMX 中需确保以下 GPIO 上电默认电平:

  • CS# (PE0):上电输出 高电平(未选中)
  • HOLD# (PE1):上电输出 高电平(不保持)
  • WP# (PB8):上电输出 高电平(不禁用写保护)

若 GPIO 初始电平错误SPI 通信将完全无响应。

5. 分区规划

分区名 设备 偏移 大小 用途
fdb_kvdb1 gd5f2gq5ue 0 64MB KVDB 键值数据库
fdb_tsdb1 gd5f2gq5ue 64MB 64MB TSDB 时序数据库
(未分配) - 128MB 128MB 剩余空间

共使用 128MB / 256MB剩余 128MB 可扩展。

6. 初始化流程

1. HAL_Init()
2. SystemClock_Config()
3. MX_GPIO_Init()          ← CS#/HOLD#/WP# 初始电平
4. MX_USART1_UART_Init()   ← 调试串口
5. MX_SPI2_Init()          ← CH395F 以太网
6. MX_SPI1_Init()          ← GD5F2GQ5UE NAND
7. gd5f2gq5ue_init()       ← NAND 初始化
8. fdb_kvdb_init()         ← FlashDB KVDB 初始化

6.1 gd5f2gq5ue_init() 内部步骤

1. CS#/WP#/HOLD# 拉高
2. 发送复位命令FFh等待 5ms
3. 读取芯片 ID9Fh校验 MID=0xC8, DID=0x52
4. 使能内部 ECCSET_FEATURE(B0h, 10h)
5. 解除块保护SET_FEATURE(A0h, 00h)

关键点SET_FEATURE 命令前必须先发写使能06h否则设置不生效。

7. KVDB 使用方法

7.1 初始化

#include "flashdb.h"

static struct fdb_kvdb kvdb;

/* path 参数对应分区表中的分区名 "fdb_kvdb1" */
/* default_kv 传 NULL 表示不使用默认键值 */
int ret = fdb_kvdb_init(&kvdb, "db", "fdb_kvdb1", NULL, NULL);
if (ret != FDB_NO_ERR) {
    /* 初始化失败处理 */
}

7.2 字符串读写

/* 写入字符串 */
fdb_kv_set(&kvdb, "device_name", "STM32F407-DTU");

/* 读取字符串(返回内部缓存指针,下次写入同一 key 后失效) */
char *val = fdb_kv_get(&kvdb, "device_name");
if (val) {
    printf("device_name = %s\n", val);
}

7.3 二进制数据读写Blob

#include <string.h>

/* 写入二进制数据 */
uint32_t temp = 2560;
fdb_kv_set_blob(&kvdb, "temperature",
                fdb_blob_make(NULL, &temp, sizeof(temp)));

/* 读取二进制数据 */
uint32_t read_temp = 0;
fdb_kv_get_blob(&kvdb, "temperature",
                fdb_blob_make(NULL, &read_temp, sizeof(read_temp)));
printf("temperature = %u\n", read_temp);

7.4 删除键

fdb_kv_del(&kvdb, "device_name");

7.5 遍历所有键

struct fdb_kv kv;
struct fdb_kv_iterator itr;

fdb_kv_iterator_init(&kvdb, &itr);
while (fdb_kv_iterate(&kvdb, &itr)) {
    kv = itr.curr;
    printf("key: %s\n", kv.name);
}

7.6 打印所有键值

fdb_kv_print(&kvdb);

8. TSDB 使用方法

8.1 获取时间戳

FlashDB 需要用户提供时间戳获取函数。本项目使用 HAL_GetTick()(毫秒):

static fdb_time_t get_time(void)
{
    return (fdb_time_t)HAL_GetTick();
}

8.2 初始化

static struct fdb_tsdb tsdb;

/* max_len 参数限制单条记录最大长度 */
int ret = fdb_tsdb_init(&tsdb, "tsdb", "fdb_tsdb1",
                        get_time, 256, NULL);

8.3 追加记录

/* 写入二进制数据,自动附加当前时间戳 */
uint32_t sensor_val = 3200;
fdb_tsl_append(&tsdb,
               fdb_blob_make(NULL, &sensor_val, sizeof(sensor_val)));

8.4 遍历记录

/* 正序遍历 */
fdb_tsl_iter(&tsdb, tsl_cb_func, NULL);

/* 逆序遍历 */
fdb_tsl_iter_reverse(&tsdb, tsl_cb_func, NULL);

/* 按时间范围遍历 */
fdb_tsl_iter_by_time(&tsdb, from_time, to_time, tsl_cb_func, NULL);

回调函数原型:

static void tsl_cb_func(struct fdb_tsl *tsl, void *arg)
{
    struct fdb_blob blob;
    uint32_t val;

    /* 读取数据 */
    fdb_blob_read((fdb_db_t)&tsl,
                  fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &val, sizeof(val))));
    printf("ts=%lu, val=%lu\n", tsl->time, val);
}

8.5 查询记录数量

size_t count = fdb_tsl_query_count(&tsdb, from_time, to_time,
                                   FDB_TSL_STATUS_APPEND);

8.6 清理所有记录

fdb_tsl_clean(&tsdb);

9. 配置文件说明

9.1 fdb_cfg.h

说明
FDB_USING_KVDB 启用 KVDB
FDB_USING_TSDB 启用 TSDB
FDB_USING_FAL_MODE 使用 FAL 模式(非文件系统)
FDB_WRITE_GRAN 写入粒度 8字节可编程
FDB_DEBUG_ENABLE 启用调试输出
FDB_KV_AUTO_UPDATE KVDB 版本变化时自动更新(默认关闭)

9.2 fal_cfg.h

分区表定义,修改分区大小需同时调整偏移和大小:

#define FAL_PART_TABLE                                                        \
{                                                                             \
    {FAL_PART_MAGIC_WORD, "fdb_kvdb1", "gd5f2gq5ue",           0, 64*1024*1024, 0}, \
    {FAL_PART_MAGIC_WORD, "fdb_tsdb1", "gd5f2gq5ue", 64*1024*1024, 64*1024*1024, 0}, \
}

字段顺序:{魔数, 分区名, 设备名, 偏移, 大小, 标志}

10. Keil 工程配置

10.1 头文件搜索路径

在 Keil 工程 Options → C/C++ → Include Paths 中添加:

../Drivers/BSP/GD5F2GQ5UE
../Lib/FlashDB/inc
../Lib/FlashDB/port/fal/inc

10.2 编译的源文件

需在 Keil 工程中添加以下源文件:

Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c
Drivers/BSP/GD5F2GQ5UE/fal_flash_gd5f2gq5ue.c
Lib/FlashDB/src/fdb.c
Lib/FlashDB/src/fdb_kvdb.c
Lib/FlashDB/src/fdb_tsdb.c
Lib/FlashDB/src/fdb_utils.c
Lib/FlashDB/port/fal/src/fal.c
Lib/FlashDB/port/fal/src/fal_flash.c
Lib/FlashDB/port/fal/src/fal_partition.c

10.3 全局宏定义

确保工程中定义了:

USE_HAL_DRIVER, STM32F407xx

11. 注意事项

11.1 NAND Flash 特性

  1. 写前必须擦除NAND 只能将 1→0不能 0→1。写入前目标块必须先擦除全部变为 0xFF
  2. 擦除单位是块:最小擦除单位 128KB不能按页擦除
  3. 写入单位是页单次写入不超过一页2048 字节),跨页需驱动层拆分
  4. Spare 区不可直接访问:驱动已使能内部 ECCSpare 区由芯片硬件管理

11.2 驱动层注意事项

  1. 每次 SPI 事务前:必须 CS_LOW() 拉低片选
  2. 每次 SPI 事务后:必须 CS_HIGH() 拉高片选
  3. SET_FEATURE 前必须写使能:先发 06h再发 1Fh + 地址 + 数据
  4. 块擦除地址是字节地址D8h 命令的参数 = 块编号 × 128KB不是块编号本身
  5. 读取 ID 需跳过 dummy 字节9Fh 返回 3 字节,第 0 字节无意义,第 1 字节 MID第 2 字节 DID

11.3 FlashDB 使用注意事项

  1. fdb_kv_get 返回值是内部缓存:下次对同一 key 写入后,之前返回的指针失效
  2. TSDB 需要用户提供时间戳:通过 get_time 回调,本项目使用 HAL_GetTick()
  3. fdb_kvdb_init 的 path 参数:对应分区表中的分区名 "fdb_kvdb1"
  4. FlashDB 开启 FDB_DEBUG_ENABLE 后:会通过 fdb_print() 输出调试信息,需确保有可用的输出(如 printf 重定向到串口)
  5. 分区大小修改后:需同步更新 fal_cfg.h 中的偏移和大小

11.4 常见问题

现象 可能原因 解决方法
init 返回 -6 (ID_MISMATCH) SPI 通信失败或芯片未上电 检查接线、GPIO 初始电平、虚焊
KVDB init 失败 分区名不匹配 确认 fdb_kvdb_init 的 path 与 fal_cfg.h 一致
写入后读取为空 未擦除或写入失败 检查写入返回值,确认目标块已擦除
读取数据异常 ECC 错误 检查 gd5f2gq5ue_init 是否成功使能 ECC
SET_FEATURE 未生效 缺少写使能命令 确认 gd5f_set_feature 中先调用 gd5f_write_enable()

12. 调试方法

12.1 串口调试输出

FlashDB 调试输出通过 fdb_print 宏实现。在 fdb_cfg.h 中定义 FDB_DEBUG_ENABLEFlashDB 内部操作会自动输出到标准输出。

确保 Keil 工程中 printf 已重定向到 USART1PA9/PA10, 115200bps

12.2 NAND 驱动调试

可通过串口输出以下信息验证驱动工作正常:

/* 读取芯片 ID */
uint8_t mid, did;
gd5f2gq5ue_read_id(&mid, &did);
printf("MID=0x%02X DID=0x%02X\n", mid, did);
/* 预期输出MID=0xC8 DID=0x52 */

/* 初始化测试 */
int ret = gd5f2gq5ue_init();
printf("init ret=%d\n", ret);
/* 预期输出init ret=0 */

12.3 FAL 设备注册验证

#include "fal.h"

/* 检查 FAL 是否初始化成功 */
int ret = fal_init();
printf("fal_init ret=%d\n", ret);
/* 预期输出fal_init ret=0 */