ESP32-C3入门教程 基础篇(八、NVS — 非易失性存储库的使用)_esp入门教学(1)

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

如果你需要这些资料,可以戳这里获取

这就不得不提一下分区表相关的内容了,官方说明地址如下:

ESP32-C3 的 flash 分区表

根据官方介绍,我们列出这里需要用到的:

分区表中的每个条目都包括以下几个部分:Name(标签)、Type(app、data 等)、SubType 以及在 flash 中的偏移量(分区的加载地址)。

  • Type 字段可以指定为 app (0x00) 或者 data (0x01),也可以直接使用数字 0-254(或者十六进制 0x00-0xFE)。注意,0x00-0x3F 不得使用(预留给 esp-idf 的核心功能)。
  • SubType 字段长度为 8 bit,内容与具体分区 Type 有关。目前,esp-idf 仅仅规定了 “app” 和 “data” 两种分区类型的子类型含义。

在这里插入图片描述

在这里插入图片描述

所以根据上面分区表的介绍说明,对于我们使用的 ESP32-C3,芯片启动会自动打印系统信息,对应的 NVS 说明如下图:

在这里插入图片描述

对于NVS分区如何生成,请参考我的另一篇博文:

ESP32-C3入门教程 网络 篇(二、 Wi-Fi 配网 — Smart_config方式 和 BlueIF方式)

在博文最后面,因为默认的分区表满足不了需求,告知了如何修改分区表。

1.3 NVS使用步骤

本文的NVS测试,是基于默认的分区表,所以在使用过程,我们不需要再进行分区表的操作。

NVS所需要用到的API,在nvs_flash.h 文件中,路径为:esp-idf/components/nvs_flash/include/nvs_flash.h

1、初始化 NVS,使用函数nvs_flash_init

在示例中:

在这里插入图片描述

2、打开NVS,使用nvs_open函数:

在这里插入图片描述

在示例中,第二个参数应该是表示打开的区域是可以读也可以写的 ,只读的是NVS_READONLY

在这里插入图片描述

3、读写操作,使用nvs_get_*(*号表示不同的数据类型,比如nvs_get_i32nvs_get_u16) 读操作,使用nvs_set_*进行写操作:

在这里插入图片描述

在这里插入图片描述

在示例中对于读写应用如下:

在这里插入图片描述

4、写入值后,需要条用nvs_commit函数确保值写入成功。

在这里插入图片描述

5、关闭NVS,完成写入后,使用nvs_close关闭。

在这里插入图片描述

2、示例测试

2.1 基础示例测试

在官方例程中,我们参考的示例程序有2个,如下图:

在这里插入图片描述

示例nvs_rw_value:

在我们上面介绍NVS的使用步骤中的举例,就是用的nvs_rw_value工程,基本的工程没什么好修改的,测试的结果如下,每次重启 restart_counter 的值就会增加1,如下图:

在这里插入图片描述

示例nvs_rw_blob:

第二个工程测试效果如下:

在这里插入图片描述

先看了测试效果,我们来简单说明一下源码,第一个函数save_restart_counter函数,和示例nvs_rw_blob基本一样,不多说。

我们来看第二个函数save_run_time,在这个函数中,我们使用了一个nvs_get_blobnvs_set_blob的函数,注意到他们都有一个 void*类型得参数,表面这两个nvs的操作能够适用于任意类型的数据。

在这里插入图片描述

上面的save_run_time函数,我们直接上添加了注释的源码:

/\* Save new run time value in NVS
 by first reading a table of previously saved values
 and then adding the new value at the end of the table.
 Return an error if anything goes wrong
 during this process.
 \*/
