FlashDB 与 NVM 存储方案

FlashDB 概述

FlashDB 为应用提供统一的 NVM(非易失性存储器)操作接口,支持初始化、读写、删除、重置等功能,适配不同存储场景(如随机存储、时序存储)。

核心接口说明

FlashDB 提供两类接口:通用 NVM 操作(app_flashdb_xxx)和时序数据库操作(app_tsdb_xxx),具体如下:

rt_err_t app_flashdb_init(void *db,);

作用:用于开机时候初始化使用。当使能kvdb的时候,需要对对应的kvdb以及tsdb进行初始化

  1. 去初始化

void app_flashdb_deinit(void *db,);

作用:用于关机的时候去初始化使用。当使能kvdb的时候,需要对对应的kvdb以及tsdb进行去初始化

  1. 重置flashdb

size_t app_flashdb_reset(void *db,);

作用:用于恢复出厂设置的时候,重置db_name对应的flashdb。

  1. 删除flashdb

rt_err_t app_flashdb_del(void *db, const char *key_name);

作用:删除db_name对应的flashdb。

  1. 读flashdb

size_t app_flashdb_read(void *db, const char *key_name, const void *data, uint32_t len);

作用:读取db_name对应的flashdb。

  1. 写flashdb

rt_err_t app_flashdb_write(void *db, const char *key_name, const void *data, uint32_t len);

作用:写db_name对应的flashdb。

  1. 清除tsdb的内容

void app_tsdb_clean(void *db);

作用:清除db对应的tsdb。

  1. 删除tsdb中start_time到end_time的内容

size_t app_tsdb_del(void *db, fdb_time_t start_time, fdb_time_t end_time, bool mode);

作用:删除db对应的tsdb中start_time到end_time的内容。

  1. 读取tsdb中start_time到end_time的内容

size_t app_tsdb_read(void *db, fdb_time_t start_time, fdb_time_t end_time, tsdb_read_cb_t cb);

作用:读取db对应的tsdb中start_time到end_time的内容。

  1. 写tsdb

rt_err_t app_tsdb_write(void *db, fdb_time_t timestamp, void *data, uint32_t len);

作用:顺序写内容到tsdb。

存储机制与适用场景

Solution 支持三种 NVM 存储机制,适配不同硬件和需求:

  1. 开源 FlashDB

    • 基于键值对(KVDB)和时序(TSDB)两种模式:

      • KVDB:适用于随机读写的小数据(如配置信息);

      • TSDB:适用于按时间戳顺序存储的批量数据(如传感器历史)。

    • 特点:无需文件系统,依赖 Flash 分区管理;支持磨损均衡和数据恢复。

    • 默认场景:NOR Flash 方案。

  2. 文件系统 SiFli 格式

    • 原理:将每个 NVM 子类(如蓝牙信息、应用配置)存储为独立文件,文件含头信息(长度+CRC),读取时校验完整性。

    • 特点:效率高,但依赖文件系统支持;适合频繁读写场景。

    • 默认场景:NAND/eMMC 方案。

  3. 分区裸写格式

    • 自定义实现,直接操作 Flash 分区,无统一框架,需用户自行处理读写逻辑。

Solution 中 NVM 的分类与使用规范

根据数据特性,Solution 将 NVM 分为以下几类,分别对应不同操作接口:

分类序号

NVM 用途描述

说明

1

BLE/BT 固件相关信息

为 SDK 提供必要的蓝牙固件基础信息

2

DFU 升级过程中相关信息

存储设备固件升级过程中的临时数据和状态

3

APP 相关信息

包括应用设置、电话号码本、设备信息及应用状态

4

消息存储

用于保存各类应用消息数据

5

sensor 算法结果相关的历史记录

如步数、心率、血氧、睡眠、GPS 轨迹等数据

Solution 的系统级 NVM 操作通过以下文件实现,提供统一的访问接口宏,主要用于管理系统共性信息(不直接面向具体应用):

实现文件

功能范围

核心接口宏(随机存储)

app_nvm.c

应用层系统级NVM 操作

nvm_xxx_setnvm_xxx_getnvm_xxx_update
nvm_xxx_copy_updatenvm_xxx_is_updated

ble_nvm.c

BLE 协议栈相关 NVM 操作

同上(针对 BLE 固件信息、连接参数等系统数据)

bt_nvm.c

蓝牙协议相关 NVM 操作

同上(针对蓝牙配置、配对信息等系统数据)

sensor_nvm.c

传感器系统级历史数据操作

时序存储接口(app_tsdb_xxx),
用于传感器算法结果的系统级存储

说明
上述接口宏和实现文件主要用于存储 Solution 系统运行必需的共性信息(如系统状态信息、协议配置、硬件状态等),不直接支持具体应用(尤其是外置动态应用)的专属数据存储

若需为具体应用(如动态插件、模块化功能)管理独立 NVM 数据,请参考 七、应用层 NVM 操作接口:app_nvm_readapp_nvm_write,该接口专为应用级数据设计,确保与系统数据隔离。

接口宏说明

接口宏

作用描述

nvm_xxx_set

将结构成员 x 的数组元素统一设为 y(适用于数组场景)

nvm_xxx_get

获取结构成员 x 的值(需已知成员类型)

nvm_xxx_update

