Как известно, сердцем Ардуино Уно является микроконтроллер фирмы Atmel — Atmega328. И как и любой микроконтроллер, Atmega328 имеет в своем составе несколько видов памяти. Каждый тип имеет свои особенности и свое предназначение.
Flash — энергонезависимая память, предназначенная для хранения программы. Эта память больше подходит для чтения данных, чем для их записи. Флэш-память в Atmega328 имеет 10 тысяч циклов перезаписи. Предполагается, что запись в эту память ведется редко, только во время создания устройства или при обновлении прошивки.
SRAM — оперативная энергозависимая память, которая нужна для хранения переменных во время работы устройства. Она очень быстрая, но полностью стирается при выключении питания.
EEPROM — энергонезависимая память, предназначенная для журналирования и хранения разного рода настроек. В отличие от Флэш памяти, она имеет 100 тысяч циклов перезаписи.
В микроконтроллере также есть регистры — это тоже память, сверхбыстрая энергозависимая, которая используется при выполнении арифметических операций, при работе с портами ввода/вывода и прочее.
Сколько памяти у Ардуино?
Размер памяти у микроконтроллера Atmega328:
- Flash: 32 кб;
- SRAM: 2 кб;
- EEPROM: 1 кб;
- регистры: в сумме 256 байт.
Для сравнения, у Ардуино Мега памяти гораздо больше:
- Flash: 256 кб;
- SRAM: 8 кб;
- EEPROM: 4 кб.
Как видим, EEPROM в Arduino Уно всего 1 килобайт. Что можно хранить в таком объеме? Предположим, мы собираемся хранить значение ручек регулировки, подключенных к аналоговым входам Ардуино. Каждая такая ручка дает число от 0 до 1024 и требует 2 байта для хранения (по факту 1 байт и 2 бита, но для простоты используем 2б ), получается в EEPROM мы можем хранить значения 512 ручек настройки!
В общем, для большинства DIY проектов 1 кб EEPROM более чем достаточно. На этом уроке мы научимся записывать данные в EEPROM и считывать их оттуда.
Программа
Напишем программу, которая будет хранить значение счетчика нажатий кнопки. То есть каждый раз, когда мы нажимаем кнопку, переменная счетчика увеличивается на единицу и сохраняет свое значение в EEPROM. Чтобы проверить программу нам понадобится собрать стенд из урока про кнопки.
Для работы с EEPROM используется стандартная библиотека EEPROM.h. Запись и чтения в эту память ведется побайтово. Это значит, что за раз мы можем записать и считать число от 0 до 255. Запись одного байта осуществляется с помощью функции write:
EEPROM.write( адрес, значение );
Здесь адрес — индекс ячейки памяти, который может принимать значение от 0 до 1023. Максимальный адрес можно легко вычислить зная размер EEPROM у конкретного микроконтроллера: 1кб = 1024 байта, значит максимальный адрес 1024 — 1. Либо можно использовать функцию length — размер памяти, тогда максимальный адрес будет EEPROM.length() — 1.
Второй аргумент значение — это число, которое мы хотим сохранить в ячейке с индексом «адрес«.
Чтение байта осуществляется с помощью функции read:
EEPROM.read( адрес );
Итак, пишем программу.
#include <EEPROM.h> // обязательно подключаем библиотеку
const byte btnPin = 2; // это контакт, к которому подключена кнопка
byte counter = 0; // переменная счетчика
int addr = 0; // адрес ячейки для записи
void setup() {
Serial.begin(9600); // включаем порт для отладки
pinMode(btnPin, INPUT); // активируем контакт за ввод
// сразу после запуска программы считываем
// число из EEPROM и выводим его в COM-порт
byte v = EEPROM.read(addr); // считываем число из памяти
Serial.print("saved counter = ");
Serial.println(v); // выводим считанное в COM-порт
}
void loop() {
// если кнопка нажата, выполняем
if( digitalRead(btnPin) == HIGH ){
counter++; // увеличиваем счетчик на единицу
EEPROM.write(addr, counter); // записываем счетчик
Serial.print("counter = ");
Serial.println(counter); // выводим текущее значение счетчика
delay(200); // пауза защитит от дребезга контактов
}
}
Загружаем программу на Ардуино, открываем монитор последовательного порта и нажимаем кнопку несколько раз. Нетрудно догадаться, что если нажать больше 255 раз, произойдет переполнение переменной. В данном случае это не страшно, просто счетчик автоматически сбросится в ноль.

