NVS
概述
非易失性存储(NVS) 是一种使用flash来存储key-value键值对数据的功能模块,本文将介绍NVS的一些基本概念。
基本原理
NVS 库通过调用 wm_nvs API 使用主 flash 的部分空间,具体使用flash分区表中名称为”nvs”的分区来存储数据。
备注
NVS 最适合存储一些较小的数据,大数据的存储可以考虑文件系统方式。
键值对
NVS 的操作对象为键值对,其中键是 ASCII
字符串,当前支持的最大长度为 15 个字符。存储值可以是以下几种类型:
整数型:
uint8_t
、int8_t
、uint16_t
、int16_t
、uint32_t
、int32_t
、uint64_t
和int64_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_info
和wm_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_float
,wm_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/下的工程。