更新基本类型成员 x 的值(检查变化,标记更新状态)

nvm_xxx_copy_update

更新构造类型成员 x 的值(检查变化,标记更新状态)

nvm_xxx_is_updated

判断成员 x 是否有更新(用于触发存储)

存储策略

  • 非消息类数据:常驻内存,睡眠前检查更新并写入 Flash(文件系统方案)。

  • 消息类数据:支持实时写或关机时写(关机写依赖内存暂存)。

  • 历史记录:通过 app_tsdb_xxx 接口实时读写(因数据量大、周期长)。

NVM 分区配置

分区需根据存储机制调整,确保效率和可靠性:

  1. 使用 FlashDB 时

    • 按“NVM 分类”为每个类型在 flash_map.xls 中分配独立分区。

    • 注意:FlashDB 回收旧块时耗时与分区大小正相关,分区需按实际存储量预留少量余量(避免过大)。

  2. 使用文件系统 SiFli 格式时

    • 分两个核心分区:

      • sys 分区:存储恢复出厂设置不删除的数据(如设备唯一标识)。

      • nvm 分区:存储其他频繁更新的 NVM 数据(建议独立分区,减少磨损均衡带来的块搬移)。

通过以上配置和接口,Solution 实现了 NVM 存储的高效、可靠管理,适配不同硬件和应用场景。

应用层 NVM 操作接口:app_nvm_readapp_nvm_write

接口特性与适用场景

app_nvm_readapp_nvm_write 专为独立应用(尤其是外置动态应用) 设计,核心特点:

  • 数据独立性:存储的 NVM 数据与系统级 NVM 完全隔离,避免冲突;

  • 动态应用适配:适合外置动态应用存储专属数据(如配置、状态等)。

无论是基于 FlashDB(开源 FlashDB )还是独立文件(文件系统 SiFli 格式),均通过 key_name 唯一标识数据,确保不同应用数据互不干扰。

1. 函数原型与参数

/**
 * @brief  应用层NVM写入接口(适用于独立应用/动态应用)
 * @param  key_name 应用专属数据标识(需唯一,建议添加应用前缀避免冲突)
 * @param  data     待写入的数据缓冲区(支持结构体、数组等任意格式)
 * @param  length   数据长度(单位:字节)
 * @retval RT_EOK   写入成功
 * @retval RT_ERROR 写入失败
 */
rt_err_t app_nvm_write(const char *key_name, const void *data, size_t length);

/**
 * @brief  应用层NVM读取接口(适用于独立应用/动态应用)
 * @param  key_name 数据标识(需与写入时的key一致)
 * @param  data     接收数据的缓冲区(需预先分配内存)
 * @param  length   期望读取的数据长度(单位:字节)
 * @retval 实际读取的字节数(0表示读取失败或无数据)
 */
size_t app_nvm_read(const char *key_name, const void *data, size_t length);

2. 使用例程

存储应用配置(使用 app_nvm_write

// 定义应用专属配置结构体(示例)
typedef struct 
{
    uint8_t brightness;  // 亮度设置(0-100)
    uint8_t volume;      // 音量设置(0-30)
    bool auto_start;     // 自动启动开关
} dyn_app_config_t;

/**
 * 功能:将应用配置写入 NVM
 */
void dyn_app_save_config(void) 
{
    // 1. 准备配置数据
    dyn_app_config_t config = 
    {
        .brightness = 80,
        .volume = 20,
        .auto_start = true
    };

    // 2. 写入 NVM(使用应用专属 key:"dyn_app_config")
    rt_err_t ret = app_nvm_write("dyn_app_config", &config, sizeof(config));
    if (ret == RT_EOK) 
    {
        LOG_I("配置保存成功");
    } else 
    {
        LOG_E("配置保存失败");
    }
}

读取应用配置(使用 app_nvm_read)

/**
 * 功能:从 NVM 读取并恢复应用配置
 */
void dyn_app_load_config(void) {
    // 1. 定义接收缓冲区
    dyn_app_config_t config;
    memset(&config, 0, sizeof(config));

    // 2. 读取 NVM(key 与写入时一致:"dyn_app_config")
    size_t read_len = app_nvm_read("dyn_app_config", &config, sizeof(config));
    
    // 3. 处理读取结果
    if (read_len == sizeof(config)) 
    {
        LOG_I("配置读取成功:亮度=%d, 音量=%d, 自动启动=%d",
              config.brightness, config.volume, config.auto_start);
        // 此处可添加配置生效逻辑
    } else 
    {
        LOG_W("未读取到配置,使用默认值");
        // 加载默认配置...
    }
}

关键说明

  • key 唯一性:动态应用需确保 key_name 唯一(如添加应用前缀,例:"weather_app_config"),避免与系统或其他应用的 key 冲突。

  • 数据类型:支持任意结构化数据(如自定义结构体、数组、字符串等),无需关心底层是FlashDB(开源 FlashDB )还是独立文件(文件系统 SiFli 格式)。

  • 使用场景

    • 应用配置(如界面风格、功能开关);

    • 用户数据(如登录状态、偏好设置);

    • 运行状态(如上次退出位置、临时缓存)。

通过 app_nvm_readapp_nvm_write,内置应用/动态应用可简单高效地实现专属 NVM 数据的持久化管理,无需依赖系统NVM。