HiSpark编程指南

- 调测资料
  - SDK Sample使用指南
    - 前言
    - 概述
    - Sample框架
    - 外设 Sample
    - SLE UART Sample
    - SLE UART DMA Sample
    - SLE Mouse Sample
    - SLE amic vdt with dongle
    - SLE microphone
    - SLE measure dis
    - BLE Lowpower Sample
    - BLE UART Sample
    - BLE Keyboard Sample
    - BLE SLE TAG Sample
    - BLE Mouse Sample
    - USB Keyboard Sample
    - USB Mouse Sample
    - USB AMIC VDT Sample
    - RCU
    - 多连接
    - air mouse
    - SLE MOUSE with Dongle
    - SLE OTA dongle
    - game mouse
    - BLE UUID
    - 缩略语
  - SDK二次开发网络安全注意事项
    - 前言
    - 产品安全解决方案
    - 结论
  - SDK开发环境搭建用户指南
    - 前言
    - 开发环境搭建
    - 编译SDK
    - 新建APP
  - 内存维测指导书
    - 前言
    - 简介
    - 内存维测功能
  - 射频AT测试指南
    - 前言
    - 射频测试AT指令
    - AT指令组包示例
    - 注意事项
  - 维测指导书
    - 前言
    - 概述
    - 日志说明
    - 日志分析
    - 问题定位
    - 典型案例
    - 常见问题FAQ
- 软件资料
  - AT命令使用指南
    - 前言
    - 指令说明
    - 通用AT指令
    - BT业务AT指令
    - 新增AT指令方法
  - DFU升级APK开发指南
    - 前言
    - 功能特性
    - 系统框架
    - 工程文件部署
    - 界面实现
    - 升级界面Fragment实现
    - 关于界面Fragment实现
    - 应用清单AndroidManifest
  - DFX软件开发指南
    - 前言
    - DIAG维测功能
    - 死机诊断
    - Dump解析
  - EFUSE使用指南
    - 前言
    - 概述
    - 使用软件接口读写EFUSE
    - 使用BurnTool烧写EFUSE
  - NFC TAG软件开发指导书
    - 前言
    - 概述
    - 开发指导
  - NV存储用户指南
    - 前言
    - NV简介
    - NV编译预置
    - NV API指南
    - 定制化NV结构使用介绍
    - 通过NV自定义传输数据最大长度
  - Release notes
    - 1.0.18版本描述文件
    - 1.0.17版本描述文件
    - 1.0.16版本描述文件
  - SDK开发指南
    - 前言
    - 概述
    - 系统接口
  - SDK接口变更说明书
    - 前言
    - BLE SDK接口变更说明
  - SP31 SLP使用指南
    - 开源版本
  - USB HID复合设备开发指南
    - 前言
    - 概述
    - 实现方式
  - 代码框架说明
    - 前言
    - SDK模块框架
    - SDK目录结构
    - SDK目录说明
  - 低功耗FAQ
    - 前言
    - 系统无法睡眠
    - IO无法唤醒系统
    - 唤醒后外设异常
    - 低功耗导致OTA升级异常
    - 功耗比预期高
    - 锁中断导致BT业务阻塞
    - 概率性狗超时
    - 供电模式&软件配置
    - 提前唤醒时间问题
  - 低功耗sample使用指南
    - 前言
    - 编译&测试
    - 系统状态管理
    - 用户指南
  - 低功耗开发指南
    - 前言
    - 低功耗概述
    - 低功耗方案
    - 开发指引
    - 功耗优化
    - 常见问题
  - 功率配置说明书
    - 前言
    - 射频功率配置说明
  - 升级方案使用指南
    - 前言
    - 概述
    - 升级包制作
    - 升级包传输
    - 升级包保存
    - 升级包本地升级
    - FAQ
  - 快速入门指南
    - 前言
    - 文档目录
    - 环境搭建
    - 基础功能开发
    - 调优
    - 特性开发
    - 测试报告
  - 星闪应用层SLE-Link协议
    - 前言
    - 概述
    - SLE Frame格式定义
    - 服务接口定义
    - 交互流程示例
  - 星闪软件开发指导书
    - 前言
    - 概述
    - Device Discovery接口
    - Connection Manager接口
    - SSAP Server接口
    - SSAP client接口
    - Low Latency接口
    - Transmission Manager接口
    - SLE Passkey接口
    - SLE Channel Map 接口
  - 红外学习使用指南
    - 前言
    - 红外学习方式
    - 通过比较器进行红外学习
    - 使用指南
  - 蓝牙软件开发指导书
    - 前言
    - 概述
    - GAP接口
    - GATT server接口
    - GATT client接口
  - 设备驱动开发指南
    - 前言
    - Pinctrl
    - GPIO
    - UART
    - SPI
    - I2C
    - ADC
    - DMA
    - PWM
    - WDT
    - Timer
    - Systick
    - TCXO
    - Flash
    - SFC
    - QDEC
    - KEYSCAN
    - PDM
    - USB_HID
    - USB_DFU
    - I2S
    - TRNG
    - 附：MIDDLEWARE
  - 音频编解码开发指导书
    - 前言
    - 概述
    - Audio Encoder
    - Audio Decoder
    - 调试与功能
