SOC-ESP32S3部分:21-非易失性存储库

article/2025/8/19 18:23:46

飞书文档https://x509p6c8to.feishu.cn/wiki/QB0Zw7GLeio4l4kyaWQcuQT3nZS

非易失性存储 (NVS) 库主要用于在 flash 中存储键值格式的数据。

它允许我们在芯片的闪存中存储和读取数据,即使在断电后,这些数据也不会丢失。

NVS 是 ESP32 flash(flash就是板子上的一个存储芯片)中的一个存储分区,我们可以在其中存储键值对(key-value pairs)。每个键值对都有一个唯一的键名(key name)和一个对应的值(value)。这种组合类似于哈希表的(key-value)对应结构。

初始化NVS

在开始使用NVS之前,需要先初始化整个NVS分区。通常在应用程序启动阶段完成这一操作

esp_err_t nvs_flash_init(void);
返回值
esp_err_t
表示函数执行的结果,通常为以下几种情况:
ESP_OK: 成功初始化 NVS(Non-Volatile Storage)闪存。
ESP_ERR_NVS_NO_FREE_PAGES: NVS 分区没有可用的页。
ESP_ERR_NVS_NEW_VERSION_FOUND: NVS 分区版本更新,需要格式化。
ESP_ERR_NVS_NOT_INITIALIZED: NVS 未初始化。
ESP_ERR_NVS_INVALID_STATE: NVS 已经初始化。
其他可能的错误代码,具体取决于底层实现。示例参考:
#include <nvs_flash.h>
void app_main()
{esp_err_t err = nvs_flash_init();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);
}

使用 NVS 存储数据

我们可以使用 nvs_open nvs_set_*nvs_commit三个函数来存储数据。

nvs_open 函数用于打开一个 NVS 命名空间,并返回一个句柄,通过该句柄可以进行后续的读写操作。
esp_err_t nvs_open(const char *name, nvs_open_mode_t open_mode, nvs_handle_t *out_handle);
参数
const char *name: NVS 命名空间的名称。命名空间用于组织存储的数据,类似于文件夹的概念。
nvs_open_mode_t open_mode: 打开命名空间的模式,可以是以下几种:
NVS_READONLY: 只读模式。
NVS_READWRITE: 读写模式。
nvs_handle_t *out_handle: 输出参数,用于返回打开的命名空间的句柄。通过这个句柄可以进行后续的读写操作。nvs_set_i32 函数用于将一个 32 位整数值存储到 NVS 中,使用指定的键名标识该数据。存储的数据可以通过该键名在后续的读取操作中检索。
esp_err_t nvs_set_i32(nvs_handle_t handle, const char *key, int32_t value);
参数
nvs_handle_t handle: 通过 nvs_open 函数获得的命名空间句柄。
const char *key: 要存储的数据的键名。键名用于标识存储的数据。
int32_t value: 要存储的 32 位整数值。nvs_commit 函数用于将对 NVS 命名空间所做的更改(如设置、删除键值对等)提交到闪存中。
在调用 nvs_set_i32 或其他设置函数后,必须调用 nvs_commit 以确保更改被持久化到 NVS 分区中。
如果不调用 nvs_commit,更改将不会保存到闪存中,下次读取时将不会看到这些更改
esp_err_t nvs_commit(nvs_handle_t handle);
参数
nvs_handle_t handle: 通过 nvs_open 函数获得的命名空间句柄。// 存储数据
nvs_handle_t my_handle;
esp_err_t err = nvs_open("storage", NVS_READWRITE, &my_handle);        //打开命名空间
if (err == ESP_OK) {err = nvs_set_i32(my_handle, "restart_counter", 1);if (err == ESP_OK) {err = nvs_commit(my_handle);}nvs_close(my_handle);
}

在这个例子中,我们首先打开了一个名为 “storage” 的 NVS 名称空间,然后在其中存储了一个键名为 “restart_counter”、值为1的键值对。

而nvs_commit()函数的主要作用是将所有挂起的更改写入NVS。当你在NVS中设置一个键值对后,这个更改首先被存储在内存中。只有当你调用nvs_commit()函数时,这些更改才会被写入闪存。

所以,如果你在调用nvs_commit()函数之前重启了设备,那么你在NVS中设置的所有键值对都将丢失。因此,每次在NVS中设置键值对后,都应该调用nvs_commit()函数,以确保这些更改在设备重启后仍然存在。

当然,还有其它不同类型的数据存储接口