esp\_err\_t save\_run\_time(void)
{
    nvs\_handle\_t my_handle;
    esp\_err\_t err;

    // Open 正常的操作步骤,打开nvs,第一个命名空间,读写,句柄名称
    err = nvs\_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle);
    if (err != ESP_OK) return err;

    /\* Read the size of memory space required for blob
 unsigned int required\_size
 读nvs,读取键值对为 "run\_time" 处的内容放入 变量required\_size
 \*/
    size\_t required_size = 0;  // value will default to 0, if not set yet in NVS
    /\*先使用一次nvs\_get\_blob函数,但是第三个参数输出地址使用的是NULL,
 表示读出的数据不保存,因为这里使用只是为了看一下 "run\_time" 处是否
 有数据,只是先读一下数据,看一下读完以后 required\_size 还是不是0
 \*/
    err = nvs\_get\_blob(my_handle, "run\_time", NULL, &required_size);
    if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;

    /\* Read previously saved blob if available
 这里申请一块地址,定义为 run\_time ,地址上存放的示uint32\_t数据,大小为
 required\_size大小 + sizeof(uint32\_t) 
 \*/
    uint32\_t\* run_time = malloc(required_size + sizeof(uint32\_t));
    /\*
 如果上面读到的 required\_size 大于0 ,说明"run\_time" 处以前是保存过数据的
 那么,就先读出来,保存在刚才申请的 地址 run\_time 处(第3个参数)。
 \*/
    if (required_size > 0) {
        err = nvs\_get\_blob(my_handle, "run\_time", run_time, &required_size);
        if (err != ESP_OK) {
            free(run_time);
            return err;
        }
    }
    /\* 
 Write value including previously saved blob if available
 不管 required\_size 有过还是没有过,进行这步操作,都会使得 required\_size 增加
 增加 sizeof(uint32\_t) 大小,因为示例本意示重启一次,计数必须加一次
 \*/
    required_size += sizeof(uint32\_t);
    /\*
 每一次保存的是使用数组形式保存数据:类似于 uint32\_t run\_time[] 数组
 给数组赋值
 \*/
    run_time[required_size / sizeof(uint32\_t) - 1] = xTaskGetTickCount() \* portTICK_PERIOD_MS;
    /\*
 最后把需要保存的数组处理完成以后
 调用 nvs\_set\_blob 函数进行保存
 \*/ 
    err = nvs\_set\_blob(my_handle, "run\_time", run_time, required_size);
    free(run_time);

    if (err != ESP_OK) return err;

    // Commit
    err = nvs\_commit(my_handle);
    if (err != ESP_OK) return err;

    // Close
    nvs\_close(my_handle);
    return ESP_OK;
}

搞清楚了上面这个函数,nvs_rw_blob示例基本就没问题了,接下来的print_what_saved(void)函数中,使用了不同的 get 函数,从同一个命名空间的不同 键值处取出了不同的数据(这个具体下一节我们会做测试),其他的倒没什么特别的:

在这里插入图片描述

2.2 数据的删除

前面说过,NVS其实也就是在 Flash 空间上开辟了一块区域,那么这块区域肯定示有地址的,只是 ESP32-C3 使用 NVS方式,对使用者而言内存地址是不透明的,只是通过命名空间 和键值对自动分配(下一节会说明)。既然有保存,那么也得知道删除,因为不删除,数据可能会一直存在与那个地址空间。

示例和官方说明只是说明了如何使用 NVS 保存数据掉电不丢失,却没有针对性的说明如何清除数据。

我们通过nvs.h找到了两个函数:

esp\_err\_t nvs\_erase\_key(nvs\_handle\_t handle, const char\* key);

esp\_err\_t nvs\_erase\_all(nvs\_handle\_t handle);

使用这两个函数,我们测试一下,测试函数基于 示例nvs_rw_blob:,然后加入按键驱动:

ESP32-C3 学习测试(二、GPIO中断、按键驱动测试)

我们通过按键操作,对示例中保存的数据进行删除,在按键驱动中,我们修改一下代码:

static void button\_single\_click\_cb(void \*arg){
   
    uint8\_t \*num = (uint8\_t \*)arg;
    uint8\_t gpio_num = \*num;
    ESP\_LOGI(TAG, "BTN%d: BUTTON\_SINGLE\_CLICK\n", gpio_num);
    printf("nvs\_erase\_key test!\r\n");
    // nvs\_erase\_key(my\_handle,"restart\_conter");
    nvs\_handle\_t my_handle;

    // Open
    nvs\_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle);

    nvs\_erase\_key(my_handle,"restart\_conter");

    // err = nvs\_commit(my\_handle);

    // nvs\_close(my\_handle);
}


图示如下:

在这里插入图片描述

测试效果如下图:

在这里插入图片描述

然后换一个 key 试一试:

在这里插入图片描述

最后测试一下nvs_erase_all,如下图:

在这里插入图片描述

2.3 命名空间,键值对

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

如果你需要这些资料,可以戳这里获取

-1715870746329)]
[外链图片转存中…(img-DEfpuDKV-1715870746330)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

如果你需要这些资料,可以戳这里获取

  • 24
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值