EEPROM в Arduino: когда хранить нужно немного

от автора

При разработке собственного устройства очень часто возникает необходимость сохранять и считывать данные из энергонезависимой памяти. Конечно, для этих целей можно использовать SD карты. Например, к платам семейства Arduino можно легко подключить адаптер для работы с microSD. Однако, часто нам не нужно сохранять такой большой объем данных. Например, если мы делаем умный будильник, где время срабатывания каждый день может быть разным, то эти значения неплохо бы сохранить в EEPROM. Или, если доступ к устройству осуществляется по паролю, то хэш этого пароля (не сам пароль!) неплохо бы тоже сохранить в EEPROM. Если зашьем пароль в прошивку, у нас не будет возможности его поменять, а это не очень хорошо с точки зрения безопасности.

В общем, есть масса ситуаций, когда нужно хранить небольшой объем информации и тогда лучше всего использовать энергонезависимую память на самом микроконтроллере.

В этой статье мы будем говорить об Arduino; но на самом деле на многих МК есть энергонезависимая память, например, на моем любимом МК ATtiny13, материалов о котором на Хабре пока не так много (но я надеюсь это в скором времени исправить).

Какая память бывает

Для того, чтобы избежать путаницы и подмены понятий, предлагаю разобраться с тем, какая память бывает на микроконтроллерах. В МК есть три вида памяти:

  • Flash‑память (ROM) — это тоже энергонезависимая память, в которой хранится прошивка контроллера; то, что там записано, нельзя изменить в процессе работы программы.

  • ОЗУ (RAM) — это оперативная память, используется для размещения переменных, массивов и других данных, при отключении питания эта информация будет потеряна.

  • И наконец, EEPROM — используется для хранения пользовательских данных, содержимое этой памяти можно модифицировать в процессе работы программы.

Вот характеристики объемов памяти для разных моделей Arduino:

Единственная поправка к представленным значениям Flash заключается в том, что из них нужно вычесть как минимум 4Кб на загрузчик Bootloader. Благодаря этому загрузчику мы можем заливать прошивки на плату через USB порт без помощи программатора.

Но вернемся к EEPROM. Как видно из таблицы, у нас будет как минимум килобайт памяти — давайте посмотрим, как мы можем их использовать.

Запись и чтение

Работу с энергонезависимой памятью в Arduino мы начнем с рассмотрения примера записи в память. Для выполнения любых операций с EEPROM необходимо подключить соответствующую библиотеку EEPROM.h. Ниже мы осуществим запись значения в память двумя способами:

#include <EEPROM.h>  void setup() {   Serial.begin(9600);   while (!Serial) {     // ждем подключения   }    byte i= 1;  // Значение, которое будем записывать в EEPROM.   int eeAddress = 0;   //адрес в памяти    //Запишем значение в память    EEPROM.write(eeAddress, i);   eeAddress = eeAddress+1; //Увеличим адрес в памяти на размер сохраненных данных    Serial.println("Запись успешно произведена!");   //Запишем значение в память    EEPROM.update(eeAddress, i);   Serial.println("Запись успешно произведена!");  }     void loop() {  } 

В примере мы использовали две функции EEPROM.write() и EEPROM.update() для записи значений в память. Чем они отличаются? Первая функция выполнить запись в ячейку в любом случае, независимо от того, изменилось ли значение, которое мы хотим записать или оно осталось прежним. Вторая функция update в соответствии со своим названием запишет выполнит операцию записи только в том случае, если записываемое значение отличается от того, котрое уже есть в ячейке. Так в нашем примере кода в памяти был 0 и update выполнит запись, поместив в память 1. Если бы в памяти на этом месте уже была 1 , то операция записи бы не выполнялась.