nvs_set_i8
功能: 存储 8 位整数。
esp_err_t nvs_set_i8(nvs_handle_t handle, const char *key, int8_t value);nvs_set_u8
功能: 存储 8 位无符号整数。
esp_err_t nvs_set_u8(nvs_handle_t handle, const char *key, uint8_t value);nvs_set_i16
功能: 存储 16 位整数。
esp_err_t nvs_set_i16(nvs_handle_t handle, const char *key, int16_t value);nvs_set_u16
功能: 存储 16 位无符号整数。
esp_err_t nvs_set_u16(nvs_handle_t handle, const char *key, uint16_t value);nvs_set_u32
功能: 存储 32 位无符号整数。
esp_err_t nvs_set_u32(nvs_handle_t handle, const char *key, uint32_t value);nvs_set_i64
功能: 存储 64 位整数。
esp_err_t nvs_set_i64(nvs_handle_t handle, const char *key, int64_t value);nvs_set_u64
功能: 存储 64 位无符号整数。
esp_err_t nvs_set_u64(nvs_handle_t handle, const char *key, uint64_t value);nvs_set_str
功能: 存储字符串。
esp_err_t nvs_set_str(nvs_handle_t handle, const char *key, const char *value);
参数:
handle: NVS 命名空间句柄。
key: 键名。
value: 要存储的字符串。nvs_set_blob
功能: 存储二进制数据块。
esp_err_t nvs_set_blob(nvs_handle_t handle, const char *key, const void *value, size_t length);
参数:
handle: NVS 命名空间句柄。
key: 键名。
value: 要存储的二进制数据块。
length: 数据块的长度(以字节为单位)。使用示例:// 存储不同类型的数据err = nvs_set_i8(my_handle, "int8_key", 10);ESP_ERROR_CHECK(err);err = nvs_set_u8(my_handle, "uint8_key", 20);ESP_ERROR_CHECK(err);err = nvs_set_i16(my_handle, "int16_key", 300);ESP_ERROR_CHECK(err);err = nvs_set_u16(my_handle, "uint16_key", 400);ESP_ERROR_CHECK(err);err = nvs_set_u32(my_handle, "uint32_key", 5000);ESP_ERROR_CHECK(err);err = nvs_set_i64(my_handle, "int64_key", 60000);ESP_ERROR_CHECK(err);err = nvs_set_u64(my_handle, "uint64_key", 70000);ESP_ERROR_CHECK(err);const char *str_value = "Hello, NVS!";err = nvs_set_str(my_handle, "str_key", str_value);ESP_ERROR_CHECK(err);uint8_t blob_value[] = {0x01, 0x02, 0x03, 0x04};size_t blob_length = sizeof(blob_value);err = nvs_set_blob(my_handle, "blob_key", blob_value, blob_length);ESP_ERROR_CHECK(err);

使用 NVS 读取数据

我们可以使用 nvs_get_* 函数来读取数据。例如,我们可以使用 nvs_get_i32 函数来读取一个整数:

nvs_get_i32 函数用于从 NVS 中读取一个 32 位整数值,使用指定的键名标识该数据。读取的数据存储在输出参数中,可以通过该参数访问。
esp_err_t nvs_get_i32(nvs_handle_t handle, const char *key, int32_t *out_value);
参数
nvs_handle_t handle: 通过 nvs_open 函数获得的命名空间句柄。const char *key: 要读取的数据的键名。键名用于标识存储的数据。int32_t *out_value: 输出参数,用于存储从 NVS 中读取的 32 位整数值。使用参考
// 检索数据
nvs_handle_t my_handle;
esp_err_t err = nvs_open("storage", NVS_READWRITE, &my_handle);
if (err == ESP_OK) {int32_t value;err = nvs_get_i32(my_handle, "restart_counter", &value);if (err == ESP_OK) {printf("Value = %d\n", value);}nvs_close(my_handle);
}

删掉NVS上的数据

esp_err_t nvs_flash_erase(void);
返回值
esp_err_t
表示函数执行的结果,通常为以下几种情况:
ESP_OK: 成功擦除 NVS 分区。
ESP_ERR_NVS_NOT_INITIALIZED: NVS 未初始化。
ESP_ERR_NVS_INVALID_STATE: NVS 处于无效状态。
其他可能的错误代码,具体取决于底层实现

最终参考程序:

