1. 了解EEPROM
在嵌入式系统开发中,断电后晚能存储少量数据是常见需求。EEPROM(Electrically Erasable Programmable Read-Only Memory)是一种非易失性存储器,即使断电数据也不会丢失。ESP32的EEPROM模拟功能利用闪存空间,方便地模拟传统EEPROM,提供安全、高效的存储方案。需要注意的是,EEPROM 具有一定的擦写寿命限制,在使用时需合理规划以避免过度擦写导致的寿命问题。
1.1 ESP32 的 EEPROM 与传统的 EEPROM 有以下一些区别:
ESP32 的 EEPROM:
集成在芯片内部:作为 ESP32 微控制器的一部分,使用较为方便,无需额外的硬件。
特定的编程接口:ESP32 有其专门的库和编程方式来操作内部的 EEPROM。
传统 EEPROM:
通常是独立的芯片:需要通过特定的引脚与其他电路连接。
容量选择更广泛:可能有多种不同容量规格可供选择,更具灵活性。
可能在一些特定应用场景中更具针对性,例如对容量、读写速度等有更高要求时。
ESP32平台提供了一个内置的EEPROM模拟功能,通过Preferences
库,我们可以方便地进行数据的读写。特别的强调地是,当你在Arduino IDE中配置为ESP32开发板时,Preferences
库已经默认包含,无需额外导入。
2. Preferences示例
以下是一个简单的示例,演示如何利用Preferences
库记录设备的启动次数:
#include <Arduino.h>/*启动计数器示例,利用Preferences库。此示例展示了如何使用该库记录设备启动次数。
*/void setup()
{Serial.begin(115200);Serial.println();Preferences preferences;preferences.begin("my-app", false); // 使用“my-app”命名空间,以防止键名冲突// 可以清除整个命名空间或删除特定键// preferences.clear();// preferences.remove("counter");unsigned int counter = preferences.getUInt("counter", 0); // 获取计数器值,不存在则设为0counter++;Serial.printf("Current counter value: %u\n", counter);preferences.putUInt("counter", counter);preferences.end();Serial.println("Restarting in 10 seconds...");delay(10000);ESP.restart();
}void loop() {}
这段代码展示了如何在ESP32上利用Preferences
库存储和读取数据。每次设备启动时,它会检查并增加"counter"
的值,然后保存。
3. Preferences库的优势
- 简便的API:使用
Preferences
库,数据读写操作就像操作字典一样简单。 - 键值对存储:以键值对形式存储数据,便于管理和查找。
- 命名空间:通过命名空间避免键名冲突,允许多个功能共享存储空间。
- 自动类型处理:支持多种数据类型,如整型、字符串,无需手动处理序列化和反序列化。
4. 使用注意事项
- 键名长度限制:键名长度限制为15个字符,如示例中的
"counter"
键。 - 清除数据:可使用
clear()
清除整个命名空间,或用remove()
删除特定键。
5. 示例解析
此示例演示了如何用Preferences
库追踪设备启动次数:
setup()
函数中,begin()以只读模式(false)开启"my-app"
命名空间。注意不要把参数写为true.- getUInt()检索或初始化
"counter"
键的值,不存在则设为0。 - 加1并更新计数器值。
- 输出当前计数器值。
- putUInt()保存计数器值, end()关闭
Preferences
。 - 延迟10秒后重启ESP32。
loop()
函数中没有其他操作,因为所有逻辑都在setup()
中完成。
通过这个例子,我们看到了ESP32如何借助内置的Preferences
库简化了EEPROM操作,使数据持久化变得更加直观和高效。
6. 清除命名空间的操作
当将前面的代码烧录到ESP32后,你会发现无论你断电,还是重新烧录上述代码,counter的值都会一起累加。这个counter所在的命名空间会一直占用EEPROM空间,如何清楚除?请用以下代码清除
#include <Preferences.h>void setup() {Serial.begin(115200);delay(1000); // 等待串口初始化完成// 开启命名空间,这里以"my-app"为例Preferences preferences;preferences.begin("my-app", false);// 清除该命名空间下的所有数据preferences.clear();// 关闭命名空间preferences.end();Serial.println("Preferences namespace 'my-app' has been cleared.");// 其他setup代码...
}void loop() {// 主循环中无需操作
}
再重新烧录前面的重启次数统计的代码,counter的内容就会重新从零开始。