- 工具资料
  - BurnTool工具使用指导书
    - 前言
    - BurnTool工具简介
    - 操作指南
    - FAQ
  - HiSpark Studio使用指南
    - 前言
    - 工具简介
    - 开发环境搭建
    - 工程管理
    - 工程配置
    - 编译运行
    - 软件包烧录
    - 栈分析和镜像分析
    - 工程调试
    - 串口控制台工具
    - Kconfig配置
    - GUI工程创建与使用
    - 高阶分析功能使用
    - 常见错误

### 该页内容

- 前言
- NV简介
- NV编译预置
  - 新增NV项
    - 新增NV项流程
    - 新节点1
    - 新增NV项示例
  - NV位置调整
    - 功能描述
    - 配置修改
  - 编译生成NV镜像
- NV API指南
  - 功能描述
  - 接口说明
  - 开发指引
  - 注意事项
- 定制化NV结构使用介绍
- 通过NV自定义传输数据最大长度

# 前言

概述

本文档主要针对BS2X芯片中NV存储模块的使用进行介绍。用于指导工程人员能够快速使用NV模块进行二次开发。

产品版本

产品名称 | 产品版本
--- | ---
BS2X | V100

读者对象

本文档主要适用于以下工程师：

- 技术支持工程师

- 软件工程师

符号约定

在本文中可能出现下列标志，它们所代表的含义如下。

符号 | 说明
--- | ---
| 表示如不避免则将会导致死亡或严重伤害的具有高等级风险的危害。
| 表示如不避免则可能导致死亡或严重伤害的具有中等级风险的危害。
| 表示如不避免则可能导致轻微或中度伤害的具有低等级风险的危害。
| 用于传递设备或环境安全警示信息。如不避免则可能会导致设备损坏、数据丢失、设备性能降低或其它不可预知的结果。 “须知”不涉及人身伤害。
| 对正文中重点信息的补充说明。 “说明”不是安全警示信息，不涉及人身、设备及环境伤害信息。

修改记录

文档版本 | 发布日期 | 修改说明
--- | --- | ---
03 | 2025-12-01 | 新增“定制化NV结构使用介绍”章节内容。 新增“通过NV自定义传输数据最大长度”章节内容。
02 | 2025-03-26 | 更新“新增NV项”章节内容。
01 | 2024-05-15 | 第一次正式版本发布。
00B02 | 2024-02-29 | 更新“新增NV项”章节内容。 更新“编译生成NV镜像”章节内容。
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项流程

1. 在头文件中新增kvalue的数据类型定义（非必须，如果是通用类型数据可忽略此步骤）。

2. 在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赋值有如下两种情况：

    1. 列表所有成员全部赋值。

    2. 只对列表前面若干个成员赋值。（末尾未赋值的成员默认赋值为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

### 配置修改

1. 修改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即可。

2. 修改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"]
           }
       }

3. 修改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读写操作步骤指引：

1. 写入默认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;

2. 写入带属性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;

3. 读取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;

4. 读取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;

5. 查询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。