ESP32中的Flash
关于ESP32中的Flash,我们需要再回顾一下命名规则。
我用的是立创开发板设计的板子,芯片型号是ESP32S3R8N8,因此可以知道我这块板子有8MB的Flash,大家可以参照着命名规则看看自己有多大的Flash容量。
操作Flash
#include "nvs_flash.h"
我们可以使用NVS(非易失性存储)库来对Flash进行操作,以键值对的形式存储数据,使用NVS的话,我们可以把Flash当成是一个巨大的map(C++)或是dict(Python),因此我们需要保存的每一个数据都需要一个键,并且键名不能重复。
初始化&取消初始化
首先我们需要初始化。
esp_err_t nvs_flash_init(void)
不需要参数,直接执行这个函数即可。
当我们使用完NVS的时候,我们再取消初始化。
esp_err_t nvs_flash_deinit(void)
一样是不需要参数。
启用一片空间&释放
esp_err_t nvs_open(const char *namespace_name, nvs_open_mode_t open_mode, nvs_handle_t *out_handle)
参数一传入一个字符串表示是命名空间,这边的命名空间就是我们把Flash再分为多个空间,在不同的命名空间中可以使用相同的键名,比如说名字是space1的空间中可以有叫key的键名,而在叫space2的空间中同样可以有叫key的键名。我们之前说的不能用相同的键名是指在同一个命名空间中不能用相同的键名。命名空间也是为了减少我们键名冲突的概率。
参数二选择打开的模式,NVS_READWRITE 或 NVS_READONLY ,分别是读写和只读,我们就选择读写。
参数三是传出参数,把这块命名空间的句柄给我们传出来。
可以理解成我们打开一个文件,命名空间的名字就相当于文件名,既然我们打开了文件,那么也需要关闭文件。
void nvs_close(nvs_handle_t handle)
写入数据
上面完成之后我们就可以通过句柄来进行操作了。
首先先是写入数据。
可以写入的整数型可以有以下几种类型,i8就是int8的意思,就是有符号的8bit数据,u8就是无符号的8bit数据。下面一堆函数的作用以此类推。
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)
用法也是一样的,只是把整数值换成了字符串,写入的就是字符串了。
除了字符串还可以写入二进制值。
esp_err_t nvs_set_blob(nvs_handle_t handle, const char *key, const void *value, size_t length)
用法也是大差不差,不过最后要提供写入数据的长度。用这个函数可以写入自定义类型。
提交写入申请
esp_err_t nvs_commit(nvs_handle_t handle)
在我们写入数据之后并不保证我们真的就是写进Flash里面了(不过大部分情况是会在正常的时间内真正写入),我们需要提交,也就是使用了上面的函数之后,之前写的数据就会立刻真正的写入Flash,这是为了防止我们的程序突发意外,还没等到真正写入的时刻程序就跑飞了,因此我们最好就是在执行写操作之后就加上这个提交操作。
读出数据
我们能写什么类型的数据自然就能读出什么类型的数据。
函数的命名方式也可以让我们清晰的知道具体是读出什么类型的数据。
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);
参数没什么可说的,就是提供键名然后是一个传出参数。
如果我们提供的键名是错误的,也就是在这个命名空间之内没有对应的键值对,那么返回值为ESP_ERR_NVS_NOT_FOUND。
删除键值对
删除键值对有两种,一种是删除指定的键值对。
esp_err_t nvs_erase_key(nvs_handle_t handle, const char *key)
提供句柄和键名。
另一种是全部都删除了,也可以理解为清空当前这个命名空间。
esp_err_t nvs_erase_all(nvs_handle_t handle)
当然,上面的操作同样需要使用上面的提交函数才会是当场生效。
实战记录ESP32启动次数
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs_flash.h"
void app_main(void){
vTaskDelay(3000/portTICK_PERIOD_MS); //烧录的时候会先短暂地运行,为了不影响效果这里加个延时
if(nvs_flash_init()==ESP_OK) printf("nvs init success\r\n"); //初始化NVS
nvs_handle_t nh;
nvs_open("test",NVS_READWRITE,&nh); //打开"test"
uint16_t count=0;
if(nvs_get_u16(nh,"count",&count)==ESP_ERR_NVS_NOT_FOUND){ //读取count值
printf("first count\r\n");
}else{
printf("count is %d\r\n",count);
}
nvs_set_u16(nh,"count",++count); //++之后再写入Flash里面
nvs_commit(nh); //提交
nvs_close(nh);
nvs_flash_deinit();
while(1){
vTaskDelay(10/portTICK_PERIOD_MS);
}
}
当我们第一次运行的时候由于没有写入,因此会打印出first count,并且写入0+1。
当我们按下复位之后可以发现读出的数据确实是每次加一的。