Дело в том, что количество циклов записи в EEPROM не бесконечно. В сети можно найти описания того, как физически работает энергонезависимая память. Но суть в том, что в процессе выполнения операций записи элементы памяти изнашиваются. В среднем производители обычно обещают порядка 100 000 циклов записи (по данным https://docs.arduino.cc/). Поэтому я бы рекомендовал по возможности при записи в EEPROM использовать функцию update, для снижения «износа» модуля памяти.

Теперь поговорим о том, как можно прочитать содержимое энергонезависимой памяти. Здесь все проще: для чтения используется функция EEPROM.read(), которой в качестве аргумента передается номер ячейки памяти, из которой нужно прочитать значение.

#include <EEPROM.h>  // начинаем чтение с нулевого адреса  int address = 0; byte value;  void setup() {   Serial.begin(9600);   while (!Serial) {      // ждем подключения    } }     void loop() {    // читаем байты из текущей ячейки памяти в цикле    value = EEPROM.read(address);  // выводим номер ячейки и значение в десятичном виде    Serial.print(address);   Serial.print("\t");   Serial.print(value, DEC);   Serial.println();   address = address + 1; //если дошли до конца памяти, возвращаемся в начало   if (address == EEPROM.length()) {     address = 0;   }   delay(500); }

Для того, чтобы ваш скетч (прошивка) была универсальной и могла работать на разных моделях Arduino, используйте функцию EEPROM.length() для выяснения размера памяти. Это лучше, чем жестко приколачивать значение в коде.

Посмотрим еще два полезных метода — это put и get. Они аналогичны операциям записи и чтения, но количество записываемых или читаемых байт зависит от типа данных или пользовательской структуры записываемой переменной. Put работает по принципу update, то есть записывает данные только в случае изменения.

#include <EEPROM.h> struct MyObject {   float field1;   byte field2;   char name[10]; };  void setup() {   Serial.begin(9600);   while (!Serial) {  }    float f = 123.456f;     int eeAddress = 0;  .    //Пишем float в первую ячейку    EEPROM.put(eeAddress, f);    Serial.println("Запись успешно произведена!");    //Создаем свой объект    MyObject customVar = {     3.14f,     65,     "Working!"   };    eeAddress += sizeof(float); //Увеличиваем адрес   EEPROM.put(eeAddress, customVar);    Serial.print("Запись успешно произведена!");  }  void loop() {  }

Как видно, нам не надо думать о том, сколько байт в памяти занимает тот или иной тип данных.

Теперь давайте посмотрим, как осуществляется чтения данных различных типов из EEPROM.

#include <EEPROM.h>  void setup() {   float f = 0.00f;     int eeAddress = 0;    Serial.begin(9600);   while (!Serial) {    }    Serial.print("Читаем из EEPROM: ");   EEPROM.get(eeAddress, f);   Serial.println(f, 3);    //Можем получить 'ovf, nan' если значение f не типа float.   secondTest(); //Run the next test. }     struct MyObject {   float field1;   byte field2;   char name[10]; };  void secondTest() {    int eeAddress = sizeof(float);    MyObject customVar; //готовим переменную для чтения MyObject   EEPROM.get(eeAddress, customVar);   Serial.println("Читаем объекты из EEPROM: ");   Serial.println(customVar.field1);   Serial.println(customVar.field2);   Serial.println(customVar.name);  }  void loop() {  }

При выполнении операций чтения важно понимать, данные какого типа мы хотим получить для того, чтобы не столкнуться с сюрпризами, когда мы получаем совсем не то, что ожидали из‑за неверного типа данных при чтении.

Собственно, представленные функции являются достаточными для работы с EEPROM. Так, если нужно очистить память, то можно с помощью update записать 0 во все ячейки. Для поиска значения в памяти можно воспользоваться read и так далее.

Заключение

Мы рассмотрели базовые функции для работы с энергонезависимой памятью в Arduino. С их помощью можно реализовать необходимый для работы с памятью функционал и использовать в своих устройствах.


В декабре в Otus в рамках онлайн-курса «Электроника и электротехника» пройдут два открытых урока, на которые приглашаются все желающие:

  • 9 декабря: Отладка встраиваемого программного обеспечения в симуляторах Proteus и NI Multisim. Записаться

  • 17 декабря: Автоматическая регулировка усиления каскадов на операционных усилителях и транзисторах. Записаться


ссылка на оригинал статьи https://habr.com/ru/articles/862452/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *