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文件内容:

../../_images/image_HCKCb4Gv8oZYrYxSLTWcyo8onJd.webp

备注

115200的十六进制为0x0001C200,在小端模式下,内存字节表现为00 C2 01 00,即十进制的[0, 194, 1, 0]。

注意事项

JSON NV机制的设计初衷是解决大量零散且易变动的NV配置项存储需求,因此 不适合存储空间占用较大的配置

如上述示例所示,同一JSON NV文件中可包含多个配置节,如 example1_cfgexample2_cfguart_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配置文件下的多个配置节点均应位于同一根节点下。

../../_images/image_HPSybRxNwowM28xo5avccd3ZnAb.webp
  • 返回值说明
    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:未知错误

备注

  1. 调用此函数前,SDK内部NV存储模块会自动检查目标JSON NV文件是否存在,若不存在,则自动创建。

  2. 内置数据一致性校验逻辑(防冗余写入机制),频繁写入相同配置不会导致Flash过度磨损。

  3. 数据的保存形式可转换为对应的字节级数组(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
注:输入时指定要查询的Key及预期数据大小;输出时用于接收读取到的数据

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:内存分配失败

备注

  1. SDK在JSON文件中查找Key时,会区分字母的大小写。

  2. 调用者必须正确设置 item_data 中的 value_size。如果文件中存储的数据长度与期望的 value_size 不一致,该函数将拒绝读取并返回 QOSA_NVM_CFG_NO_SEARCH_ERROR

  3. 如果某个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:未知错误

    备注

    1. 该函数会将解析出的目标节点及其所有子数据整体删除。

    2. 在删除操作中,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

未知错误

应用逻辑流程图

image

示例代码

完整示例代码请查看 https://github.com/UniRTOS/UniRTOS-Doc-Examples/blob/main/system/nv/nv_demo.c