NVM3(Third Generation Non-Volatile Memory,第三代非易失性存储器)驱动程序提供了一种读写存储在 Flash 中的数据对象(key/value pair
)的方法。耗损平衡(wear-leveling)用于减少擦写周期,以最大限度地延长 Flash 的寿命。该驱动程序对断电和重启事件是可复原的,这确保从驱动程序中检索的对象总是处于有效状态。单个 NVM3 实例可以在多个无线协议栈和应用代码之间共享,这使得它非常适合多协议应用。本应用笔记解释了如何在 ZigBee 和 Thread 应用(包括使用 ZigBee 和 Bluetooth 的动态多协议应用)中使用 NVM3 进行非易失性数据存储。在 ZigBee 应用中使用 NVM3 时,NVM3 存储可以放在外部 Flash 中,以减少内部 Flash 的使用。
NVM3(Third Generation Non-Volatile Memory,第三代非易失性存储器)数据存储驱动程序是 Simulated EEPROM(SimEE)的替代品。其与 EmberZNet 和 Silicon Labs Thread 以及持久化存储(PS Store)一起使用,或与 Silicon Labs Bluetooth 协议栈一起使用。由于 NVM3 可以与 EmberZNet 和 Bluetooth 一起使用,因此它允许在使用 Bluetooth 和 EmberZNet 的 DMP(Dynamic Multiprotocol,动态多协议)应用中共享单个数据存储实例。
NVM3 设计用于 EmberZNet、Silicon Labs Thread、Flex 和 Bluetooth 应用(在 EFR32 上运行),以及 MCU 应用(在 EFM32 上运行)。
以下是 NVM3 的一些主要特性:
- 在 Flash 中存储
key/value pair
数据 - 运行时创建和删除对象
- 在断电和重启事件间保持
- 耗损均衡可最大限度地延长 Flash 寿命
- 对象大小最多可配置为 4096 字节
- 可配置的 Flash 存储大小(最少 3 个 Flash 页)
- 具有可配置大小的缓存,以便快速访问对象
- 数据和计数器对象类型
- 提供了 Token 和 PS Store API 的兼容层
- 多协议应用中的单个共享存储实例
- 重填(repack)API,以允许应用在 CPU 负载较低的时段运行 clean-up 页擦除
- 可以选择将数据存储放置在外部 Flash 中(仅限 EmberZNet 应用)(BETA quality)
有关 NVM3 的详细信息,请参阅 Gecko HAL & Driver API Reference Guide 中的 EMDRV->NVM3 部分。开发 EFM32 MCU 应用或通过其 native API 访问 NVM3 的用户,请参阅此 API 参考指南。使用 EmberZNet 和 Bluetooth 开发 EmberZNet、Thread 或 DMP 应用的用户应该使用此应用笔记来了解如何在这些用例中使用 NVM3。
本章提供了有关如何与 ZigBee、Thread 或 DMP 应用,或与 ZigBee 和 Bluetooth,一起使用 NVM3 的信息。首先提供关于 NVM3 的信息,包括:
- NVM3 重填
- 默认 NVM3 实例
NVM3 Library plugin
Simulated EEPROM version 2 (SimEEv2) to NVM3 Upgrade plugin
- NVM3 栈使用情况
- 最大基本存储
NVM3 External Flash Library Plugin
当 Flash 填满时,它将达到无法再存储其它对象的点。其需要重填操作来释放过时的对象,以释放 Flash。由于擦除页面需要很长时间,因此 NVM3 驱动程序不会自动触发该过程,除非可用空间达到一个较低的临界级别。该临界级别称为强制重填界限(Forced Repack Limit),当达到该级别时,NVM3 驱动程序会在用户启动写入操作时自动运行重填。强制重填界限根据设备的页大小和 NVM3 的初始化参数来自动计算得出。
在某些应用中,在 CPU 空闲时调度重填以便不干扰其它任务的时序是有利的。在这种情况下,应用代码可以通过调用 nvm3_repack()
函数来触发重填过程。如果可用空间低于用户重填界限(User Repack Limit),则该函数将触发重填;如果有较多的可用空间,则该函数将立即返回。可以通过设置低于强制重填界限多少字节来相对地配置用户重填界限,用户重填界限应当置于 NVM3 初始化结构中。该默认值为 0
,表示用户重填界限与强制重填界限相同。如果应用的时序要求较为严格,则可能需要将用户重填界限设置成远低于强制重填界限,以确保通过调用 nvm3_repack()
来触发的所有重填都执行,避免强制重填。在这种情况下,用户重填界限应远低于强制重填界限,以允许 nvm3_repack()
调用之间的最坏情况数量的对象写入(包括开销),而不会达到强制重填界限。
在 nvm3_repack()
调用期间,NVM3 将数据移动到新页或删除过时的页。调用将最多阻塞等于页擦除时间加上较小执行开销的时段。EFM32 和 EFR32 器件的页擦除时间可在各自的数据手册中找到。
有关更多重填的信息,请参阅 Gecko HAL & Driver API Reference Guide 中的 EMDRV->NVM3 部分。
可以在设备上创建多个 NVM3 实例(彼此独立存在),但为了节省存储空间,通常仅使用一个 NVM3 实例,因为每个实例都会增加一些开销。对于使用 Gecko SDK 构建的 ZigBee、Thread 或 DMP 应用,使用一个通用的默认实例。在当前版本中,该 NVM3 实例设置为使用 36kB 的 Flash 空间进行数据存储。NVM3 还具有缓存(cache)来加速对 NVM3 对象的访问,该缓存的默认大小为 200 个元素(element),但可以在 AppBuilder 的 NVM3 Library plugin
选项中进行配置,如 Figure 2.1 所示。
注意:缓存大小必须设置为大于或等于所使用的 NVM3 对象数的值。这些包括通过 native NVM3 API 创建的 Token、PS Store 对象和 NVM3 对象的数量。对于
indexed token
,为每个索引添加一个缓存项。
nvm3_countObjects()
函数可用于查找任何给定点的已使用 NVM3 对象数。Silicon Labs 建议在初始化 Token、PS Store 和 native NVM3 对象后检查此函数,以估计 NVM3 默认缓存大小的正确大小。
NVM3 使用一个 20-bit key 来标识每个对象。为避免对多个对象使用相同的 key,默认 NVM3 实例的 NVM3 key 空间已划分为多个域,如下表所示。例如,在 EmberZNet 协议栈中定义的 NVM3 对象应该使用 0x10000
至 0x1FFFF
范围内的 NVM3 key,而用户应用 Token 应该使用 0x10000
以下的 key。
Domain | NVM3 Key |
---|---|
User | 0x00000 - 0x0FFFF |
EmberZNet stack | 0x10000 - 0x1FFFF |
Silicon Labs Thread stack | 0x20000 - 0x2FFFF |
Connect (Flex) stack | 0x30000 - 0x3FFFF |
Bluetooth stack | 0x40000 - 0x4FFFF |
Z-Wave stack | 0x50000 - 0x5FFFF |
Reserved | 0x60000 - 0xFFFFF |
要将 NVM3 与 EmberZNet、Thread 或 DMP 示例应用一起使用,项目中应该包含 NVM3 Library plugin
。所有 PS Store
和 SimEE
插件应该取消选择。
NVM3 Library plugin
提供四个插件选项:
Flash Pages
:用于 NVM3 数据存储的 Flash 页数。必须是3
或以上。Cache Size
:要缓存的对象数。为减少访问时间,此数值应该等于或大于 NVM3 中随时存储在 NVM3 中的对象数(包括 Token)。Max Object Size
:允许的最大的 NVM3 对象的大小(以字节为单位)。必须介于208
和4096
之间。User Repack Headroom
:Headroom
确定了用户重填界限少于强制重填界限多少字节数。其默认值为0
,表示强制重填界限与用户重填界限相同。
重要提示:使用当前版本的
NVM3 library plugin
为 Flash 中已包含 NVM3 实例的设备创建应用时,为 NVM3 实例配置的 Flash 页数必须与设备上找到的 NVM3 实例的 Flash 页数相匹配。因此,一旦将 NVM3 实例安装在设备上,就无法更改 NVM3 实例的大小,而无需先删除存储NVM3实例的Flash页面和存储在那里的NVM3对象。因此,一旦在设备上安装了 NVM3 实例,就不可能在不擦除存有 NVM3 实例及 NVM3 对象的 Flash 页的情况下,更改实例的大小。
使用 NVM3 Library plugin
时,必须包含 Simulated EEPROM version 2 to NVM3 Upgrade Library
或 Simulated EEPROM version 2 to NVM3 Upgrade Stub Library
,如 2.4 SimEEv2 to NVM3 Upgrade Plugin 中所述。
为 ZigBee 和 Thread 应用提供了一个 AppBuilder 插件(Simulated EEPROM version 2 to NVM3 Upgrade Library
),该插件用于将存储在 SimEEv2 中的 Token 升级到 NVM3。要使 Token 成功地升级到 NVM3,必须为所有 Token 添加 CREATOR_*
和 NVM3KEY_*
定义,如 3.1 Token API 中所述。该升级插件将使用 NVM3 存储实例来替换 SimEEv2 存储。该插件通过将 SimEEv2 存储压缩至 12kB,然后在原始 36kB SimEEv2 存储空间的剩余 24kB 中创建 NVM3 实例来实现此目的。在将 Token 数据从 SimEEv2 复制到 NVM3 后,将擦除 SimEEv2 的存储并调整 NVM3 实例的大小以使用整个 36kB 存储空间。除了升级库代码所需的代码空间外,升级不需要任何额外的 Flash 空间。该升级插件要求现有的 SimEEv2 存储空间和新的 NVM3 存储空间位于同一地址并具有相同的大小。
要启用升级,应该包含 Simulated EEPROM version 2 to NVM3 Upgrade Library
,如下图所示。如果未找到 SimEEv2 Token 数据,则该升级插件将查找 NVM3 数据,如果未找到,则将创建新的 NVM3 实例,其 Token 设置为其默认值。对于不需要升级任何 SimEEv2 Token 的应用,应该包括 Simulated EEPROM version 2 to NVM3 Upgrade Stub
插件。
为 ZigBee 应用提供了一个 AppBuilder 插件(NVM3 External Flash Library
),它将 NVM3 数据存储放在外部 Flash 中。通过去除内部 Flash 中 NVM3 对象的存储,可以对减少内部 Flash 的使用。
NVM3 External Flash Library
插件需要一个 SPI-enabled Gecko Bootloader
来与 external SPI Flash 进行通信。与外部 Flash 的所有通信都由 Gecko Bootloader 处理。NVM3 又通过 Bootloader 应用接口与 Gecko Bootloader 通信。SPI 接口的配置通过 bootloader 配置来完成。有关如何配置 Gecko Bootloader 的更多信息,请参阅 UG266: Silicon Labs Gecko Bootloader User’s Guide。
NVM3 External Flash Library
插件提供以下插件选项:
External Flash NVM3 Start Address
:外部 Flash 地址空间中 NVM3 存储的起始地址。这必须是外部 Flash 的 Flash 页边界的开始。External Flash NVM3 Size
:外部 Flash 中用于 NVM3 存储的字节数。这必须匹配外部 Flash 的整个 Flash 页数。Cache Size
:要缓存的对象数。为了减少访问时间,此数值应该等于或高于 NVM3 中随时存储在 NVM3 中的对象数(包括 Token)。Max Object Size
:允许的最大 NVM3 对象的大小(以字节为单位)。必须介于208
和4096
之间。User Repack Headroom
:Headroom
确定了用户重填界限少于强制重填界限多少字节数。其默认值为0
,表示强制重填界限与用户重填界限相同。
重要提示:默认的 NVM3 起始地址和大小设置将 NVM3 存储放置在 512kB 外部 Flash 的最高 36kB 中。必须将此设置设置为与外部 Flash 中未使用的空间匹配。注意,Gecko Bootloader 的存储插槽必须配置为不与选定的 NVM3 存储空间重叠。
NVM3 External Flash Library
插件仅进行了 beta 测试。Silicon Labs 不支持此 Beta 版本的任何生产部署。只有插件的 GA 版本才支持生产部署。此版本仅用于实验室和评估目的。
在外部 Flash 中使用 NVM3 时,NVM3 数据在存储到外部 Flash 之前由 NVM3 加密。设备首次启动时会生成一个随机的 NVM3 AES 密钥,然后存储在设备的 Lock Bits 页中。此密钥用于加密每个 NVM3 对象。NVM3 还为每个对象加密生成随机数。注意,存储在 lock bits 页中的密钥必须与用于加密外部 Flash 中的 NVM3 内容的密钥匹配,以便能够解密对象。如果在设备上执行设备解锁,擦除 lock bits 页和 NVM3 加密密钥,则外部 Flash 中的 NVM3 对象将不再可读。在这种情况下,应该擦除外部 Flash 中的 NVM3 存储空间,以允许 NVM3 使用新的加密密钥创建新的存储实例。
NVM3 使用多达 380 个字节的栈,任何调用 NVM3 功能的操作系统任务栈都应足够大,以应对 NVM3 栈的使用。
基本存储(basic storage)定义为所有对象实例的大小,包括使用数据存储的任何开销。对于 NVM3,您可以存储的最大数据量取决于用于存储的 Flash 页数和用于 NVM3 的最大对象大小。下表展示了最多 18 个 2kB Flash 页允许的最大基本存储空间以及最小的(208 字节)、默认的(254 字节)和最大的(1900 字节)最大对象大小。注意,这是一个理论上的限制,如果基本存储器在此限制中,则不会留下用于耗损均衡的空间,并且将为每个对象的写入强制擦除页。因此,NVM3 实例应配置足够的 Flash 页,以使最大允许的基本存储空间显著高于实际的基本存储空间。
Flash pages |
Total size (bytes) |
Max allowed basic storage (bytes) | |||
---|---|---|---|---|---|
Max object size = 208 bytes |
Max object size = 254 bytes |
Max object size = 1900 bytes |
Max object size = 4096 bytes |
||
3 | 6144 | 1596 | 1504 | 0 | 0 |
4 | 8192 | 3624 | 3532 | 240 | 0 |
5 | 10240 | 5652 | 5560 | 2268 | 0 |
6 | 12288 | 7680 | 7588 | 4296 | 0 |
7 | 14336 | 9708 | 9616 | 6324 | 0 |
8 | 16384 | 11736 | 11644 | 8352 | 0 |
9 | 18432 | 13764 | 13672 | 10380 | 1900 |
10 | 20480 | 15792 | 15700 | 12408 | 3928 |
11 | 22528 | 17820 | 17728 | 14436 | 5956 |
12 | 24576 | 19848 | 19756 | 16464 | 7984 |
13 | 26624 | 21876 | 21784 | 18492 | 10012 |
14 | 28672 | 23904 | 23812 | 20520 | 12040 |
15 | 30720 | 25932 | 25840 | 22548 | 14068 |
16 | 32768 | 27960 | 27868 | 24576 | 16096 |
17 | 34816 | 29988 | 29896 | 26604 | 18124 |
18 | 36864 | 32016 | 31924 | 28632 | 20152 |
本章介绍了可用于访问 NVM3 对象的三种不同 API:
- Token API
- Persistent Store API
- Native NVM3 API
Token API 用于访问存储在 EmberZNet、Silicon Labs Thread 和 Flex SDK 以及多协议应用中的 SimEEv1 和 SimEEv2 中的数据。有关如何定义和访问 Token 的信息可以在 AN1154: Using Tokens for Non-Volatile Data Storage 中找到,用户应在使用 Token API 之前阅读该文档。当选择 NVM3 库插件而不是 SimEE 插件时,NVM3 默认实例将代替 SimEE 用来存储 Token 数据。可以使用相同的 Token API 来访问存储在 NVM3 中的 Token,但 Token 的定义需要进行一些修改才能与 NVM3 一起使用,如下所述。
在定义要与 SimEE 一起使用的 Token 时,必须将 creator code 定义为 Token 的标识符。同样,在定义要与 NVM3 一起使用的 Token 时,必须为 Token 定义 NVM3 key。与 NVM3 和 SimEE 兼容的 Token 定义将包含 NVM3 key 和 creator code,如下所示:
#define CREATOR_name 16bit_value
#define NVM3KEY_name 20bit_value
#ifdef DEFINETYPES
typedef data_type type
#endif
#ifdef DEFINETOKENS
DEFINE_*_TOKEN(name, type, ... ,defaults)
#endif
根据 2.2.1 中的表,为 Token 选择一个 20-bit NVM3 key。每个 Token 必须具有唯一的 NVM3 key(Indexed Token 除外),其中必须按照 3.1.2 Indexed Token 的特殊注意事项 中的描述保留更多的 NVM3 key。
由于 Token 是在编译时创建的,因此无法在运行时创建或删除它们。然而,NVM3 对象是在运行时创建和删除的,Token 初始化函数为每个定义的 Token 创建 NVM3 对象(如果它们还不存在的话)。Token 初始化不会删除没有与之关联的相应 Token 的 NVM3 对象。因此,如果应用中不再包含 Token,则应用应使用 3.3 Native NVM3 API 中描述的 NVM3 Native API 手动删除关联的 NVM3 对象。
NVM3 没有对 Indexed Token 的 native support,因此,对 Indexed Token 的 NVM3 key 选择有特别的要求。使用 NVM3,Indexed Token 是通过将每个索引存储在单独的对象中来实现的,从定义的 NVM3KEY_name key
值(索引 0)开始到 NVM3KEY_name + 127
(索引 127)结束。由于此实现,必须为每个 Indexed Token 保留 128 个 NVM3 key。用户仍然只需定义一个 NVM3KEY_name key
值,但不应该使用此定义后面的 127 个值中的 key 值来定义其他 Token。即使 Token 定义的索引少于 128 个,也应该保留 128 个索引,因为以后可能会使用更多索引来扩展 Token。
以下示例展示了在 user key domain 中定义的两个 Indexed Token:
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved
#define NVM3KEY_MY_INDEXED_TOKEN_A 0x00000
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved
#define NVM3KEY_MY_INDEXED_TOKEN_B 0x00080
NVM3 Key | NVM3 Objects Contents |
---|---|
0x00000 | Reserved for TOKEN_MY_INDEXED_TOKEN_A index 0 |
0x00001 | Reserved for TOKEN_MY_INDEXED_TOKEN_A index 1 |
0x00002 | Reserved for TOKEN_MY_INDEXED_TOKEN_A index 2 |
... | |
0x0007F | Reserved for TOKEN_MY_INDEXED_TOKEN_A index 127 |
0x00080 | Reserved for TOKEN_MY_INDEXED_TOKEN_B index 0 |
0x00081 | Reserved for TOKEN_MY_INDEXED_TOKEN_B index 1 |
... | |
0x000FF | Reserved for TOKEN_MY_INDEXED_TOKEN_B index 127 |
持久存储(PS Store)在 Silicon Labs Bluetooth 应用中用于将数据存储在 Flash 中。在 DMP 应用中,PS Store 设置为使用默认 NVM3 实例作为其存储机制。Bluetooth API Reference Manual 中介绍了 PS Store API。
16-bit key 与 PS Store API 一起使用,在 NVM3 用作 PS Store 存储机制时,该 key 映射到 20-bit NVM3 key。其中四个最高有效位设置为 0x4,以将这些对象放置在 NVM3 默认实例 key 空间的 Bluetooth domain 中。由于 PS Store API 已固定为仅在 Bluetooth domain 中使用,因此应使用 native NVM3 API 来创建和访问要放置在其他域中的任何对象(例如 User domain)。
对于访问不需要与 Token 或 PS Store API 兼容的 NVM3 对象,建议使用 native NVM3 API 来访问 NVM3 数据,以减少代码大小和允许使用 NVM3 的完整特性集。也可以使用相同的 NVM3 key 通过 native NVM3 API 来访问任何 PS Store 对象或 Token。有关此 API 的完整文档,请参阅 Gecko HAL & Driver API Reference Guide 的 EMDRV->NVM3 部分。
Simplicity Commander 是在生产环境中使用的一个通用工具。它使用一个简单的 CLI(scriptable)调用。Simplicity Commander 支持从设备中读取 NVM3 数据区并解析 NVM3 数据以提取存储的值。这在调试方案中非常有用,您可能需要找出已运行一段时间的应用的存储状态。
有关如何在 NVM3 中使用 Simple Commander 的更多信息,请参阅 UG162: Simplicity Commander Reference Guide。