آموزش حافظه EEPROM برد ESP32 (جامع)

این یک راهنمای جامع برای حافظه EEPROM و کتابخانه آن در ESP32 برای Arduino Core است. EEPROM نوعی حافظه غیر فرار (NVM) است، به این معنی که دادههای ذخیرهشده در آن هنگام قطع برق یا ریست سخت ESP32 از بین نمیروند.
در این آموزش شما با عملکرد آن آشنا میشوید و متوجه میشوید که کتابخانه ESP32 EEPROM در واقع با استفاده از حافظه FLASH، عملکرد EEPROM را شبیهسازی میکند. ما چند پروژه نمونه ایجاد میکنیم تا بتوانید توابع کتابخانه ESP32 EEPROM را تمرین و آزمایش کنید.
قبل از ادامه این آموزش، باید Arduino Core برای ESP32 را در Arduino IDE نصب کرده باشید تا بتوانید پروژهها را برای ESP32 کامپایل و اجرا کنید.
گزینه دیگر استفاده از حافظه SPI FLASH خارجی است که رابطهای ساده کتابخانهای برای اکثر برنامههای ساده ارائه میدهد. برای یادگیری بیشتر در مورد گزینههای حافظه غیر فرار ESP32 (NVS)، میتوانید آموزشهای پیشنهادی را بررسی کنید.
EEPROM در برد ESP32
EEPROM (حافظه فقط خواندنی قابل برنامهریزی و پاکشونده الکتریکی) نوعی حافظه غیر فرار است، مشابه FLASH. هر دو برای ذخیرهسازی دائمی داده استفاده میشوند، زیرا دادههای ذخیرهشده در EEPROM یا FLASH هنگام قطع برق یا ریست سخت از بین نمیروند.
ابتدا باید بدانید که ESP32 حافظه داخلی EEPROM ندارد. با این حال، کتابخانه ESP32 EEPROM عملکرد آن را با نوشتن و خواندن دادهها از حافظه FLASH شبیهسازی میکند. این پیادهسازی اولیه بر پایه کد درایور NVS ESP32 بود و بسیار شبیه به کتابخانه شناختهشده EEPROM آردوینو است.
در زمان نگارش این آموزش، کتابخانه EEPROM برای ESP32 منسوخ اعلام شده و با پیادهسازی جدیدتر و قدرتمندتر Preferences جایگزین شده است که از حافظه FLASH ESP32 استفاده میکند. میتوانید اطلاعات بیشتر را در همین آموزش بیابید.
کاربرد های ESP32 EEPROM و NVS
حافظههای غیر فرار معمولاً زمانی استفاده میشوند که بخواهیم دادهای را ذخیره کنیم و میکروکنترلر آن را حتی بعد از قطع برق یا ریست به خاطر بسپارد. نمونههای معمول از دادههایی که باید در NVS ذخیره شوند شامل موارد زیر است:
- رمز عبور
- اطلاعات ورود WiFi
- پیکربندی کاربر
- پارامترهای سطح اپلیکیشن
- آخرین خوانش حسگر
- متغیرهای وضعیت (برای برخی ماشینهای حالت)
- دادههای کالیبراسیون حسگر
- و بسیاری موارد دیگر
حجم ESP32 EEPROM
با اینکه کتابخانه ESP32 EEPROM از حافظه FLASH داخلی 4MB استفاده میکند، اندازه مجاز EEPROM برای ESP32 به صورت تئوری 20kB است. این مقدار مطابق نقشه حافظه بخشهای FLASH ESP32 است.