#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_log.h"static const char *TAG = "NVS"; // 定义日志标签void app_main(void)
{// 初始化 NVSesp_err_t err = nvs_flash_init();if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {// NVS 分区被截断,需要擦除// 重新初始化 nvs_flashESP_ERROR_CHECK(nvs_flash_erase());err = nvs_flash_init();}ESP_ERROR_CHECK(err);// 打开命名空间ESP_LOGI(TAG, "打开非易失性存储 (NVS) 句柄...");nvs_handle_t my_handle;err = nvs_open("storage", NVS_READWRITE, &my_handle);if (err != ESP_OK) {ESP_LOGI(TAG, "打开 NVS 句柄时出错 (%s)!\n", esp_err_to_name(err));} else {// 读取重启计数器ESP_LOGI(TAG, "从 NVS 读取重启计数器 ... ");int32_t restart_counter = 0; // 如果 NVS 中未设置值,则默认为 0err = nvs_get_i32(my_handle, "restart_counter", &restart_counter);ESP_LOGI(TAG, "重启计数器 = %" PRIu32 "\n", restart_counter);// 更新重启计数器ESP_LOGI(TAG, "更新 NVS 中的重启计数器 ... ");restart_counter++;err = nvs_set_i32(my_handle, "restart_counter", restart_counter);// 提交写入的值。设置任何值后,必须调用 nvs_commit() 以确保更改写入闪存存储。ESP_LOGI(TAG, "提交 NVS 中的更改 ... ");err = nvs_commit(my_handle);// 关闭命名空间nvs_close(my_handle);}ESP_LOGI(TAG, "\n");// 重启模块for (int i = 10; i >= 0; i--) {ESP_LOGI(TAG, "将在 %d 秒后重启...\n", i);vTaskDelay(1000 / portTICK_PERIOD_MS);}ESP_LOGI(TAG, "现在重启。\n");fflush(stdout);esp_restart();
}

http://www.hkcw.cn/article/pGhLvKAnOS.shtml

相关文章

Agentic RAG 的技术演进详解

一、从被动检索到主动决策&#xff1a;Agentic RAG的核心突破 在人工智能领域&#xff0c;检索增强生成&#xff08;RAG&#xff09;技术的诞生标志着大语言模型&#xff08;LLM&#xff09;从“内部知识闭环”迈向“外部数据互联”的关键一步。传统RAG通过检索外部文档为生成…

2025-05-31 Python深度学习9——网络模型的加载与保存

文章目录 1 使用现有网络2 修改网络结构2.1 添加新层2.2 替换现有层 3 保存网络模型3.1 完整保存3.2 参数保存&#xff08;推荐&#xff09; 4 加载网络模型4.1 加载完整模型文件4.2 加载参数文件 5 Checkpoint5.1 保存 Checkpoint5.2 加载 Checkpoint 本文环境&#xff1a; Py…

智能测试新范式:GenAI 与 Playwright MCP 如何重塑 QA 流程

文章简介 在敏捷开发背景下&#xff0c;传统测试自动化面临动态 UI 适配难、脚本维护成本高等挑战。本文深度解析 ** 生成式 AI&#xff08;GenAI&#xff09;与Playwright MCP&#xff08;模型上下文协议&#xff09;** 的协同机制&#xff0c;展示如何通过自然语言驱动测试创…

孙颖莎全红婵们等比例长大 体育名将童心未泯

显示图片孙颖莎全红婵们等比例长大-今日头条-手机光明网今天是六一儿童节,祝大家儿童节快乐!来看看体育圈那些等比例长大的名将们吧!2025-06-01 10:05:46责任编辑:zx0176

赛龙舟快得千篇一律沉得五花八门 各地龙舟赛“名场面”

端午节到了,又是一年一度观看“水上F1”的时刻。作为端午节的传统节目,全国各地的龙舟大赛已经激烈打响,各地画风确实有所不同。快得千篇一律,沉得五花八门。有人划龙舟是为了纪念屈原,有人则是去救屈原,还有人是屈原救我。广东佛山的叠滘龙舟队是龙舟界公认最具实力的队…

AI预测3D新模型百十个定位预测+胆码预测+去和尾2025年5月31日第94弹

从今天开始&#xff0c;咱们还是暂时基于旧的模型进行预测&#xff0c;好了&#xff0c;废话不多说&#xff0c;按照老办法&#xff0c;重点8-9码定位&#xff0c;配合三胆下1或下2&#xff0c;杀1-2个和尾&#xff0c;再杀4-5个和值&#xff0c;可以做到100-300注左右。 (1)定…

HackMyVM-First

