一、背景
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。
注:如果没有在编译器中定义 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.c 和 snv_flash.h 两个文件拖拽至CCS工程的Application文件夹下
添加文件过程中,选项选择如下
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 日
网友评论