Не всегда есть возможность полностью отладить устройство и прошить с нужными настройками. Это может быть связано с разными факторами: иногда подстройка производится только после установки девайса, иногда просто со временем нужно внести корректировки в работу и т.д. Для таких случаев можно использовать подстроечные резисторы, но это не всегда удобно, например, если придется менять сразу десяток конфигураций. В таких случаях можно воспользоваться, встроенной в arduino, энергонезависимой памятью – EEPROM. Ей не страшно отсутствие питания или перезагрузки устройства, данные останутся в памяти.
Я как-то писал статью про подстроечный резистор и обещал продолжение. Пришло время сдержать обещание и немного расширить функционал из старой статьи.
Энергонезависимая память EEPROM и arduino
EEPROM – это запоминающее устройство, которое позволяет хранить данные десятки лет с отключенным питанием. Перезаписывать данные можно довольно много раз, но не бесконечно. В одних источниках пишут, что EEPROM позволяет осуществлять до нескольких миллионов циклов перезаписи, а где-то пишут, что не больше ста тысяч. В любом случае, этого количества с лихвой должно хватить, если использовать с умом.
Arduino уже имеет встроенную EEPROM и управлять ею очень просто. В зависимости от версии arduino, объем памяти отличается:
— Платы с микроконтроллерами ATmega8 и ATmega168 используют EEPROM с объемом памяти в 512 байт.
— Платы с ATmega328 используют EEPROM с объемом 1 кб
— Платы на ATmega1280 и ATmega2560 имеют EEPROM с объемом в 4 кб
Для хранения настроек и других не больших массивов данных, даже 512 байт будет достаточно. Но если вы решите хранить какие-то огромные значения, не помещающиеся даже в 4 кб, то всегда можно купить дополнительную микросхему EEPROM и подключить ее к arduino. Такие микросхемы способны хранить, куда больший объем, чем встроенные. Главное при выборе обращать внимание на совместимость с arduino и способность работать с 8-битным микроконтроллером. Например, можно использовать 24LC256.
Подстроечный резистор и EEPROM
Чтобы было понятней, как работает EEPROM и как ее использовать, попробуем реализовать небольшой пример. Подключим к ардуино подстроечный резистор, кнопку и пять светодиодов(не забыв про резисторы, чтобы не сжечь светодиоды). И напишем небольшой скетч: по нажатию на кнопку будем считывать значения с подстроечного резистора и конвертировать их в число от 0 до 4х. По повторному нажатию на кнопку, будем сохранять полученное значение в EEPROM. И уже в зависимости от записанного значения, зажжем соответствующее количество светодиодов. Если после перезагрузки или после отключения питания arduino, будет гореть нужное количество огоньков, значит все работает и эксперимент можно считать успешным.
Чтобы было понятней, ниже приведена схема подключения всех элементов.
Скетч записи и чтения данных из EEPROM
Для работы EEPROM в arduino IDE уже есть встроенная библиотека EEPROM.h, ее и будем использовать. Вся память разбита на ячейки по 1 байту, соответственно в каждую ячейку можно записать не больше одного байта, если хранить целые числа, то можно записывать значения от 0 до 255 без сложностей. Этим мы и займемся в скетче – используем одну ячейку памяти и сначала сохраним в нее число от 0 до 4, а потом прочитаем содержимое.
Если вам необходимо хранить значения, превышающие размер в 1 байт, то их придется разбивать по байтам и записывать в разные ячейки по частям.
Обратите внимание: по умолчанию в пустой EEPROM в каждой ячейке хранятся значения 255. Но делать проверку на пустоту с помощью сравнения с числом 255 не стоит. Поскольку не всегда можно полагаться на заводские настройки. И перед использованием лучше прогнать все ячейки в цикле и записать в них свои значения для обнуления, чтобы быть полностью уверенным в содержимом.
Далее остается только привести скетч с подробными комментариями:
#include <EEPROM.h> // библиотека для работы с EEPROM int pin_rezistor = A0; int value = 0; int count_leds = 0; // пин светодиода int ledPins[5] = {4, 5, 6, 7,8}; boolean editOn = false; // состояние светодиода // пин кнопки int btn_1 = 2; boolean lastBtn_1 = false; // предыдущее состояние кнопки boolean currentBtn_1 = false; // текущее состояние кнопки void setup() { // режимы работы пинов pinMode(pin_rezistor, INPUT); pinMode (btn_1, INPUT); for(int i=0; i<=4; i++){ pinMode (ledPins[i], OUTPUT); } } void loop() { if(editOn){ value = analogRead(pin_rezistor); count_leds = map(value, 0, 1000, 0, 4); }else{ count_leds = EEPROM_int_read(0); } // получение состояния кнопки currentBtn_1 = isBtnStatus(lastBtn_1); // если кнопка была нажата 5 миллисекунд и более if (lastBtn_1 == false && currentBtn_1 == true) { // меняем состояние светодиода editOn = !editOn; // если выключился режим настройки if(!editOn){ EEPROM.write(0, count_leds); } } // сохраняем состояние кнопки lastBtn_1 = currentBtn_1; // все гасим for(int i=0; i<=4; i++){ digitalWrite(ledPins[i], LOW); } // включаем нужное количество for(int i=0; i<=count_leds; i++){ digitalWrite(ledPins[i], HIGH); } delay(50); } // функция для определения состояния кнопки boolean isBtnStatus(boolean last) { // считываем состояние кнопки boolean current = digitalRead(btn_1); // сравниваем текущее состояние кнопки со старым if (last != current) { // ждем 5 миллисекунд delay(5); // считываем состояние кнопки еще раз current = digitalRead(btn_1); } // возвращаем состояние кнопки return current; } // чтение int EEPROM_int_read(int addr) { byte raw[2]; for(byte i = 0; i < 2; i++) raw[i] = EEPROM.read(addr+i); int &num = (int&)raw; return num; } // запись void EEPROM_int_write(int addr, int num) { byte raw[2]; (int&)raw = num; for(byte i = 0; i < 2; i++) EEPROM.write(addr+i, raw[i]); }
Послесловие
Я заказал себе в стране Великого Дракона десяток 24LC256, чтобы можно было поэкспериментировать с внешней EEPROM. Как посылка приедет и я «наиграюсь» с микросхемой, то постараюсь написать статью и поделиться опытом.
Почему всегда в примерах народ указывает нулевую ячейку памяти когда сам производитель рекомендует её использовать в крайних случаях? Примеры смотрят начинающие и надо им указывать на эти моменты.