一、背景

SNV(Simple Non-Volatile,简单的非易挥发),是从内部 flash 末尾划分大约 1~4K大小的一块专用于存储数据的存储空间。一般用于存储协议栈必要的存储数据、用户的应用数据。类似于 STM32 的片内 EEPROM。

NV(Non-Volatile,非易挥发) 是 16 位 ID,而 SNV 是简化版的 NV 只有 8 位 ID。

1.1 SNV大小

两页,共 4K(协议栈已使用一部分)。

1.2 可用SNV的ID号

在Bcomdef.h中

// Device NV Items - Range 0 - 0x1F
#define BLE_NVID_IRK 0x02              //!< The Device's IRK
#define BLE_NVID_CSRK 0x03             //!< The Device's CSRK
#define BLE_NVID_SIGNCOUNTER 0x04      //!< The Device's Sign Counter
#define BLE_LRU_BOND_LIST 0x05         //!< The Device's order of bond indexes in least recently used order

// Bonding NV Items - Range 0x20 - 0x5F - This allows for 10 bondings
#define BLE_NVID_GAP_BOND_START 0x20   //!< Start of the GAP Bond Manager's NV IDs
#define BLE_NVID_GAP_BOND_END 0x5f     //!< End of the GAP Bond Manager's NV IDs Range

// GATT Configuration NV Items - Range 0x70 - 0x79 - This must match the number of Bonding entries
#define BLE_NVID_GATT_CFG_START 0x70   //!< Start of the GATT Configuration NV IDs
#define BLE_NVID_GATT_CFG_END 0x79     //!< End of the GATT Configuration NV IDs

// Customer NV Items - Range 0x80 - 0x8F - This must match the number of Bonding entries
#define BLE_NVID_CUST_START 0x80       //!< Start of the Customer's NV IDs
#define BLE_NVID_CUST_END 0x8F         //!< End of the Customer's NV IDs

可见 0x80~0x8F 是用户可以用(包含 0x80、0x8F),每个 ID 号最多一次可写入 252 字节。

注:实际可以写几个 ID 取决于所剩余 SNV 空间,并不是说可以把所有 ID 号都写满 252字节。

1.3 工程中可选的SNV大小

可在编译器的预编译处写入三种宏,不同的宏表示不同 SNV 大小:

① OSAL_SNV = 0

0 个可写 SNV 页。工程中不使用 SNV,由于绑定信息时需要 SNV,该操作将导致无法使用绑定功能。

② OSAL_SNV = 1

1 个可写 SNV 页,共 2K。

③ OSAL_SNV = 2(默认)

2 个可写 SNV 页,共 4K。

nvflash 只改型号 nvflash怎么用_SNV

注:如果没有在编译器中定义 OSAL_SNV,在协议栈代码中会自动被复制为 2,也就是默认用 4K的 SNV。

二、注意事项

1) 仿真时编译器会擦除 flash 全片数据,SNV 数据也会被擦除。
2) 写 1K 字节到 SNV,怎么写?
可以每个 ID 写 252 字节,一共需要 4 个 ID(1000/252=3.96)。
3) 为什么有些工程使用不了 SNV(比如组网的例程)?
因为组网例程所需 flash 较大,因此该工程的 SNV 部分被充分利用在了组网部分。
可以看工程的预编译中包含了“OSAL_SNV=0”。
4) 写 SNV 会耗时百毫秒级,尽可能在写的时候关闭中断。
5) 尽可能地少写 SNV,因为它耗时耗电。
6) 如果 SNV 的存储结构改变,或者协议栈版本升级了,有必要重新擦除和初始化 SNV 内存数据,否则读写时会出错。
7) 尽量不要把 SNV 的代码放到中断函数里,建议单独弄个事件处理 SNV。
8) 一次最多只能对一个 ID 写 252 个字节,写多时虽然返回值仍然是“SUCCESS”,但实测读出来的数据是错的。

三、移植文件

链接:https://pan.baidu.com/s/1fzlCQEthZHpK5qd3rr3JDA 提取码:3nbv

snv_flash.csnv_flash.h 两个文件拖拽至CCS工程的Application文件夹下

nvflash 只改型号 nvflash怎么用_nvflash 只改型号_02


添加文件过程中,选项选择如下

nvflash 只改型号 nvflash怎么用_SNV_03


nvflash 只改型号 nvflash怎么用_BLE_04

3.1 snv_flash.c

/*********************************************************************
 * INCLUDES
 */
#include "osal_snv.h"
#include "snv_flash.h"

/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/**
 @brief SNV读写内存操作
 @param snvId NV页数
 @param readWriteFlag 读写操作标志
 @param pData 指向需要操作的数据
 @param dataLen 数据长度
 @return SUCCESS - 成功;FAILURE - 失败
*/
uint8 Snv_FlashContrl(uint8 snvId, uint8 readWriteFlag, uint8 *pData, uint8 dataLen)
{
    if(readWriteFlag == SNV_READ)                       // 读取数据
    {
        return osal_snv_read(snvId, dataLen, pData);
    }
    else                                                // 写入数据
    {
        return osal_snv_write(snvId, dataLen, pData);
    }
}

/*************************************END OF FILE*************************************/

3.2 snv_flash.h

#ifndef _SNV_FLASH_H_
#define _SNV_FLASH_H_

/*********************************************************************
 * DEFINITIONS
 */
#define SNV_READ            0x00
#define SNV_WRITE           0x01

#define CUSTOM_SNV_ID       0x80    // 使用的ID,用户可用0x80~0x8F
#define CUSTOM_SNV_NUM      252     // 此ID使用到的字节数

/*********************************************************************
 * API FUNCTIONS
 */
uint8 Snv_FlashContrl(uint8 snvId, uint8 readWriteFlag, uint8 *pData, uint8 dataLen);

#endif /* _SNV_FLASH_H_ */

四、API调用

需包含头文件 snv_flash.h

Snv_FlashContrl

功能

SNV读写内存操作

函数定义

uint8 Snv_FlashContrl(uint8 snvId, uint8 readWriteFlag, uint8 *pData, uint8 dataLen)

参数1

snvId NV页数

参数2

readWriteFlag 读写操作标志

参数3

pData 指向需要操作的数据

参数4

dataLen 数据长度

返回

SUCCESS - 成功;FAILURE - 失败

五、使用例子

1)添加头文件(例 multi_role.c 中)

#include "snv_flash.h"

2)定义一个数据缓存区(例 multi_role.c 中)

// 数据缓冲区
static uint8 s_testData[256] = {0};

3)添加读取数据代码(multi_role.c 的 multi_role_init 函数末尾中)

// 读取保存在SNV中的数据
Snv_FlashContrl(CUSTOM_SNV_ID, SNV_READ, (uint8 *)s_testData, CUSTOM_SNV_NUM);

4)添加写入数据代码(在某个事件回调函数中)

// SNV写入内存
Snv_FlashContrl(CUSTOM_SNV_ID, SNV_WRITE, (uint8 *)s_testData, CUSTOM_SNV_NUM);

• 由 Leung 写于 2019 年 4 月 11 日