NV配置开发指南¶
Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.
功能概述¶
JSON NV存储¶
在嵌入式设备及物联网模块中,非易失性(Non-Volatile, NV)参数的存储是系统的关键功能之一。UniRTOS提供了基于JSON格式的轻量级、高容错NV参数存储机制。相比传统二进制存储方式,它可以在一定程度上解决NV数据结构变动带来的兼容性问题,为设备提供一个安全、灵活、易于维护的“配置数据库”。本文主要描述如何通过相关API实现NV配置。
痛点¶
在过去的方案中,NV参数的存储通常是 将C语言中的配置结构体内存块直接以二进制形式写入文件系统,读取时再原样拷贝回内存中。
这种基于“内存偏移量”的硬编码方式存在严重的向下兼容性问题:
主要问题:一旦在后续版本中修改了结构体(如在中间插入新变量、删除旧变量或改变变量类型),整个结构体的内存分布就会发生改变。
严重后果:新固件在读取旧设备保存的配置文件时,数据无法与新结构体对齐,会导致参数读取失败、数据错位,甚至引发系统崩溃。
解决方法¶
为了解决上述问题,UniRTOS引入了JSON键值对(Key-Value)模型,实现了 数据存储与内存物理位置的彻底解耦。
标识解耦:为结构体中的每个变量赋予唯一的字符串标识(Key)。读取时“认名字不认位置”。无论结构体成员如何增删调整,只要Key匹配,就能精准还原数据。
映射绑定机制:开发者只需定义一张 参数节点表,将自定义的Key与需要保存的结构体变量的内存地址及其大小进行绑定。底层读写接口会自动根据此表,完成JSON节点与内存数据的双向序列化与反序列化。
例如,某应用需要将串口的波特率和回显模式(即串口收到什么就回复什么)存储到NV中。
按照需求确定好配置结构体:
typedef struct {
int baudrate; // 波特率
char echo_mode; // 回显模式
} uart_app_config_t;
uart_app_config_t g_uart_app_cfg = { 115200, 1 }; // 当前内存中的值
在JSON NV方案中,上述配置结构体可映射为如下JSON键值对:
// 声明参数映射表 (将变量绑定到 JSON Key 上)
qosa_nv_cfg_item_data_t uart_app_param_table[] = {
// JSON Key名称 变量内存地址 变量大小
{ "baudrate", &g_uart_app_cfg.baudrate, sizeof(g_uart_app_cfg.baudrate) },
{ "echo_mode", &g_uart_app_cfg.echo_mode, sizeof(g_uart_app_cfg.echo_mode) }
};
// 调用 API 写入 (设配置节点名为uart_app_cfg)
qosa_nv_item_json_write("application_cfg.json/config/uart_app_cfg",
uart_app_param_table,
2); // 数组元素个数为 2
执行完上述代码后,底层会生成对应的JSON文件内容:
备注
115200的十六进制为0x0001C200,在小端模式下,内存字节表现为00 C2 01 00,即十进制的[0, 194, 1, 0]。
注意事项¶
JSON NV机制的设计初衷是解决大量零散且易变动的NV配置项存储需求,因此 不适合存储空间占用较大的配置。
如上述示例所示,同一JSON NV文件中可包含多个配置节,如 example1_cfg、example2_cfg 和 uart_app_cfg 等。实际使用时,通常会将不同应用的配置节放在同一个JSON NV文件中,以便集中管理多种应用的NV存储需求。
此场景下需控制JSON文件的大小。如果存放的配置数据过多,导致JSON文件过大,读写该文件将消耗更多存储资源。
不推荐将结构体整体绑定到一个Key上,示例如下:
{"sub_config_all", &g_cfg.sub_cfg, sizeof(g_cfg.sub_cfg)}
以这种方式存储的JSON NV配置,整个结构体将变成一个超长数组。若后续 sub_cfg 中的结构体成员发生变动,该数组到结构体的映射会错乱,从而失去JSON映射的意义。
推荐将结构体拆解,参考如下示例,逐个映射到JSON对象中:
{"sub_param_A", &g_cfg.sub_cfg.param_A, sizeof(...) }
{"sub_param_B", &g_cfg.sub_cfg.param_B, sizeof(...) }
这种方式虽然会产生更多Key并增加配置表长度,但能确保结构体在后续版本中任意增删改时,依然保持向下兼容。
NV配置API¶
头文件¶
qosa_nvitem.h
函数概览¶
函数 |
说明 |
|---|---|
qosa_nv_item_json_write() |
写入JSON文件配置项 |
qosa_nv_item_cfg_read() |
读取指定JSON文件中保存的配置项,并将读取的值提取、还原后存入指定的内存地址中 |
qosa_nv_item_cfg_delete() |
删除JSON文件配置项 |
函数详解¶
qosa_nv_item_json_write¶
功能描述
写入JSON文件配置项。向指定的JSON文件中写入或更新配置项信息。系统会将传入的二进制数据转换为JSON格式的整型数组(IntArray)进行保存。若写入前检测到新数据与已有数据完全一致,则跳过写入操作,以保护底层Flash寿命。函数原型
qosa_nvm_error_e qosa_nv_item_json_write(char *name, qosa_nv_cfg_item_data_t *item_data, qosa_uint16_t item_count)
参数说明
参数名 |
输入/输出 |
类型 |
说明 |
|---|---|---|---|
name |
输入 |
char* |
目标节点路径 |
item_data |
输入 |
qosa_nv_cfg_item_data_t* |
配置项映射表数组的首地址,数组元素包含Key、数据指针及数据大小;详见 qosa_nv_cfg_item_data_t |
item_count |
输入 |
qosa_uint16_t |
item_data 数组中的元素个数 |
name 用于指定操作的 JSON NV配置文件 名称及 配置节 名称。JSON NV配置文件默认保存在文件系统的 /config 路径下,无需指定存储路径。请注意文件名不能与系统中的配置文件重名。系统当前使用的配置文件如下:
atcmd_cfg.json
components_cfg.json
system_cfg.json
name 字符串格式请遵循如下规则:JSON NV配置文件名 + 根节点 + 配置节点名。同一JSON NV配置文件下的多个配置节点均应位于同一根节点下。
返回值说明
QOSA_NVM_CFG_OK:函数执行成功
QOSA_NVM_CFG_PARAM_ERROR:参数输入错误
QOSA_NVM_CFG_PATH_ERROR:输入路径名不符合规范
QOSA_NVM_CFG_WRITE_FILE_ERROR:写入JSON文件失败
QOSA_NVM_CFG_UNKNOWN_ERROR:未知错误
备注
调用此函数前,SDK内部NV存储模块会自动检查目标JSON NV文件是否存在,若不存在,则自动创建。
内置数据一致性校验逻辑(防冗余写入机制),频繁写入相同配置不会导致Flash过度磨损。
数据的保存形式可转换为对应的字节级数组(IntArray),支持任意类型(包括结构体)的数据。
qosa_nv_item_cfg_read¶
功能描述
读取指定JSON文件中保存的配置项。根据传入的路径及键名(Key),在文件中查找对应节点,并将其值提取、还原后存入指定的内存地址中。函数原型
qosa_nvm_error_e qosa_nv_item_cfg_read(char *name, qosa_nv_cfg_item_data_t *item_data, qosa_uint16_t item_count)
参数说明
参数名 |
输入/输出 |
类型 |
说明 |
|---|---|---|---|
name |
输入 |
char* |
目标节点路径(格式同 name字符串格式) |
item_data |
输入/输出 |
qosa_nv_cfg_item_data_t* |
配置项映射表数组的首地址,数组元素包含Key、数据指针及数据大小;详见 qosa_nv_cfg_item_data_t |
item_count |
输入 |
qosa_uint16_t |
item_data 数组中的元素个数 |
返回值说明
QOSA_NVM_CFG_OK:函数执行成功
QOSA_NVM_CFG_PARAM_ERROR:参数输入错误
QOSA_NVM_CFG_NO_SEARCH_ERROR:JSON中对应项名不存在
QOSA_NVM_CFG_READ_FILE_ERROR:读取JSON文件失败
QOSA_NVM_CFG_MALLOC_ERROR:内存分配失败
备注
SDK在JSON文件中查找Key时,会区分字母的大小写。
调用者必须正确设置 item_data 中的 value_size。如果文件中存储的数据长度与期望的 value_size 不一致,该函数将拒绝读取并返回 QOSA_NVM_CFG_NO_SEARCH_ERROR。
如果某个Key读取失败,该函数将直接中断后续其他元素的读取并返回 QOSA_NVM_CFG_NO_SEARCH_ERROR。
qosa_nv_item_cfg_delete¶
功能描述
删除JSON文件配置项。从指定的JSON文件中删除指定的配置项(Item节点)信息。删除后,更新后的JSON树将被重新写回文件系统。函数原型
qosa_nvm_error_e qosa_nv_item_cfg_delete(char *name, qosa_nv_cfg_item_data_t *item_data, qosa_uint16_t item_count)
参数说明
参数名 |
输入/输出 |
类型 |
说明 |
|---|---|---|---|
name |
输入 |
char* |
目标节点路径(格式同 name字符串格式) |
item_data |
输入 |
qosa_nv_cfg_item_data_t* |
配置项映射表数组的首地址,数组元素包含Key、数据指针及数据大小;详见 qosa_nv_cfg_item_data_t |
item_count |
输入 |
qosa_uint16_t |
item_data 数组中的元素个数 |
返回值说明
QOSA_NVM_CFG_OK:函数执行成功
QOSA_NVM_CFG_PARAM_ERROR:参数输入错误
QOSA_NVM_CFG_NO_SEARCH_ERROR:JSON中对应项名不存在
QOSA_NVM_CFG_WRITE_FILE_ERROR:写入JSON文件失败
QOSA_NVM_CFG_UNKNOWN_ERROR:未知错误备注
该函数会将解析出的目标节点及其所有子数据整体删除。
在删除操作中,item_data 不参与实际的数据处理,仅用于SDK内部的参数合法性校验。
数据结构定义¶
qosa_nv_cfg_item_data_t¶
配置项映射表数组结构体定义如下:
typedef struct
{
char *key;
void *value;
qosa_uint16_t value_size;
} qosa_nv_cfg_item_data_t;
参数 |
类型 |
说明 |
|---|---|---|
key |
char* |
JSON键名,通常对应配置结构体的成员名称 |
value |
void* |
结构体数据指针 |
value_size |
qosa_uint16_t |
结构体数据大小 |
qosa_nv_cfg_item_data_t 用于 qosa_nv_item_json_write() 和 qosa_nv_item_cfg_read() 传输配置数据。
在构建配置结构体时, 可使用宏 NV_ITEM_CFG_DEF 填充 qosa_nv_cfg_item_data_t 数组, 示例代码如下:
qosa_nv_cfg_item_data_t uart_app_param_table[] = {
// JSON Key名称 变量内存地址 变量大小
{ "baudrate", &g_uart_app_cfg.baudrate, sizeof(g_uart_app_cfg.baudrate) },
{ "echo_mode", &g_uart_app_cfg.echo_mode, sizeof(g_uart_app_cfg.echo_mode) }
};
//⬇️等同于如下写法⬇️
qosa_nv_cfg_item_data_t uart_app_param_table[] = {
NV_ITEM_CFG_DEF("baudrate", g_uart_app_cfg.baudrate),
NV_ITEM_CFG_DEF("echo_mode", g_uart_app_cfg.echo_mode)
};
枚举定义¶
qosa_nvm_error_e¶
结果码枚举定义如下:
typedef enum
{
QOSA_NVM_CFG_OK = QOSA_OK,
QOSA_NVM_CFG_MALLOC_ERROR = 1 | QOSA_ERRCODE_NVM_CFG_BASE,
QOSA_NVM_CFG_PARAM_ERROR,
QOSA_NVM_CFG_PATH_ERROR,
QOSA_NVM_CFG_READ_FILE_ERROR,
QOSA_NVM_CFG_NO_SEARCH_ERROR,
QOSA_NVM_CFG_WRITE_FILE_ERROR,
QOSA_NVM_CFG_UNKNOWN_ERROR,
QOSA_NVM_CFG_ERROR_MAX
} qosa_nvm_error_e;
成员 |
说明 |
|---|---|
QOSA_NVM_CFG_OK |
函数执行成功 |
QOSA_NVM_CFG_MALLOC_ERROR |
内存分配失败 |
QOSA_NVM_CFG_PARAM_ERROR |
参数输入错误 |
QOSA_NVM_CFG_PATH_ERROR |
输入路径名不符合规范 |
QOSA_NVM_CFG_READ_FILE_ERROR |
读取JSON文件失败 |
QOSA_NVM_CFG_NO_SEARCH_ERROR |
JSON中对应项名不存在 |
QOSA_NVM_CFG_WRITE_FILE_ERROR |
写入JSON文件失败 |
QOSA_NVM_CFG_UNKNOWN_ERROR |
未知错误 |
应用逻辑流程图¶
示例代码¶
完整示例代码请查看 https://github.com/UniRTOS/UniRTOS-Doc-Examples/blob/main/system/nv/nv_demo.c 。