Ардуино: память EEPROM

Как известно, сердцем Ардуино Уно является микроконтроллер фирмы 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 раз, произойдет переполнение переменной. В данном случае это не страшно, просто счетчик автоматически сбросится в ноль.

Работа с EEPROM на Ардуино

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

Работа с EEPROM на Ардуино

Участок кода в конце функции 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 на Ардуино

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

Работа с EEPROM на Ардуино

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

Надо заметить, что put и get умеют работать не только с естественными типами int, float, long, char, byte, но и со структурами. Для оценки их размера лучше использовать упомянутую функцию sizeof.

Дальше вы уже сами. Успехов!


Изменено:

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

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

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.