همانطور که در جدول نقشه حافظه مشاهده میکنید، بخش NVS که توسط کتابخانه EEPROM استفاده میشود، حجمی برابر با 0x5000 بایت دارد که معادل 20kB است. این به صورت نظری، حداکثر فضای حافظه مجاز برای ذخیره داده در NVS است.
پس از آزمایش، مشخص شد که نمیتوان در هیچ مکانی پس از 11kB نوشت یا خواند که بیشترین حجم قابل استفاده برای ذخیره داده با کتابخانه EEPROM است. این مقدار با جدول نقشه حافظه ESP32 در مخزن Arduino مطابقت ندارد، اما بهتر از مقالات دیگری است که حداکثر EEPROM ESP32 را 512 بایت یا 4kB ذکر کردهاند.
کنجکاوی باعث شد یک برد ESP32 دیگر مشابه برد اول بیاورم و همان آزمایشها را تکرار کنم. نتیجه: میتوانستم به راحتی و به طور مداوم از مکانهای حافظه تا حدود 12kB-13kB بخوانم و بنویسم. محدودیت کمی کمتر از 13kB بود، و کد و تنظیمات آزمایش دقیقاً یکسان بودند.
در نهایت، نتیجه حدود 11kB شد که فضای زیادی برای اکثر برنامههاست. این مقدار نسبتاً مناسب است، مخصوصاً با توجه به اینکه پیادهسازی جدیدتر کتابخانه Preferences حداکثر 20kB را پشتیبانی میکند.
چرخههای نوشتن در حافظه های ESP32
به طور کلی، حافظههای EEPROM معمولاً 10 برابر چرخههای نوشتن/پاککردن بیشتری نسبت به FLASH دارند. یک EEPROM معمولی میتواند بین 100,000 تا 1,000,000 چرخه نوشتن/پاککردن داشته باشد، در حالی که حافظه FLASH معمولاً بین 10,000 تا 100,000 چرخه دارد.
از آنجایی که ESP32 حافظه EEPROM ندارد و در واقع از حافظه FLASH استفاده میکند، انتظار داریم که حدود 10,000 چرخه نوشتن برای هر مکان حافظه FLASH داشته باشیم. به عبارت دیگر، اگر یک بایت داده را مرتباً به همان آدرس حافظه FLASH بنویسید، تنها بعد از 10,000 نوشتن، آن مکان حافظه بهطور دائمی آسیب میبیند.
اگر بارها و بارها به همان آدرسهای حافظه بنویسید، نهایتاً آسیب دائمی خواهد دید و این اتفاق میتواند سریع رخ دهد اگر توجهی به آن نداشته باشید.
کتابخانه ESP32 EEPROM
بزرگترین مزیت استفاده از کتابخانه ESP32 EEPROM برای ذخیره داده در حافظه FLASH این است که بسیار شبیه به کتابخانه EEPROM آردوینو است که اکثر ما با آن آشنا هستیم.
با استفاده از کتابخانه ESP32 EEPROM، شما میتوانید تا 11kB حافظه برای ذخیره داده در FLASH استفاده کنید. به عبارت دیگر، شما 11264 آدرس جداگانه برای ذخیره داده دارید. هر مکان حافظه یک بایت است و میتواند داده 8 بیتی (مقدار بین 0 تا 255) را ذخیره کند.
نوشتن در EEPROM
برای استفاده از کتابخانه ESP32 EEPROM، ابتدا باید فایل هدر EEPROM.h را اضافه کنید.
#include <EEPROM.h>
سپس باید حداکثر اندازه حافظه مورد نیاز برای ذخیره داده در EEPROM را مشخص کنید. اگر دقیقاً میدانید چند بایت نیاز دارید، از همان به عنوان محدودیت استفاده کنید، در غیر این صورت، کمی بیشتر اختصاص دهید تا بافر ایمنی داشته باشید.
#define EEPROM_SIZE 1 // این 1 بایت است
بعد، باید با فراخوانی تابع begin(EEPROM_SIZE) حافظه را راهاندازی کنید. این تابع اندازه حافظه EEPROM که قبلاً تعریف کردید را به عنوان آرگومان میگیرد. اکنون حافظه لازم برای ذخیره داده در ESP32 NVS (FLASH) اختصاص یافته است.
void setup()
{
...
EEPROM.begin(EEPROM_SIZE);
}
اکنون میتوانید از EEPROM برای نوشتن داده با تابع EEPROM.write(address, data) استفاده کنید. این تابع دو آرگومان میگیرد: آدرس و داده. آدرس میتواند هر مکان داخل محدوده حافظه تعریفشده باشد و داده یک مقدار 8 بیتی (0 تا 255) است.
EEPROM.write(address, data);
با توجه به اینکه کتابخانه EEPROM بر پایه درایور Flash NVS است و در واقع حافظه Flash است، آدرسدهی تکبایتی پشتیبانی نمیشود. معمولاً EEPROMها قابلیت خواندن/نوشتن تکآدرس را دارند، اما حافظههای Flash به صورت صفحهای آدرسدهی میشوند. یک صفحه معمولی Flash، 64 بایت است. بنابراین، برای تعویض دو بایت، نیاز است که کل صفحه خوانده، اصلاح و دوباره نوشته شود.
کتابخانه EEPROM این موضوع را با ذخیره داده در بافر هنگام فراخوانی write() حل میکند. دادهها تا زمانی که تابع EEPROM.commit() فراخوانی نشود، به حافظه FLASH نوشته نمیشوند.
EEPROM.commit();
خواندن از EEPROM
برای خواندن یک بایت از FLASH، از تابع EEPROM.read(address) استفاده کنید که آدرس مکان حافظه مورد نظر را میگیرد.
data = EEPROM.read(address);
نوشتن و خواندن رشته
برای نوشتن یک رشته در ESP32 EEPROM، از تابع زیر استفاده کنید:
EEPROM.writeString(address, &myString);
برای خواندن یک رشته از EEPROM، از تابع زیر استفاده کنید:
String myStr = EEPROM.readString(address);
نوشتن و خواندن اعداد اعشاری (Float)
برای نوشتن یا خواندن یک متغیر Float در حافظه EEPROM، نیازی به بررسی تکبایتی حافظه نیست. فقط از توابع زیر استفاده کنید:
EEPROM.writeFloat(address, myFloat); myFloat = EEPROM.readFloat(address);
خواندن و نوشتن سایر انواع داده
کتابخانه ESP32 EEPROM توابعی برای نوشتن و خواندن تمام انواع داده شناختهشده فراهم کرده است:
نوشتن:
size_t writeByte(int address, uint8_t value); size_t writeChar(int address, int8_t value); size_t writeUChar(int address, uint8_t value); size_t writeShort(int address, int16_t value); size_t writeUShort(int address, uint16_t value); size_t writeInt(int address, int32_t value); size_t writeUInt(int address, uint32_t value); size_t writeLong(int address, int32_t value); size_t writeULong(int address, uint32_t value); size_t writeLong64(int address, int64_t value); size_t writeULong64(int address, uint64_t value); size_t writeFloat(int address, float_t value); size_t writeDouble(int address, double_t value); size_t writeBool(int address, bool value); size_t writeString(int address, const char* value); size_t writeString(int address, String value); size_t writeBytes(int address, const void* value, size_t len);
خواندن:
uint8_t readByte(int address); int8_t readChar(int address); uint8_t readUChar(int address); int16_t readShort(int address); uint16_t readUShort(int address); int32_t readInt(int address); uint32_t readUInt(int address); int32_t readLong(int address); uint32_t readULong(int address); int64_t readLong64(int address); uint64_t readULong64(int address); float_t readFloat(int address); double_t readDouble(int address); bool readBool(int address); size_t readString(int address, char* value, size_t maxLen); String readString(int address); size_t readBytes(int address, void * value, size_t maxLen);
خواندن و نوشتن تمام انواع داده
کتابخانه ESP32 EEPROM دو تابع جالب put() و get() ارائه میدهد که میتوانید برای نوشتن و خواندن هر نوع داده شناختهشده یا حتی دادههای تعریفشده توسط کاربر مانند ساختارها استفاده کنید:
EEPROM.put(address, myData); EEPROM.get(address, myData);
مثال ESP32 EEPROM (Arduino IDE)
در این مثال، حافظه EEPROM با ذخیره آخرین وضعیت LED آزمایش میشود. LED توسط یک دکمه فشار کنترل میشود و هر زمان که وضعیت آن تغییر کند، آخرین وضعیت در EEPROM ذخیره میشود. پس از ریست برد ESP32، آخرین وضعیت ذخیرهشده باید بازیابی شود.
در این بخش نحوه اتصال دکمه ورودی و LED خروجی نمایش داده شده است.

کد کامل برای این مثال به شرح زیر است:
#include <EEPROM.h>
#define EEPROM_SIZE 1
#define LED_GPIO 25
#define BTN_GPIO 2
int ledState = LOW;
int btnState = LOW;
int lastBtnState = LOW;
int lastDebounceTime = 0;
int debounceDelay = 50;
int eeprom_address = 0;
void setup() {
Serial.begin(115200);
pinMode(LED_GPIO, OUTPUT);
pinMode(BTN_GPIO, INPUT);
EEPROM.begin(EEPROM_SIZE);
ledState = EEPROM.read(eeprom_address);
digitalWrite(LED_GPIO, ledState);
}
void loop() {
int reading = digitalRead(BTN_GPIO);
if (reading != lastBtnState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != btnState) {
btnState = reading;
if (btnState == HIGH) {
ledState = !ledState;
digitalWrite(LED_GPIO, ledState);
EEPROM.write(eeprom_address, ledState);
EEPROM.commit();
Serial.println("State Changed & Saved To FLASH!");
}
}
}
lastBtnState = reading;
} توضیح کد
این مثال به سادگی پین ورودی دکمه را میخواند و با استفاده از منطق debounce مطمئن میشود که نویز دریافت نمیکند. اگر دکمه واقعی فشار داده شود، LED تغییر وضعیت میدهد و وضعیت فعلی LED در حافظه FLASH ذخیره میشود.
ابتدا کتابخانه EEPROM.h اضافه میشود:
#include <EEPROM.h>
سپس اندازه حافظه مورد نیاز EEPROM (1 بایت) و پینهای GPIO تعریف میشوند:
#define EEPROM_SIZE 1 #define LED_GPIO 25 #define BTN_GPIO 2
متغیرهای سراسری برای ذخیره وضعیت LED و دکمه و انجام debounce تعریف شدهاند:
int ledState = LOW; int btnState = LOW; int lastBtnState = LOW; int lastDebounceTime = 0; int debounceDelay = 50; int eeprom_address = 0;
تابع setup()
در تابع setup()، ارتباط سریال برای دیباگ، حالت پینها و EEPROM با اندازه تعریفشده راهاندازی میشوند. همچنین آخرین وضعیت ذخیرهشده LED از حافظه FLASH خوانده و روی خروجی LED اعمال میشود.
Serial.begin(115200); pinMode(LED_GPIO, OUTPUT); pinMode(BTN_GPIO, INPUT); EEPROM.begin(EEPROM_SIZE); ledState = EEPROM.read(eeprom_address); digitalWrite(LED_GPIO, ledState);
تابع loop()
در loop() بیشتر عملیات خواندن دکمه و debounce انجام میشود. زمانی که دکمه واقعی فشار داده شده و نویزی نباشد، LED تغییر وضعیت میدهد و وضعیت فعلی LED در EEPROM ذخیره میشود.
int reading = digitalRead(BTN_GPIO);
if (reading != lastBtnState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != btnState) {
btnState = reading;
if (btnState == HIGH) {
ledState = !ledState;
digitalWrite(LED_GPIO, ledState);
EEPROM.write(eeprom_address, ledState);
EEPROM.commit();
Serial.println("State Changed & Saved To FLASH!");
}
}
}
lastBtnState = reading;
پس از اجرای این مثال، ESP32 وضعیت آخرین LED را بعد از هر بار ریست به یاد میآورد. این سادهترین کاربرد حافظههای غیر فرار است.
موارد موجود در فایل : سورس کامل
برای دانلود فایل ها باید حساب کاربری داشته باشید ثبت نام / ورود








