Библиотека EEPROM
В микроконтроллере Ардуино есть EEPROM - память, в которой информация сохраняется даже после выключения устройства (подобно маленькому жесткому диску). Данная библиотека позволяет записывать и считывать информацию из этой памяти.
Объем памяти EEPROM различных микроконтроллеров, входящих в состав Ардуино, может отличаться: 1024 байта в ATmega328, 512 байт - в ATmega168 и ATmega8, 4 КБ (4096 байт) - в ATmega1280 и ATmega2560.
Микроконтроллеры ATmega имеют на борту энергонезависимую память EEPROM, не потеряющую записанные в нее данные даже после отключения питания. 512 байтов такой памяти несут ATmega8 и ATmega168, 1024 байта — ATmega328, 4096 байтов — Arduino Mega. Память типа EEPROM допускает несколько десятков тысяч циклов записи и стирания данных. Она может пригодиться для сохранения изменяющихся настроек при отключении питания от устройств Arduino. Для работы с этой памятью в составе Arduino IDE имеется удобная библиотека EEPROM.
Функции библиотек EEPROM
Библиотека EEPROM содержит две функции: чтения и записи в память данных.
Функция чтения EEPROM.read
Функция EEPROM.read считывает байт из энергонезависимой памяти EEPROM. Если байт до этого никогда не перезаписывался — вернет значение 255.
Синтаксис функции EEPROM.read: EEPROM.read(address)
Параметр: address — порядковый номер ячейки памяти для чтения от 0 до 512 (или 1024) (int);
Возвращаемое значение — байт, хранимый в ячейке памяти.
Пример считывания значения всех байтов энергонезависимой памяти EEPROM и вывода их в COM-порт представлен в примере.
#include <EEPROM.h>
// начальный адрес памяти EEPROM int address = 0;
byte value; void setup()
{
Serial.begin(9600);
}
void loop()
{
// считываем значение по текущему адресу EEPROM value = EEPROM.read(address);
Serial.print(address); Serial.print("\t"); Serial.print(value, DEC); Serial.println();
// устанавливаем следующую ячейку памяти address = address + 1;
// EEPROM содержит всего 512(1024) байт
// если адрес достиг 512(1024), то снова переходим на 0 if (address == 512)
address = 0; delay(500);
}
Функция записи EEPROM.write
Функция EEPROM.write записывает байт в энергонезависимую память.
Синтаксис функции EEPROM.write:
EEPROM.write(address, value)
Параметры:
address — порядковый номер ячейки памяти для записи — от 0 до 511 (int);
value — байт для записи — от 0 до 255 (byte). Возвращаемого значения нет.
Возможное количество циклов перезаписи данных (Write/Erase Cycles) в памяти ограничено 100 тыс. раз — это следует учитывать при использовании данной памяти. Время, требуемое для завершения цикла записи, составляет 3,3 ms. Данная задержка уже учитывается библиотекой EEPROM.
Пример сохранения в энергонезависимой памяти EEPROM значений, считанных с аналогового входа analog input 0, представлен в листинге 8.2. Данные значения останутся в памяти и после отключения питания от платы и в будущем могут быть доступны для другого скетча.
#include <EEPROM.h>
// текущее значение адреса EEPROM int addr = 0;
void setup()
{;}
void loop()
{
if (addr < 512)
{
// EEPROM может хранить только значения от 0 до 255. int val = map(analogRead(0),0,1024,0,256);
// записываем значение в энергонезависимую память EEPROM.write(addr, val);
// устанавливаем следующую ячейку памяти. addr = addr + 1;
}
}
Примеры использования памяти EEPROM
Воспроизведение звука
Создадим проект, в котором устройство Arduino используется для генерации звука. Мелодии хранятся в памяти EEPROM и выводятся на динамик. Для этого на динамик следует подать сигнал соответствующей частоты. В качестве динамика используем динамическую головку 8 Ом, подключаемую к выводу D8. Схема подключения представлена на рисунке.
![]() |
Схема включения |
Вместо сопротивления с номиналом 220 Ом можно использовать и большее, например, 510 Ом или 1 кОм. Недостатком такого подключения является то, что звук получается очень-очень тихий. Поэтому, чтобы получить громкость более приличного уровня, динамик можно подключить к выводу не напрямую, а через транзистор, как показано на рисунке.
![]() |
Схема включения с транзистором |
Громкость при этом получается весьма большой, поэтому в схему в качестве регулятора громкости добавлен потенциометр R2. Транзистор включен по схеме с общим эмиттером, и в данном случае выступает не в роли усилителя, а в качестве электронного ключа для согласования нагрузок. Дело в том, что у динамической головки сопротивление очень маленькое, и при подаче на нее напряжения +5 В через нее станет протекать ток около 625 мА. Однако максимальный ток, который могут обеспечить все выводы микроконтроллера, составляет всего 150 мА, т. е. в 4 раза меньше. И таким образом, подключая динамик к микроконтроллеру не напрямую, а через транзистор, способный пропускать через себя большее напряжение и ток большей силы, мы обеспечиваем электрическое согласование — в данном случае согласование по току.
Сначала составим скетч для воспроизведения одной мелодии. Мелодия состоит из двух массивов: массива с последовательным списком нот и массива со значениями длительности воспроизведения каждой ноты. Для воспроизведения одной ноты подаем на динамик сигнал определенной частоты и длительности. Затем воспроизводим следующую ноту. И так далее до окончания мелодии. Переменная tempo отвечает за скорость воспроизведения. Расчет тонов для нот одной октавы представлен в таблице.
Для подачи частотного сигнала на динамик воспользуемся функцией tone(). Скетч для воспроизведения мелодии представлен в примере сопровождающего книгу электронного архива.
Таблица расчета тонов для нот одной октавы
![]() |
// ноты мелодии
char notes[]="GECgabCaCg DGECabCDED EFEDGEDC CECaCag gCEDgCEDEFGECDgC "; // пробел - это пауза
// длительность для каждой ноты и паузы
int beats[] = { 4, 4, 4, 4, 1, 1, 1, 2, 1, 4, 2, 4, 4, 4, 4, 1, 1, 1, 2,
1,4, 2, 1, 1, 1, 1, 2, 1, 1, 4, 2, 1, 2, 1, 2, 1, 1, 4, 2, 1,
2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1, 4, 4, 4} ;
// подключить динамик к pin 8 int speakerPin = 8;
int length = sizeof(beats); // число нот
// темп воспроизведения int tempo = 400;
// проиграть ноту
void playNote(char note, int duration)
{
// массив для наименований нот (до ре ми ... и т. д. в пределах двух
// октав)
char names[]={'c','d','e','f','g','a','b','C','D','E','F','G','A','B'}; int tones[]={3830,3400,3038,2864,2550,2272,2028,1912,1700,
1518,1432,1276,1136,1014 };
// проиграть тон, соответствующий ноте for (int i = 0; i < sizeof(tones); i++)
{
if (names[i] == note) tone(speakerPin,tones[i],duration * 1000L);
}
}
void setup()
{pinMode(speakerPin, OUTPUT);}
void loop()
{
for (int i = 0; i < length; i++)
{
if (notes[i] == ' ')
tone(speakerPin,0, beats[i]*tempo* 1000L); // пауза else
playNote(notes[i], beats[i] * tempo);
}
// пауза между нотами delay(tempo / 2);
}
delay(3000);
}
Звонок с мелодиями
Проигрывать мелодию через динамик мы уже умеем. Теперь напишем скетч загрузки мелодий в память EEPROM. Зарезервируем там место для 10 мелодий, которые сможем проигрывать в нужный момент. Карта адресов памяти будет иметь следующий вид:
0-9 — указатель на адрес мелодии;
10-511 — место для мелодий.
Каждая мелодия будет представлять следующую последовательность данных: tempo(2 байта), нота1, длит.1, нота2, длит.2 ,..., нотаN, длит.N, end где:
tempo — темп проигрываемой мелодии;
нота1, нота2 ,..., нотаN — ноты;
длит1, длит2 ,..., длитN — длительности проигрывания нот;
end — байт конца мелодии (значение 0).
Запись последовательности нот и длительности их воспроизведения для каждой мелодии хранятся в массивах. В нашем скетче мы после записи мелодий в память EEPROM выведем содержимое памяти EEPROM. Фрагмент содержимого скетча представлен в примере.
// Использование библиотеки EEPROM
// запись мелодий в EEPROM
// подключение библиотеки EEPROM
#include <EEPROM.h>
// ноты мелодии
String notes[] ={ "ccggaagffeeddc ",
"GECgabCaCg DGECabCDED EFEDGEDC CECaCag gCEDgCEDEFGECDgC "};
// пробел - это пауза
// длительность для каждой ноты и паузы
String beats[]={"1,1,1,1,1,1,2,1,1,1,1,1,1,2,4", "4,4,4,4,1,1,1,2,1,4,2,4,4,4,4,1,1,1,2,1,4,2,1,1,1,1,2,1,1,4,2,1,
2,1,2,1,1,4,2,1,2,1,2,1,2,1,2,1,1,1,1,1,2,1,4,4,4"} ;
int tempo[]= {300,400};
.........
int addr1=0; int addr2=10;
void write_melody(int num)
{
EEPROM.write(addr1,addr2); // указатель на мелодию EEPROM.write(addr2,tempo[num]>>8); // для tempo – 2 байта addr2++;
EEPROM.write(addr2,tempo[num]); addr2++;
for(int j=0;j<notes[num].length();j++)
{
EEPROM.write(addr2,notes[num][j]); // нота addr2++;
EEPROM.write(addr2,beats[num][j*2]); // длительность addr2++;
Serial.print(notes[num][j]);Serial.print(" "); Serial.print(beats[num][j*2]-48);Serial.println(" ");
}
// записать 0 - end EEPROM.write(addr2,0); addr2++;
addr1++;
}
void setup()
{
.........
for(int i=0;i<sizeof(tempo)/2;i++)
{write_melody(i);}
Serial.println("end..."); Serial.println("control..."); for(int i=0;i<511;i++)
{
Serial.print(i);Serial.print(" - ");
Serial.println(EEPROM.read(i),DEC);
}
}
void loop()
{;}
В примере мы загружаем в память 2 мелодии. Я думаю, у вас не возникнет трудностей для изменения скетча для загрузки большего количества мелодий.
А теперь напишем скетч, проигрывающий случайную мелодию из памяти EEPROM при нажатии кнопки. Схема при этом изменяется незначительно. Один контакт кнопки подсоединяем к D9. К нему же подключаем подтягивающий резистор 10 кОм, который в свою очередь соединяем с землей. Другой выход кнопки соединяем с питанием 5 В. При включении в программе setup() подсчитаем количество записанных мелодий. Затем в цикле на динамик выдается мелодия. Данные мелодии (темп, список нот и их продолжительность) берутся из памяти EEPROM. Для контроля данные выводятся в монитор последовательного порта.
// ***** Воспроизведение мелодии, записанной в EEPROM
// подключение библиотеки EEPROM
#include <EEPROM.h>
// подключить динамик к pin 8 int speakerPin = 8;
// подключить кнопку к pin 9 int buttonPin = 9;
// темп воспроизведения, ноты, длительности int tempo,notes,beats;
// адрес указателя мелодии, случайная мелодия, количество мелодий int addr1;
int rand1; int count=0;
// проиграть ноту
void playNote(char note, int duration)
{
// массив для наименований нот (до ре ми ... и т. д. в пределах двух октав) char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b',
'C','D','E','F','G','A','B' };
int tones[] = { 3830, 3400, 3038, 2864, 2550, 2272, 2028, 1912, 1700, 1518,
1432, 1276, 1136, 1014 };
// проиграть тон, соответствующий ноте for (int i = 0; i < sizeof(tones); i++)
{
if (names[i] == note)
{
tone(speakerPin,tones[i],duration * 1000L);
}
}
}
void setup()
{
pinMode(speakerPin, OUTPUT); pinMode(buttonPin, INPUT); Serial.begin(9600);
for(int i=0;i<200;i++)
{
Serial.print(i);Serial.print(" - "); Serial.println(EEPROM.read(i),DEC);
}
int i=0; while(EEPROM.read(i)<255)
{count++;i++;} Serial.print("count=");Serial.println(count);
}
void loop()
{
if(digitalRead(buttonPin)==HIGH)
{
Serial.println("click");
// получить случайное rand1=random(0,count+1); addr1=EEPROM.read(rand1); Serial.println(addr1);
// tempo=EEPROM.read(addr1); addr1++;
Serial.print(tempo,DEC);Serial.println(""); tempo=tempo*256+EEPROM.read(addr1); addr1++; Serial.print(tempo,DEC);Serial.println(""); while(EEPROM.read(addr1)>0)
{
notes=EEPROM.read(addr1); addr1++; beats=EEPROM.read(addr1); addr1++;
Serial.print(notes);Serial.print(" "); Serial.print(beats,DEC);Serial.println(" ");
if (notes == ' ')
tone(speakerPin,0, beats*tempo*1000L); // пауза else
playNote(notes, beats*tempo);
// пауза между нотами delay(tempo / 2);
}
Serial.println("end");
}
}
Рекомендуем:
Побитовый свдиг влево (<<), побитовый сдвиг вправо (>>)