Теперь отключим плату Ардуино от питания, затем снова включим и откроем монитор.

Участок кода в конце функции setup считал байт из нулевой ячейки EEPROM памяти и вывел его в порт. Работает!
Запись и чтение int и float
Что делать, если нам нужно сохранить переменную типа integer, long или float? Для этих целей в библиотеке EEPROM есть две специальные функции: put и get. Первая позволяет записать в память переменную любого типа, вторая — считать из памяти.
В отличие от однобайтовых функций write и read, в случае использования put и get легко запутаться в адресации. Так, при записи целого типа (int) с помощью put мы займем два байта памяти. И чтобы по-очереди записать два числа нужно будет прибавить к адресу двойку:
int addr = 0; // начальный адрес int v = 16555; // какое-то большое число EEPROM.put(addr, v); // записываем в ячейки 0 и 1 addr = addr + 2; EEPROM.put(addr, v);// записываем в ячейки 2 и 3
Если решим сохранить переменную типа long, то уже потребуется 4 байта. В общем, нужно всегда знать размер переменной, которую хотим разместить в EEPROM, или уметь его вычислять функцией sizeof.
Хорошим примером работы функций put и get послужит сохранение в EEPROM показаний поворотного потенциометра, подключенного к аналоговому входу Ардуино. Предположим, у нас есть прибор с одним потенциометром, с помощью которого настраиваются три коэффициента какого-нибудь PID-регулятора. Как нам уже известно, плата Ардуино Уно имеет 10-разрядный АЦП. Для чтения АЦП мы используем функцию analogWrite, которая вернет целый тип int — а это два байта.
Данная программа постоянно считывает значение с контакта A0 и при нажатии кнопки сохраняет его в EEPROM. После первого нажатия, значение сохраняется в ячейки с адресами 0,1. После второго — 2,3. После третьего — 4,5.
#include <EEPROM.h>
const byte btnPin = 2;
const byte potPin = A0; // к этому контакту подключен потенциометр
byte potIdx = 0;
void setup() {
Serial.begin(9600);
pinMode(btnPin, INPUT);
pinMode(potPin, INPUT);
// считываем значения из EEPROM в цикле
// сначала по адресу 0, потом 2, потом 4
// i - итератор, он же номер потенциометра
Serial.println("Read data from EEPROM");
for( byte i=0; i<3; i++ ){
int v;
EEPROM.get(i*2, v);
Serial.print("value of pot #");
Serial.print(i+1);
Serial.print(" = ");
Serial.println(v);
}
delay(3000);
// пауза 3 секунды
}
void loop() {
int v = analogRead(potPin);
Serial.print("v = ");
Serial.println(v);
if( digitalRead(btnPin) == HIGH ){
EEPROM.put(potIdx*2, v);
Serial.println("Write data to EEPROM");
Serial.print("value of pot #");
Serial.print(potIdx+1);
Serial.print(" = ");
Serial.println(v);
// если мы добрались до третьей ручки
// обнуляем счетчик ручек
if( potIdx == 2 )
potIdx = 0;
else
potIdx++;
delay(1000);
// пауза 1 секунда
}
}
После запуска программы и вывода ранее сохраненных в EEPROM значений в последовательный порт начнут сыпаться числа. Крутим потенциометр до нужного нам значения и жмем кнопку. Программа покажет нам сохраняемое значение и подождет 1 секунду.

Настроив таким образом все три числа, отключим Ардуино от компьютера, а затем снова включим и откроем монитор последовательного порта.

Готово! Теперь можно не настраивать устройства каждый раз после подачи питания, а использовать EEPROM.
Надо заметить, что put и get умеют работать не только с естественными типами int, float, long, char, byte, но и со структурами. Для оценки их размера лучше использовать упомянутую функцию sizeof.
Дальше вы уже сами. Успехов!
А можно так же про еепром 2402 -2404 внешнюю рассказать, спасибо!!!