ESP-C3入门4. NVS非易失性存储使用

Posted 编程圈子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ESP-C3入门4. NVS非易失性存储使用相关的知识,希望对你有一定的参考价值。

ESP-C3入门4. NVS非易失性存储使用

一、NVS简介

官网文档地址

1. 存储类型

nvs是在Flash中存储键值对数据,适合存储小规模的数据,不适合存储大的数据。
写键值对时,key 的最大长度为154个字符, 值支持常用的数据类型, 如:

  • uint8_t
  • int8_t
  • uint16_t
  • int16_t
  • uint32_t
  • int32_t
  • uint64_t
  • int64_t
  • 字符串(以\\0结尾)
  • 可变二进制数据(BLOB)

2. 命名空间

NVS为每个键值对分配了一个命名空间,命名空间的长度最大为 NVS_KEY_NAME_MAX_SIZE-1 个字符。

3. 注意事项

另外,Flash的操作要注意:

  • Flash在写之前要保证页是空的
  • 如果nvs分区被截断,如更改分区表布局,或内容区是满的要先擦除分区内容

二、NVS的使用流程

nvs操作是在库: nvs_flash.h中。

1. 配置分区表

如果使用默认的nvs分区操作,这一步可以不用。

2. 擦除NVS空间

(1)删除默认分区

函数定义

esp_err_t nvs_flash_erase(void)
删除名称为"nvs"的默认nvs分区。如果分区已经被初始化了,则先要执行反初始化。

返回值

  • ESP_OK on success
  • ESP_ERR_NOT_FOUND 分区表中没有找到"nvs"空间
  • 其它值很少出现

(2)删除指定分区

esp_err_t nvs_flash_erase_partition(const char *part_name)

参数

  • part_name :分区表名称

返回值

nvs_flash_erase(void)相同

(3) 删除自定义分区

esp_err_t nvs_flash_erase_partition_ptr(const esp_partition_t *partition)

参数

  • partition 指向由 ESP 分区 API 获取的分区指针

返回值

  • ESP_OK on success
  • ESP_ERR_NOT_FOUND 分区表中没有找到指定分区
  • ESP_ERR_INVALID_ARG 分区为NULL
  • 其它基础闪存错误代码

3. 初始化NVS空间

(1) 函数原型

esp_err_t err = nvs_flash_init();
默认的NVS分区是在分区表中标记为“nvs"的分区。初始化NVS空间时,注意要检查flash是否已满、另外SDK版本与flash中使用的版本是否一致。

(2) 示例

下面是示例初始化代码:

    // 初始化nvs flash
    esp_err_t err = nvs_flash_init();

    // 如果nvs flash 满了就清空
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) 
        // NVS partition was truncated and needs to be erased
        // Retry nvs_flash_init
        ESP_ERROR_CHECK(nvs_flash_erase());
        err = nvs_flash_init();
    
    ESP_ERROR_CHECK(err);

4. 获取NVS空间的操作句柄

(1) 函数定义

esp_err_t nvs_open(const char *namespace_name, nvs_open_mode_t open_mode, nvs_handle_t *out_handle)

(2) 参数

  1. namespace_name:命名空间名称。最大长度为 NVS_KEY_NAME_MAX_SIZE-1 个字符,不能为空。
  2. open_mode:读写标志,NVS_READWRITE或NVS_READONLY。
  3. out_handle:操作成功(返回代码为零),将在此参数中返回句柄。

(3) 返回值

  • ESP_OK:存储句柄是否成功打开
  • ESP_FAIL:是否存在内部错误
  • ESP_ERR_NVS_NOT_INITIALIZED:存储驱动程序未初始化
  • ESP_ERR_NVS_PART_NOT_FOUND:是否找不到标签为“NVS”的分区
  • ESP_ERR_NVS_NOT_FOUND :id 命名空间尚不存在,模式NVS_READONLY
  • ESP_ERR_NVS_INVALID_NAME:命名空间名称不满足约束
  • ESP_ERR_NO_MEM:无法为内部结构分配内存的情况
  • ESP_ERR_NVS_NOT_ENOUGH_SPACE:如果没有空间容纳新条目或有太多不同的命名空间(允许的不同命名空间的最大值:254)

(4) 示例

    printf("Opening Non-Volatile Storage (NVS) handle... ");
    nvs_handle my_handle;
    err = nvs_open("storage", NVS_READWRITE, &my_handle);
    if (err != ESP_OK) 
        printf("Error (%s) opening NVS handle!\\n", esp_err_to_name(err));
    
    else 
        printf("Done\\n");
    

5. 读NVS空间

(1) 函数定义

esp_err_t nvs_get_i8(nvs_handle_t handle, const char *key, int8_t *out_value)
esp_err_t nvs_get_u8(nvs_handle_t handle, const char *key, uint8_t *out_value)
esp_err_t nvs_get_i16(nvs_handle_t handle, const char *key, int16_t *out_value)
esp_err_t nvs_get_u16(nvs_handle_t handle, const char *key, uint16_t *out_value)
esp_err_t nvs_get_i32(nvs_handle_t handle, const char *key, int32_t *out_value)
esp_err_t nvs_get_u32(nvs_handle_t handle, const char *key, uint32_t *out_value)
esp_err_t nvs_get_i64(nvs_handle_t handle, const char *key, int64_t *out_value)
esp_err_t nvs_get_u64(nvs_handle_t handle, const char *key, uint64_t *out_value)
esp_err_t nvs_get_str(nvs_handle_t handle, const char *key, char *out_value, size_t *length)
esp_err_t nvs_get_blob(nvs_handle_t handle, const char *key, void *out_value, size_t *length)

(2) 参数

  • handle 从nvs_open函数获取的句柄。
  • key 键,最大长度为 (NVS_KEY_NAME_MAX_SIZE-1) 个字符,不应为空。
  • out_value : 指向输出值的指针。对于nvs_get_str和nvs_get_blob可能是 NULL,在这种情况下,所需的长度将在长度参数中返回。

(3) 返回

  • ESP_OK:是否成功检索
  • ESP_FAIL:是否存在内部错误,很可能是 NVS 分区损坏
  • ESP_ERR_NVS_NOT_FOUND:请求的密钥不存在
  • ESP_ERR_NVS_INVALID_HANDLE: 句柄是否已关闭或为 NULL
  • ESP_ERR_NVS_INVALID_NAME:如果键名不满足约束
  • ESP_ERR_NVS_INVALID_LENGTH:长度是否不足以存储数据

(4) 示例

// Example of using nvs_get_i32:
int32_t max_buffer_size = 4096; // default value
esp_err_t err = nvs_get_i32(my_handle, "max_buffer_size", &max_buffer_size);
assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND);
// if ESP_ERR_NVS_NOT_FOUND was returned, max_buffer_size will still
// have its default value.
// Example (without error checking) of using nvs_get_str to get a string into dynamic array:
size_t required_size;
nvs_get_str(my_handle, "server_name", NULL, &required_size);
char* server_name = malloc(required_size);
nvs_get_str(my_handle, "server_name", server_name, &required_size);

// Example (without error checking) of using nvs_get_blob to get a binary data
into a static array:
uint8_t mac_addr[6];
size_t size = sizeof(mac_addr);
nvs_get_blob(my_handle, "dst_mac_addr", mac_addr, &size);

6. 保存

(1) 函数定义

esp_err_t nvs_set_i8(nvs_handle_t handle, const char *key, int8_t value)
esp_err_t nvs_set_u8(nvs_handle_t handle, const char *key, uint8_t value)
esp_err_t nvs_set_i16(nvs_handle_t handle, const char *key, int16_t value)
esp_err_t nvs_set_u16(nvs_handle_t handle, const char *key, uint16_t value)
esp_err_t nvs_set_i32(nvs_handle_t handle, const char *key, int32_t value)
esp_err_t nvs_set_u32(nvs_handle_t handle, const char *key, uint32_t value)
esp_err_t nvs_set_i64(nvs_handle_t handle, const char *key, int64_t value)
esp_err_t nvs_set_u64(nvs_handle_t handle, const char *key, uint64_t value)
esp_err_t nvs_set_str(nvs_handle_t handle, const char *key, const char *value)

(2) 参数

  • handle : 由nvs_open函数返回的句柄
  • key:键名
  • value : 值,如果是字符串且有完整的空间,最大值是4000字节;如果空间是碎片化的,则值会减少。

(3) 返回

  • ESP_OK:是否设置成功
  • ESP_ERR_NVS_INVALID_HANDLE:句柄是否被关闭或为空
  • ESP_ERR_NVS_READ_ONLY:只读
  • ESP_ERR_NVS_INVALID_NAME:名称是否无效
  • ESP_ERR_NVS_NOT_ENOUGH_SPACE:空间不足
  • ESP_ERR_NVS_REMOVE_FAILED:闪存写入操作失败导致无法更新数值,但是该值已写入,并且重新初始化nvs后将更新(前提是闪存不会再次写入失败)。
  • ESP_ERR_NVS_VALUE_TOO_LONG : 值太长

7. 删除

(1)删除所有命名空间所有值

esp_err_t nvs_erase_all(nvs_handle_t handle)

函数定义

参数

  • handle :由nvs_open返回的句柄

返回

  • ESP_OK:删除操作成功
  • ESP_FAIL:内部错误,一般是NVS分区故障
  • ESP_ERR_NVS_INVALID_HANDLE :无效句柄
  • ESP_ERR_NVS_READ_ONLY :句柄只读
  • 其它值:其它存储驱动问题

(2) 删除指定键

esp_err_t nvs_erase_key(nvs_handle_t handle, const char *key)

参数

  • handle:由nvs_open返回的句柄
  • key :键,最大 (NVS_KEY_NAME_MAX_SIZE-1) ,不得为空

返回

nvs_erase_all相同 。

7. 提交修改

(1) 函数定义

esp_err_t nvs_commit(nvs_handle_t handle)
将任何挂起的更改写入非易失性存储。

(2) 参数

  • handle :由nvs_open返回的句柄。

