Как хранить данные в Arduino
У плат семейства плат Arduino есть несколько видов памяти. Во-первых, это статическое ОЗУ, которая используется для хранения переменных в процессе выполнения программы. Во-вторых, это флеш-память, в которой хранятся написанные вами скетчи. И в-третьих, это EEPROM, которую можно использовать для постоянного хранения информации.
Виды памяти Arduino:
- Статическое ОЗУ (оперативное запоминающее устройство) – энергозависимая память, которая используется для хранения переменных в процессе выполнения программы. После перезагрузки Arduino теряет информацию.
- Флеш-память – энергоНЕзависимая память, в ней хранятся написанные скетчи.
- EEPROM – дословно переводится как «электрически стираемая программируемая память» – энергоНЕзависимая память, которую можно использовать для постоянного хранения пользовательской информации. В неё можно записывать информацию во время работы Arduino и читать по мере необходимости. Эту память мы и рассмотрим сейчас.
Инструкция по использованию EEPROM в Arduino
1Описание памяти EEPROM
EEPROM означает Electrically Erasable Programmable Read-Only Memory, т.е. электрически стираемое перепрограммируемое постоянное запоминающее устройство. Данные в этой памяти могут храниться десятки лет после отключения питания. Количество циклов перезаписи – порядка нескольких миллионов раз.
Количество EEPROM памяти в Arduino довольно ограниченно:
- для плат, основанных на микроконтроллере ATmega328 (например, Arduino UNO и Nano), количество памяти составляет 1 кБ,
- для плат на ATmega168 и ATmega8 – 512 байт,
- на ATmega2560 и ATmega1280 – 4 кБ.
2Библиотека EEPROM
Для работы с EEPROM для Arduino написана специальная библиотека, которая входит в Arduino IDE по умолчанию. Библиотека содержит следующие возможности.
Функция | Назначение |
---|---|
read(address) | считывает 1 байт из EEPROM; address – адрес, откуда считываются данные (ячейка, начиная с 0); |
write(address, value) | записывает в память значение value (1 байт, число от 0 до 255) по адресу address; |
update(address, value) | заменяет значение value по адресу address, если её старое содержимое отличается от нового; |
get(address, data) | считывает данные data указанного типа из памяти по адресу address; |
put(address, data) | записывает данные data указанного типа в память по адресу address; |
EEPROM[address] | позволяет использовать идентификатор "EEPROM" как массив, чтобы записывать данные в память и считывать их из памяти. |
Чтобы задействовать библиотеку в скетче, подключаем её директивой #include EEPROM.h.
3Запись целых чисел в EEPROM
Давайте запишем в память EEPROM два целых числа, а затем прочитаем их из EEPROM и выведем в последовательный порт. С числами от 0 до 255 проблем нет, они занимают как раз 1 байт памяти и с помощью функции EEPROM.write() записываются в нужную ячейку.
Если число больше, чем 255, то с помощью операторов highByte() и lowByte() его нужно делить на байты и записывать каждый байт в свою ячейку. Максимальное число при этом – 65536 (или 216).
#include <EEPROM.h> // подключаем библиотеку EEPROM void setup() { int smallNum = 123; // целое число от 0 до 255 EEPROM.write(0, smallNum); // запись числа в ячейку 0 int bigNum = 789; // число > 255 разбиваем на 2 байта (макс. 65536) byte hi = highByte(bigNum); // старший байт byte low = lowByte(bigNum); // младший байт EEPROM.write(1, hi); // записываем в ячейку 1 старший байт EEPROM.write(2, low); // записываем в ячейку 2 младший байт Serial.begin(9600); // инициализация послед. порта } void loop() { for (int addr=0; addr<1024; addr++) { // для всех ячеек памяти (для Arduino UNO 1024) byte val = EEPROM.read(addr); // считываем 1 байт по адресу ячейки Serial.print(addr); // выводим адрес в послед. порт Serial.print("\t"); // табуляция Serial.println(val); // выводим значение в послед. порт } delay(60000); // задержка 1 мин }
Смотрите, монитор последовательного порта в ячейку 0 просто выводит число, меньшее, чем 255. В ячейках 1 и 2 хранится большое число 789. При этом ячейка 1 хранит множитель переполнения 3, а ячейка 2 – недостающее число 21 (т.е. 789 = 3×256 + 21).
Чтобы заново «собрать» большое число, разобранное на байты, есть функция word(): int val = word(hi, low), где "hi" и "low" – это значения старшего и младшего байтов числа "val".
Во всех остальных ячейках, которые не были нами ни разу записаны, хранятся числа 255.
4Запись чисел с плавающей запятой и строк в EEPROM
Для записи чисел с плавающей запятой и строк нужно использовать метод EEPROM.put(), а для чтения – EEPROM.get().
#include <EEPROM.h> // подключаем библиотеку void setup() { int addr = 0; // адрес float f = 3.1415926f; // число с плавающей точкой (типа float) EEPROM.put(addr, f); // записали число f по адресу addr addr += sizeof(float); // вычисляем следующую свободную ячейку памяти char name[20] = "Hello, SolTau.ru!"; // создаём массив символов EEPROM.put(addr, name); // записываем массив в EEPROM Serial.begin(9600); // инициализация послед. порта } void loop() { for (int addr=0; addr<1024; addr++) { // для всех ячеек памяти (1024Б=1кБ) Serial.print(addr); // выводим адрес в послед. порт Serial.print("\t"); // табуляция float f; // переменная для хранения значений типа float EEPROM.get(addr, f); // получаем значение типа float по адресу addr Serial.print(f, 5); // выводим с точностью 5 знаков после запятой Serial.print("\t"); // табуляция char c[20]; // переменная для хранения массива из 20 символов EEPROM.get(addr, c); // считываем массив символов по адресу addr Serial.println(c); // выводим массив в порт } delay(60000); // ждём 1 минуту }
В процедуре setup() сначала запишем число с плавающей запятой "f". Затем сдвинемся на количество ячеек памяти, которое занимает тип "float", и запишем строку символов "char" ёмкостью 20 ячеек.
В процедуре loop() будем считывать все ячейки памяти и пытаться расшифровать их сначала как тип "float", а затем как тип "char", и выводить результат в последовательный порт.
Видно, что значение в ячейках с 0 по 3 правильно определилось как число с плавающей точкой, а начиная с 4-ой – как строка.
Появляющиеся значения ovf (переполнение) и nan (не число) говорят о том, что значение в ячейке памяти по этому адресу не может быть корректно преобразовано в число с плавающей точкой. Если вы точно знаете, какого типа данные какие ячейки памяти занимают, то у вас не будет возникать проблем.
5Работа с EEPROM как с массивом
Очень удобная возможность – обращение к ячейкам памяти как к элементам массива EEPROM. В данном скетче в процедуре setup() мы сначала запишем данные в 4 первых байта, а в процедуре loop() ежеминутно будем считывать данные из всех ячеек и выводить их в последовательный порт.
#include <EEPROM.h> void setup() { EEPROM[0] = 11; // записываем 1-ю ячейку EEPROM[1] = 121; // записываем 2-ю ячейку EEPROM[2] = 141; // записываем 3-ю ячейку EEPROM[3] = 236; // записываем 4-ю ячейку Serial.begin(9600); } void loop() { for (int addr=0; addr<1024; addr++) { Serial.print(addr); Serial.print("\t"); int n = EEPROM[addr]; // считываем ячейку по адресу addr Serial.println(n); // выводим в порт } delay(60000); }