前言¶
概述
本文档主要针对BS2X芯片中NV存储模块的使用进行介绍。用于指导工程人员能够快速使用NV模块进行二次开发。
产品版本
产品名称 |
产品版本 |
|---|---|
BS2X |
V100 |
读者对象
本文档主要适用于以下工程师:
技术支持工程师
软件工程师
符号约定
在本文中可能出现下列标志,它们所代表的含义如下。
符号 |
说明 |
|---|---|
|
表示如不避免则将会导致死亡或严重伤害的具有高等级风险的危害。 |
|
表示如不避免则可能导致死亡或严重伤害的具有中等级风险的危害。 |
|
表示如不避免则可能导致轻微或中度伤害的具有低等级风险的危害。 |
|
用于传递设备或环境安全警示信息。如不避免则可能会导致设备损坏、数据丢失、设备性能降低或其它不可预知的结果。 “须知”不涉及人身伤害。 |
|
对正文中重点信息的补充说明。 “说明”不是安全警示信息,不涉及人身、设备及环境伤害信息。 |
修改记录
文档版本 |
发布日期 |
修改说明 |
|---|---|---|
03 |
2025-12-01 |
|
02 |
2025-03-26 |
更新“新增NV项”章节内容。 |
01 |
2024-05-15 |
第一次正式版本发布。 |
00B02 |
2024-02-29 |
|
00B01 |
2024-02-05 |
第一次临时版本发布。 |
NV简介¶
NV模块用于在本地存储器中存储非易失性数据。NV中的每项数据以类似key-value的方式进行定义,数据项中包含唯一的索引key和自定义数据类型的value。
NV项可通过两种方式进行存储:编译预置和API写入。
编译预置是指开发者可在代码编译阶段,通过修改NV头文件和NV配置文件的方式生成客制化的NV镜像,在镜像烧录的过程中统一烧录到存储介质中。预置的NV在代码运行阶段可通过API接口进行读取和更新。具体使用方法请参见“NV编译预置”章节。
API写入是指用户可直接在代码中调用API接口写入新的NV项,具体使用方法请参见“NV API指南”章节。
以Flash为例,NV项存储在Flash中时,以Flash器件的sector为单位进行管理,NV的每个页为一个sector大小(4096Byte),NV页的数量默认配置为2页。除去管理结构,单个非加密NV项的有效数据最大不应超过4060Byte。
NV编译预置¶
新增NV项¶
新增NV项流程¶
在头文件中新增kvalue的数据类型定义(非必须,如果是通用类型数据可忽略此步骤)。
在json文件中新增NV描述项。
新增kvalue数据类型
通用数据类型:
unit8_t、unit16_t、unit32_t、bool。
自定义数据类型:
支持自定义枚举(enum)类型和结构体(struct)类型。
自定义数据类型存放路径:
以bs21为例:middleware\chips\bs21\nv\nv_config\bs21_nv_default\include\common.h,其中bs21_nv_default和“build\config\target_config\bs21\config.py"中的'nv_cfg': 'bs21_nv_default'对应,模式配置为bs21_nv_default。
当用户使用通用或已定义的数据类型时,不涉及本小节的修改;当用户要新增枚举或结构体类型时,需在上述文件中定义。
新增NV描述项
NV描述项文件路径:以bs21为例,middleware\chips\bs21\nv\nv_config\bs21_nv_default\cfg\acore\app.json,其中bs21_nv_default和“build\config\target_config\bs21\config.py"中的'nv_cfg': 'bs21_nv_default'对应,模式配置为bs21_nv_default。
定义说明如表1所示。
表 1 NV配置选项说明
NV配置选项
说明
key_id
NV项的ID。
key_status
NV项的状态。
structure_type
NV项的数据结构类型。
attributions
NV项的属性值。
value
NV项的数据。
NV配置文件示例:
{ "common":{ "module_id": "0x0", "host_config": { "key_id": "0x1", "key_status": "alive", "structure_type": "uint8_t", "attributions": 1, "value": 15 }, "sample1": { "key_id": "0x2", "key_status": "alive", "structure_type": "sample_type_t", "attributions": 1, "value": [1,2,3,4,5] } } }
在NV的配置中,每个字段的详细描述如下:
key_id:
以十六进制形式给出的NV项ID。key_id必须唯一,不能重复,因此建议用户对key_id的取值进行管理,如:key_id由16bit组成,可规定高8bit为所在模块内的module_id,低8bit在模块内取值,避免不同模块使用NV互相影响。
key_status:
用于标记是否将该项的NV值编到生成的bin文件中。该字段为“alive”,表示NV项生效,当前固件版本正在使用此key;若为其他的字段或空,则不生效。
structure_type:
NV项的数据类型。具体说明请参见“新增kvalue数据类型”中详细描述。
attributions:NV项属性值。
1 、2、4为互斥关系,三选一。
1:Normal NV(普通NV)。
2:Permanent NV(不可修改)。
4:Un-upgrade NV(不随版本升级而修改,未使用到)。
value:如果value不是上述通用数据类型,任何结构都必须以列表的形式书写。
value赋值有如下两种情况:
列表所有成员全部赋值。
只对列表前面若干个成员赋值。(末尾未赋值的成员默认赋值为0)。
注:赋值只支持十进制格式。
新节点1¶
新增NV项示例¶
以BS21为例,在middleware\chips\bs21\nv\nv_config\bs21_nv_default\include\common.h文件中,新增自定义结构体。新增自定义数据类型示例如下。
新增结构体类型且结构体内都为基础类型:
typedef struct { int8_t param1; int8_t param2; int8_t param3; int8_t param4; int8_t param5; uint32_t param6; uint32_t param7; int32_t param8; uint32_t param9; uint32_t param10; uint32_t param11; uint32_t param12; uint32_t param13; uint32_t param14; uint32_t param15; uint32_t param16; uint32_t param17; } sample_type_t;
新增结构体类型且结构体内有数组类型:
typedef struct { uint16_t param1; uint16_t param2; uint16_t param3; uint16_t param4[2]; } sample_two;
新增枚举类型:
typedef enum { PARAM1, PARAM2, PARAM3, PARAM4 } sample_three;
以bs21为例,在middleware\chips\bs21\nv\nv_config\bs21_nv_default\cfg\acore\app.json文件中,添加新的NV项。
当kvalue预置值类型是基础类型时,添加kvalue预置值如图1所示,可在app.json配置文件直接添加,无需在头文件中新增。
图 1 添加kvalue预置值为基础类型
"sample1": { "key_id": "0x1", "key_status": "alive", "structure_type": "uint8_t", "attributions": 1, "value": 0 }
当kvalue预置值类型是结构体类型时,添加kvalue预置值如图2所示,该kvalue值要对应头文件中已有的结构体,如果没有需手动添加自定义结构体。
图 2 添加kvalue预置值为结构体类型
"sample2": { "key_id": "0x2", "key_status": "alive", "structure_type": "sample_type_t", "attributions": 1, "value": [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17] }
NV位置调整¶
功能描述¶
NV位于Flash上的存储区域,其位置及大小支持自定义调整,相关配置文件如下(以BS21为例):
位置及大小相关宏配置文件:
middleware\chips\bs21\nv\include\nv_config.h
bin生成相关配置文件:
middleware\chips\bs21\nv\nv_config\nv_target.json
打包相关文件:
tools\pkg\chip_packet\bs21\packet.py
配置修改¶
修改nv_config.h宏定义:
#define KV_STORE_DATA_SIZE NV_LENGTH #define KV_STORE_START_ADDR (NV_STATR_ADDR) #define KV_STORE_PAGES_ACPU 1
KV_STORE_DATA_SIZE修改成目标NV的大小,SIZE大小为扇区(0x1000)的整数倍,最小SIZE为2×FLASH_PAGE_SIZE。
KV_STORE_START_ADDR修改为目标位置映射的内存空间。
KV_STORE_PAGES_ACPU修改A核扇区的数量,目前BS2X NV操作在A核进行,只适配ACPU即可。
修改nv_target.json生成信息:
修改flash_size为KV_STORE_DATA_SIZE大小,同时修改page_nums,使其与 KV_STORE_PAGES_ACPU对应。
{ "size":{ "flash_size" : "0x2000", "page_size" : "0x1000" }, "cores" :{ "app" : { "page_nums" : 4, "page_id_start" : "0x254D" } }, "acore_test_nv":{ "TYPE" : "nv", "CHIP" : "bs21", "CORE" : "app", "KERNEL_BIN" : "acore", "COMPONENT" : ["app"] } }
修改packet.py打包脚本:
修改打包脚本中的NV bin打包地址。
nv = os.path.join(SDK_DIR, "interim_binary", "bs21", "bin", "bs21_all_nv.bin") nv_bx = nv + "|0x8c5fc000|0x4000|100"
NV bin代码打包格式为nv_bx = nv + "|地址|大小|下载类型",用户完成NV地址和大小的修改后,需要将代码中的地址、大小和nv_config.h里面的地址大小对应,下载类型保持不变。
编译生成NV镜像¶
目前SDK NV bin会随APP的编译一起更新,修改完NV的参数以及地址信息后,重新编译APP,NV bin会自动更新到(以bs21为例)interim_binary\bs21\bin\bs21_all_nv.bin路径下,并且打包到tools\pkg\fwpkg\bs21\bs21_all.fwpkg路径下,用户使用Burntool下载即可。
NV API指南¶
功能描述¶
NV API主要提供以下几种功能:
NV项写入:
保存需要存储的格式化数据,同时可以定义写入数据的三种属性,分别为是否永久存储、是否加密存储和是否不可升级。
NV项读取:
从本地存储器读取NV数据。
NV信息查询:
查询NV是否已存储于本地存储器中。
查询NV空间的使用状态。
NV项的写入接口可设置NV项的属性,对于通过API动态添加的NV项,可在其写入的接口中传入其所拥有的特殊属性。
接口说明¶
NV模块主要提供以下API:
uapi_nv_init |
初始化NV模块,包括nv区域map,必须在使用nv函数之前调用。 |
|---|---|
NULL |
NULL |
uapi_nv_write |
写入NV数据项,默认属性Normal,无回调函数。 |
|---|---|
key |
要写入的NV项的key ID,用于索引。 |
*kvalue |
指向要写入的NV项的值的指针。 |
kvalue_length |
写入数据的长度,单位:Byte。 |
uapi_nv_write_with_attr |
写入NV数据项,并根据业务需求配置属性及回调函数。 |
|---|---|
key |
要写入的NV项的key ID,用于索引。 |
*kvalue |
指向要写入的NV项的值的指针。 |
kvalue_length |
写入数据的长度,单位:Byte。 |
*attr |
要配置的NV项的属性。 |
func |
kvalue写入Flash后调用的回调函数。 |
uapi_nv_read |
读取指定NV数据项的值,默认不获取key的属性值。 |
|---|---|
key |
要读取的NV项的key ID,用于索引。 |
kvalue_max_length |
允许存储数据的最大长度,单位:Byte。 |
*kvalue_length |
实际读取到的数据长度(读取以四字节对齐)。 |
*kvalue |
指向保存读取数据的buffer的指针。 |
uapi_nv_read_with_attr |
读取指定NV数据项的值,同时获取key的属性值 |
|---|---|
key |
要读取的NV项的key ID,用于索引。 |
kvalue_max_length |
允许存储数据的最大长度,单位:Byte。 |
*kvalue_length |
实际读取到的数据长度(读取以四字节对齐)。 |
*kvalue |
指向保存读取数据的buffer的指针。 |
*attr |
获取到的NV项的属性。 |
uapi_nv_get_store_status |
获取NV存储空间使用情况。 |
|---|---|
*status |
指向保存NV状态数据的指针。 |
开发指引¶
以下步骤为NV读写操作步骤指引:
写入默认Normal类型NV。
uint8_t *test_nv_value; /* 要写入的NV value保存在test_nv_value中 */ uint32_t test_len = 15; /* 长度为test_len ,例中为15*/ uint16_t key = TEST_KEY; /* TEST_KEY 为该Key的ID*/ uint16_t key_len= test_len; uint8_t *write_value = uapi_malloc(key_len); (void)memcpy_s(write_value, key_len, test_nv_value, key_len); errcode_t nv_ret_value = uapi_nv_write(key, write_value, key_len); if (nv_ret_value != ERRCODE_SUCC) { /* ERROR PROCESS */ uapi_free(wrt_value); return ERRCODE_FAIL; } /* APP PROCESS */ uapi_free(wrt_value); return ERRCODE_SUCC;
写入带属性NV(配置加密属性,其他略)。
uint8_t *test_nv_value; /* 要写入的NV value保存在test_nv_value中 */ uint32_t test_len = 15; /* 长度为test_len,例中为15 */ uint16_t key = TEST_KEY; uint16_t key_len= test_len; nv_key_attr_t attr = {0}; attr.permanent = false; attr.encrypted = true; /* 加密属性设为true */ attr.non_upgrade = false uint8_t *write_value= uapi_malloc(key_len); (void)memcpy_s(write_value, key_len, test_nv_value, key_len); errcode_t nv_ret_value = uapi_nv_write_with_attr(key, write_value, key_len, &attr, NULL); if (nv_ret_value != ERRCODE_SUCC) { /* ERROR PROCESS */ uapi_free(write_value); return ERRCODE_FAIL; } /* APP PROCESS */ uapi_free(write_value); return ERRCODE_SUCC;
读取NV。
uint16_t key = TEST_KEY; uint16_t key_len= test_len; uint16_t real_len= 0; uint8_t *read_value = uapi_malloc(key_len); if (uapi_nv_read(key, key_len, &real_len, read_value) != ERRCODE_SUCC) { /* ERROR PROCESS */ uapi_free(read_value); return ERRCODE_FAIL; } /* APP PROCESS */ uapi_free(read_value); return ERRCODE_SUCC;
读取NV及属性。
uint16_t key = TEST_KEY; uint16_t key_len = test_len; uint16_t real_len = 0; uint8_t *read_value = uapi_malloc(key_len); nv_key_attr_t attr = {false, false, false, 0}; ext_errno nv_ret = uapi_nv_read_with_attr(key, key_len, &real_len, read_value, &attr); if (nv_ret != ERRCODE_SUCC ) { /* ERROR PROCESS */ uapi_free(read_value); return ERRCODE_FAIL; } /* APP PROCESS */ uapi_free(read_value); return ERRCODE_SUCC;
查询NV空间状态。
nv_store_status status; if (uapi_nv_get_store_status(&status) == ERRCODE_SUCC) { /* APP PROCESS */ printf("Total: %d Bytes\r\n", status.total_space); printf("used: %d Bytes\r\n", status.used_space); printf("reclaimable:%d Bytes\r\n", status.reclaimable_space); printf("corrupted: %d Bytes\r\n", status.corrupted_space); printf("max_key: %d Bytes\r\n", status.max_key_space); }
说明: 开发指引只是API接口的测试用例,为用户提供简单的sample参考,sample中省略了宏、部分变量、回调函数的定义过程和业务处理过程。
注意事项¶
uapi_nv_write:默认不对所存储的key添加额外属性(是否永久存储、是否加密存储等)。
uapi_nv_write_with_attr:可同时配置key属性和注册回调函数。目前NV代码中没有使用到回调函数,传NULL忽略即可。
NV属性结构体和NV空间状态结构体说明详见nv.h文件。
NV write和read接口使用到信号量同步获取,禁止在中断回调中使用。
定制化NV结构使用介绍¶
bth_ble_nv_reserved_struct_t
表 1 bth_ble_nv_reserved_struct_t结构体基本信息
key_id |
0xE |
|---|---|
结构体名称 |
bth_ble_nv_reserved_struct_t |
长度 |
128字节(每个0是一个字节,默认值为十进制数) |
表 2 字节使用记录
字节偏移 |
字节描述 |
功能描述 |
|---|---|---|
0 |
customize_flag |
定制化使能flag,每个bit对应一个定制化开关。 |
1 |
customize_flag |
|
2 |
gfsk power |
GFSK(BLE和GLE帧1)调制类型的功率定制化。 |
3 |
psk power |
GLE PSK调制类型的功率定制化。 |
4 |
em_customized_data_tx_size低8位 |
星闪低时延传输时业务需要的最大发送包长。 |
5 |
em_customized_data_tx_size高8位 |
星闪低时延传输时业务需要的最大发送包长。 |
6 |
max_nb_active_link |
星闪低时延传输时业务需要的最大链接数量。 |
7 |
fem switch |
RT201 fem管脚适配开关。 |
8 |
ctrim_flag |
XO ctrim电容值写过flash标志位。 |
9 |
ctrim_value |
XO ctrim电容值。 |
10 |
em_customized_data_rx_size低8位 |
rx em buffer大小的低8位。 |
11 |
em_customized_data_rx_size高8位 |
rx em buffer大小的高8位。 |
12 |
em_customized_acl_txbuff_nb |
定制化ACL EM DATA TXBUFF个数。 |
13 |
em_customized_acl_data_size |
定制化ACL EM DATA大小。 |
14 |
g_max_temp |
记录芯片最高温度和最低温度。芯片最高温度初始设置为-40,最低温度初始设置为125。在nv中保存使用的数据类型和实际使用中不同,-40经过转换为216。因此在nv中最高温度初始值实际配置为216。 |
15 |
g_min_temp |
通过NV自定义传输数据最大长度¶
NV根据字节使用记录表可以设置需要发送的数据长度,修改如图1所示。
图 1 NV自定义传输数据长度60字节示例

需要注意的是
对于low latency模式,不同回报率下支持发送的最大数据长度的能力不同,8K回报率最多支持用户发送5Byte,4K最多支持用户发送16Byte,2K最多支持用户发送36Byte,1K最多支持用户发送250Byte。
对于非low latency模式,最多支持用户发送255Byte。
em_customized_data_tx_size和em_customized_data_rx_size根据需要设置为传输数据的最大长度。
max_nb_active_link需设置为非0,最大值为8。