(3) 返回

  • ESP_OK 写入成功
  • ESP_ERR_NVS_INVALID_HANDLE 句柄为空或关闭
  • 其它错误代码:存储驱动故障

8. 关闭NVS空间

(1)函数定义

void nvs_close(nvs_handle_t handle)

参数

  • handle 要关闭的句柄

三、 其它操作

1. 获取nvs状态

esp_err_t nvs_get_stats(const char *part_name, nvs_stats_t *nvs_stats)

示例:

// Example of nvs_get_stats() to get the number of used entries and free entries:
nvs_stats_t nvs_stats;
nvs_get_stats(NULL, &nvs_stats);
printf("Count: UsedEntries = (%d), FreeEntries = (%d), AllEntries = (%d)\\n",
       nvs_stats.used_entries, nvs_stats.free_entries, nvs_stats.total_entries);

结构体定义:

struct nvs_stats_t

  • size_t used_entries : 存储条目数量
  • size_t free_entries :空闲条目数量
  • size_t total_entries:有效条目总数
  • size_t namespace_count:命名空间数量

2. 计算命名空间内的存储数量

esp_err_t nvs_get_used_entry_count(nvs_handle_t handle, size_t *used_entries)

// Example of nvs_get_used_entry_count() to get amount of all key-value pairs in one namespace:
nvs_handle_t handle;
nvs_open("namespace1", NVS_READWRITE, &handle);
...
size_t used_entries;
size_t total_entries_namespace;
if(nvs_get_used_entry_count(handle, &used_entries) == ESP_OK)
    // the total number of entries occupied by the namespace
    total_entries_namespace = used_entries + 1;

3. 键搜索

esp_err_t nvs_entry_find(const char *part_name, const char *namespace_name, nvs_type_t type, nvs_iterator_t *output_iterator)
esp_err_t nvs_entry_next(nvs_iterator_t *iterator)
esp_err_t nvs_entry_info(const nvs_iterator_t iterator, nvs_entry_info_t *out_info)

示例:

// Example of listing all the key-value pairs of any type under specified partition and namespace
 nvs_iterator_t it = NULL;
 esp_err_t res = nvs_entry_find(<nvs_partition_name>, <namespace>, NVS_TYPE_ANY, &it);
 while(res == ESP_OK) 
     nvs_entry_info_t info;
     nvs_entry_info(it, &info); // Can omit error check if parameters are guaranteed to be non-NULL
     printf("key '%s', type '%d' \\n", info.key, info.type);
     res = nvs_entry_next(&it);
 
 nvs_release_iterator(it);

四、操作示例


#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "nvs.h"


void setup() 
   
    //01 初始化nvs flash
    esp_err_t err = nvs_flash_init();

    //02 如果nvs flash 满了就清空
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) 
        ESP_ERROR_CHECK(nvs_flash_erase());
        err = nvs_flash_init();
    
    ESP_ERROR_CHECK(err);

    //03 打开nvs,配置句柄
    printf("\\n");
    printf("Opening Non-Volatile Storage (NVS) handle... ");
    nvs_handle my_handle;
    err = nvs_open("storage", NVS_READWRITE, &my_handle);
    if (err != ESP_OK) 
        printf("Error (%s) opening NVS handle!\\n", esp_err_to_name(err));
    
    else 
        printf("Done\\n");

        //04 读操作
        printf("Reading restart counter from NVS ... ");
        int32_t restart_counter = 0; 
        // 如果没有在nvs中设置,则值默认为0
        err = nvs_get_i32(my_handle, "restart_counter", &restart_counter);
        switch (err) 
        case ESP_OK:
            printf("Done\\n");
            printf("Restart counter = %d\\n", restart_counter);
            break;
        case ESP_ERR_NVS_NOT_FOUND:
            printf("The value is not initialized yet!\\n");
            break;
        default:
            printf("Error (%s) reading!\\n", esp_err_to_name(err));
        

        //05 写操作
        printf("Updating restart counter in NVS ... ");
        restart_counter++;
        err = nvs_set_i32(my_handle, "restart_counter", restart_counter);
        printf((err != ESP_OK) ? "Failed!\\n" : "Done\\n");

        //06 提交修改
        printf("Committing updates in NVS ... ");
        err = nvs_commit(my_handle);
        printf((err != ESP_OK) ? "Failed!\\n" : "Done\\n");

        //07 关闭nvs
        nvs_close(my_handle);
    

    printf("\\n");

    // 定时重启模块
    for (int i = 10; i >= 0; i--) 
        printf("Restarting in %d seconds...\\n", i);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    
    printf("Restarting now.\\n");
    fflush(stdout);
    esp_restart();
	


void app_main()

    printf("main");
    setup();
  

以上是关于ESP-C3入门4. NVS非易失性存储使用的主要内容,如果未能解决你的问题,请参考以下文章

ESP-C3入门3. NVS非易失性存储使用

MicroPython ESP32NVS数据非易失性存储示例讲解说明

Arduino框架下对ESP32 NVS非易失性存储解读以及应用示例

ESP32学习笔记(23)——NVS(非易失性存储)接口使用

everspin非易失性存储器中MRAM的潜在用途

展望由非易失性设备构成的未来存储