信息搜集 主机发现 ┌──(kali㉿kali)-[~] └─$ nmap -sn 192.168.43.0/24 Starting Nmap 7.95 ( https://nmap.org ) at 2025-05-31 06:13 EDT Nmap scan report for 192.168.43.1 Host is up (0.0080s latency). MAC Address: C6:45:66:05:91:88 (Unknown) …

【赵渝强老师】数据库不适合Docker容器化部署的原因

在Docker的容器中可以部署运行一个MySQL数据库&#xff0c;并通过数据卷将运行在容器中的MySQL数据库的数据进行持久化。如果这时候运行MySQL的容器被销毁了&#xff0c;数据也将会发生丢失。因此在Docker中部署数据库服务时&#xff0c;一定要考虑数据持久化的问题。但数据库并…

“婚内强奸”被羁押285天当事人发声 申请33万赔偿

5月30日,尹某向河南省濮阳县人民检察院递交了《国家赔偿申请书》,申请国家赔偿33万元,并要求追责相关办案人员。濮阳县人民检察院接受了相关的申请材料。尹某表示,当时以强奸罪对他提起公诉,量刑为4年,但一年前决定不起诉,他被错误羁押了285天,希望获得应有的国家赔偿。…

美计划大幅增加对台军售,外交部回应 坚决反对售台武器

中国外交部发言人林剑在例行记者会上表示,中方坚决反对美国向中国台湾地区出售武器,敦促美方恪守一个中国原则和中美三个联合公报,特别是“八一七”公报的规定,停止售台武器,停止制造台海局势紧张因素。有记者在会上提问,据报道,美国特朗普政府正计划扩大对台军售规模,…

事关志愿填报!这些核心信息必看

随着高考临近,各高校陆续公布2025年招生章程,它是考生了解高校招生信息、录取规则的重要渠道,也是填报志愿的关键依据,怎么吃透招生章程?需要重点关注哪些信息?一起来听听高校招办主任的解读。高校招生章程必看的核心信息高校招生章程包含招生计划分配原则、招生录取规则…

Labubu冲破关税打压美国卖断货,中国智造雄起 原创设计赢得全球认可

美国摩根大通CEO杰米戴蒙最近访问了中国,他表示,面对美国的关税打压,中国人并不害怕,想让中国对美国卑躬屈膝的想法是不现实的。中国能够昂首挺胸,因为有越来越多的中国公司能生产出让美国消费者喜欢的产品。近期,一个来自中国的娃娃Labubu成为了世界顶流。从最初被部分人…

AR测量工具:精准测量,多功能集成

在日常生活中&#xff0c;我们常常会遇到需要测量物体长度、距离或角度的情况。无论是装修房屋、制作家具&#xff0c;还是进行户外活动&#xff0c;一个精准的测量工具都能大大提高我们的工作效率。AR测量工具就是这样一款集多种功能于一体的实用测量软件&#xff0c;它利用增…

LiquiGen流体导入UE

导出ABC 导出贴图 ABC导入Houdini UE安装SideFX_Labs插件 C:\Users\Star\Documents\houdini20.5\SideFXLabs\unreal\5.5 参考: LiquiGenHoudiniUE血液流程_哔哩哔哩_bilibili

Unity3D仿星露谷物语开发57之保存库存信息到文件

1、目标 保存下面库存栏中信息到文件中。 2、修改SceneSave.cs脚本 添加2行代码&#xff1a; 3、修改InventoryManager对象 添加Generate GUID组件。 4、修改InventoryManager.cs脚本 添加继承自ISaveable 添加属性信息&#xff1a; private string _iSaveableUniqueID;pub…

港大NVMIT开源Fast-dLLM:无需重新训练模型,直接提升扩散语言模型的推理效率

作者&#xff1a;吴成岳&#xff0c;香港大学博士生 原文&#xff1a;https://mp.weixin.qq.com/s/o0a-swHZOplknnNxpqlsaA 最近的Gemini Diffusion语言模型展现了惊人的throughput和效果&#xff0c;但是开源的扩散语言模型由于缺少kv cache以及在并行解码的时候性能严重下降等…

【AGI】Qwen3混合推理模型微调数据集

【AGI】Qwen3混合推理模型微调数据集 &#xff08;1&#xff09;OpenMathReasoning 数据集&#xff08;AIMO-2&#xff09;&#xff08;2&#xff09;FineTome-100k 数据集&#xff08;Maxime Labonne&#xff09; ​ 搭建好基础环境后&#xff0c;开始准备Qwen3混合推理模型微…

联邦学习与深度学习结合

联邦学习&#xff08;Federated Learning&#xff09;与深度学习&#xff08;Deep Learning&#xff09;的结合&#xff0c;是当前人工智能领域的研究热点之一。这种结合既发挥了深度学习在复杂数据建模中的强大能力&#xff0c;又通过联邦学习的分布式框架解决了数据隐私、安全…

特朗普送马斯克白宫钥匙 马斯克将任总统顾问

当地时间30日,美国总统特朗普在白宫举行新闻发布会“欢送”“政府效率部(DOGE)”负责人马斯克。发布会上,两人互道临别感言,特朗普向马斯克赠送了一把金色的白宫钥匙,而马斯克则表示,在从DOGE离职后,将成为特朗普的顾问,继续为其提供建议。当天,在椭圆形办公室举行的新…

日方请求恢复水产品入华,外交部回应 坚持科学安全原则

5月30日,外交部发言人林剑主持例行记者会。会上,日本广播协会的记者提问,询问发言人提到的关于日本水产品安全问题的新一轮技术交流取得实质性进展具体指哪些方面。记者还提到,日方表示双方已就恢复进口日本水产品所需的技术性条件达成一致,并询问中方是否也持有相同的看法…