这是最新(主)开发分支的文档。如果您正在查找以前版本的文档,使用左侧的下拉菜单选择所需的版本。

NVS

概述

非易失性存储(NVS) 是一种使用flash来存储key-value键值对数据的功能模块,本文将介绍NVS的一些基本概念。

基本原理

NVS 库通过调用 wm_nvs API 使用主 flash 的部分空间,具体使用flash分区表中名称为”nvs”的分区来存储数据。

备注

NVS 最适合存储一些较小的数据,大数据的存储可以考虑文件系统方式。

键值对

NVS 的操作对象为键值对,其中键是 ASCII 字符串,当前支持的最大长度为 15 个字符。存储值可以是以下几种类型:

  • 整数型: uint8_tint8_tuint16_tint16_tuint32_tint32_tuint64_tint64_t

  • 字符串:以 0 结尾的字符串

  • 二进制数据:可变长度的二进制数据 (BLOB)

  • 浮点数: double 类型的浮点数

警告

存储字符串类型的数据时,会把末尾的 \0 字符也存储,即实际存储的长度为strlen函数获取到的长度加1; 在使用接口获取字符串时, 给的buffer长度需要比实际字符个数多1,否则会返回无效参数。

备注

当前flash每个sector大小为4096字节,NVS的存储不能跨sector存储,默认每个sector块头部20字节用于标记sector的使用情况,每个数据item有24字节的头部, 所以无论存储的是字符串或者二进制数据,其大小不能超过4052字节。

备注

虽然NVS提供了整数、浮点数的读写,但项目中把整数、浮点数转字符串去存储也是可以的。。

键值必须唯一,为现有的键写入新值时,会将旧的值及数据类型和值替换。

读取值时会执行数据类型检查。如果读取操作预期的数据类型与对应键的数据类型不匹配,则返回错误。

分组

NVS不支持分组功能,如果用户需要把键值分组,建议可以把一组键值加一个分组前缀,如wifi.ssid。迭代器的遍历接口支持按前缀遍历, 指定前缀遍历就可以获取到该前缀的全部键值对。

NVS迭代器

迭代器允许用于遍历NVS中的数据,可以指定获取某个键值,或者指定前缀获取一组键值,不指定时,遍历所有键值。

使用以下函数,可执行相关操作:

  • wm_nvs_entry_find:创建一个不透明迭代器句柄,并让迭代器指向第一条记录,用于后续调用 wm_nvs_entry_next , wm_nvs_entry_infowm_nvs_entry_data 函数;

  • wm_nvs_entry_next:让迭代器指向下一个键值对;

  • wm_nvs_entry_info:返回当前键值对的信息。

  • wm_nvs_entry_data:返回当前键值对的数据。

  • wm_nvs_release_iterator:释放迭代器句柄。

总的来说,所有通过 wm_nvs_entry_find() 获得的迭代器(包括 NULL 迭代器)都必须使用 wm_nvs_release_iterator() 释放。

实现功能

为了用户使用的多样性,我们能实现多种功能:

  • NVS 初始化:

    wm_nvs_init

  • 字符串写入与读取:

    wm_nvs_set_str, wm_nvs_get_str

  • 二进制数据写入与读取:

    wm_nvs_set_blob, wm_nvs_get_blob

  • 8位、16位、32位、64位整数的写入与读取:

    wm_nvs_set_i8, wm_nvs_get_i8

    wm_nvs_set_i16, wm_nvs_get_i16

    wm_nvs_set_i32, wm_nvs_get_i32

    wm_nvs_set_i64, wm_nvs_get_i64

    wm_nvs_set_u8, wm_nvs_get_u8

    wm_nvs_set_u16, wm_nvs_get_u16

    wm_nvs_set_u32, wm_nvs_get_u32

    wm_nvs_set_u64, wm_nvs_get_u64

  • 浮点数的读写:

    wm_nvs_set_floatwm_nvs_get_float

  • 遍历 NVS 数据项并打印:

    wm_nvs_print

  • 获取数据项的类型与大小:

    wm_nvs_get_info

  • NVS 数据项删除:

    wm_nvs_del

  • NVS 重置:

    wm_nvs_reset

  • NVS遍历接口

    wm_nvs_entry_find : 创建迭代器,让迭代器指向第一条记录

    wm_nvs_entry_next : 移动到下一条记录

    wm_nvs_entry_info : 根据迭代器获取记录的键值,类型,数据长度

    wm_nvs_entry_data : 根据迭代器获取记录的数据

    wm_nvs_release_iterator : 释放迭代器

  • 支持 磨损平衡

  • 支持 掉电保护

  • 支持 Hash快速读写

备注

wm_nvs_reset 执行后,将格式化nvs的分区,所有存储数据将清空,请谨慎使用。

配置方法

在项目中要调整 NVS 大小是,可以调整分区表中名称为 nvs 的分区,起始地址和大小都可以调整。具体配置如下:

# name,         offset,        size,       flag
nvs,            0x1F0000,      0x8000,     0x0

详细配置请参考 分区表 章节。

备注

在数据写满时需要对已经删除数据的回收,为预防回收过程断电丢失数据,需要一个专门的sector做回收使用,所以实际可用空间会少4K,size大小至少要配置2个sector, 即至少0x2000

模块配置

  • HASH表配置

    Hash表是加快读写而创建的,初始化时会扫描NVS存储分区,在内存中建立一个地址偏移表,在读写时,把键值通过Hash计算转换成 索引下标,直接从表中得到存储该键值的偏移地址,这样可以快速的读取到值。初始Hash表入口数为53个,后面根据写入数据条目数 同台扩展,当存储的数据条数超过75%时,会动态扩充Hash表的长度,每次增加大概50个入口。 每个入口占用3个字节,关闭Hash表能节省的内存空间大小大概为存储数量 * 4,但代价是每次读写要遍历各个sector。

    CONFIG_NVS_HASH_ENABLED可以配置是否使用HASH表,默认是开启的,Hash表能大大提升读写速度,不建议关闭。

  • 键值长度配置

    键值长度使用CONFIG_NVS_ITEM_NAME_MAX_SIZE配置,默认配置16,由于要存储结尾的0,实际字符串最长支持15个字符,项目需要更长的键值名称 时可以增大该配置。

备注

修改该键值长度配置后,历史存储数据将丢失。

应用实例

详细请参考sdk中/examples/storage/nvs/下的工程。