Работа со встроенной EEPROM в микроконтроллерах на Arduino
Микроконтроллеры AVR, на основе которых создаются платы Arduino и Genuino, обладают встроенной EEPROM: памятью, которая хранит значения переменных, пока плата выключена (подобно крошечному жесткому диску). Описываемая в данной статье библиотека позволяет считывать и записывать данные в EEPROM.
Поддерживаемые микроконтроллеры на различных платах Arduino и Genuino обладают разным размером EEPROM: 1024 байта в ATmega328, 512 байт в ATmega168 и ATmega8, 4 KB (4096 байт) в ATmega1280 и ATmega2560. Платы Arduino и Genuino 101 обладают эмуляцией EEPROM пространства размером 1024 байт.
Функции для работы с EEPROM:
- read()
- write()
- update()
- get()
- put()
- EEPROM[]
read()
ОписаниеСчитывает байт из EEPROM. Ячейки, которые никогда не были записаны, содержат значение 255.СинтаксисEEPROM.read(address)Параметрыaddress: адрес ячейки для считывания, начинается с 0 (int).Возвращаемое значениеЗначение, записанное в ячейку (byte).Пример/* * EEPROM Read * * Считывает значение каждого байта в EEPROM и выводит его * на компьютер.
*/ #include // начать чтение с первого байта (адрес 0) в EEPROM int address = 0; byte value; void setup() { // инициализировать последовательный порт и ждать, // пока порт не будет открыт: Serial.begin(9600); while (!Serial) { ; // ждать подключения последовательного порта. Необходимо только для встроенного USB порта.
} } void loop() { // прочитать байт из текущего адреса EEPROM value = EEPROM.read(address); Serial.print(address); Serial.print(” “); Serial.print(value, DEC); Serial.println(); /*** Перейти к следующему адресу, или, если находимся в конце, вернуться в начало.
Большие AVR контроллеры обладают большим размером EEPROM, например: – Arduno Duemilanove: 512b EEPROM; – Arduino Uno: 1kb EEPROM; – Arduino Mega: 4kb EEPROM; Вместо жесткого прописывания размера EEPROM, лучше использовать предусмотренную функцию определения размера. Это сделает ваш код портируемым на все микроконтроллеры AVR.
***/ address = address + 1; if (address == EEPROM.
length()) { address = 0; } /*** Поскольку размеры EEPROM являются степенями двойки, то возврат к началу EEPROM можно было бы реализовать с помощью операции побитового И со значением (размер – 1). ++address &= EEPROM.length() – 1; ***/ delay(500); }
write()
ОписаниеЗаписывает байт в EEPROM.СинтаксисEEPROM.write(address, value)Параметрыaddress: адрес ячейки для записи, начинается с 0 (int).
value: значение для записи, от 0 до 255 (byte).Возвращаемое значениеНичего не возвращается.ПримечаниеЗапись в EEPROM занимает 3,3 мс.
Память EEPROM обладает жизненным циклом 100 000 операций записи/стирания, поэтому, возможно, вам придется быть осторожными с тем, как часто записываете её.Пример/* * EEPROM Write * * Записывает значения, считанные с аналогового входа 0, в EEPROM. * Эти значения будут оставаться в EEPROM, когда плата будет * выключена, и могут быть извлечены позже при следующем запуске.
*/ #include /** текущий адрес в EEPROM (т.е. какой байт мы собираемся записать следующим) **/ int addr = 0; void setup() { /** Нет никаких начальных настроек. **/ } void loop() { /*** Необходимо делить на 4, так как диапазон значений на аналоговых входах составляет от 0 до 1023, а каждый байт EEPROM может хранить значения только от 0 до 255.
***/ int val = analogRead(0) / 4; /*** Записать значение в соответствующий байт EEPROM. Эти значения останутся там, когда плата будет выключена. ***/ EEPROM.write(addr, val); /*** Перейти к следующему адресу, или, если находимся в конце, вернуться в начало.
Большие AVR контроллеры обладают большим размером EEPROM, например: – Arduno Duemilanove: 512b EEPROM; – Arduino Uno: 1kb EEPROM; – Arduino Mega: 4kb EEPROM; Вместо жесткого прописывания размера EEPROM, лучше использовать предусмотренную функцию определения размера. Это сделает ваш код портируемым на все микроконтроллеры AVR.
***/ addr = addr + 1; if (addr == EEPROM.
length()) { addr = 0; } /*** Поскольку размеры EEPROM являются степенями двойки, то возврат к началу EEPROM можно было бы реализовать с помощью операции побитового И со значением (размер – 1). ++addr &= EEPROM.length() – 1; ***/ delay(100); }
update()
ОписаниеЗаписывает байт в EEPROM. Значение записывается, только если оно отличается от значения уже записанного по этому адресу.СинтаксисEEPROM.update(address, value)Параметрыaddress: адрес ячейки для записи, начинается с 0 (int).
value: значение для записи, от 0 до 255 (byte).Возвращаемое значениеНичего не возвращается.
ПримечаниеЗапись в EEPROM занимает 3,3 мс. Память EEPROM обладает жизненным циклом 100 000 операций записи/стирания, поэтому данная функция, в отличие от write(), может сохранить время, если записываемые данные часто не меняются.Пример/*** EEPROM Update Сохраняет значения, считанные с аналогового входа 0, в EEPROM.
Эти значения будут оставаться в EEPROM, когда плата будет выключена, и могут быть извлечены позже при следующем запуске. Если значение не изменилось, то EEPROM не перезаписывается, что поможет без необходимости не сокращать срок службы EEPROM. ***/ #include /** текущий адрес в EEPROM (т.е.
какой байт мы собираемся записать следующим) **/ int address = 0; void setup() { /** EMpty setup **/ } void loop() { /*** Необходимо делить на 4, так как диапазон значений на аналоговых входах составляет от 0 до 1023, а каждый байт EEPROM может хранить значения только от 0 до 255. ***/ int val = analogRead(0) / 4; /*** Обновить конкретную ячейку EEPROM.
Эти значения останутся там при выключении платы. ***/ EEPROM.update(address, val); /*** Функция EEPROM.update(address, val) эквивалентна следующему: if( EEPROM.read(address) != val ){ EEPROM.write(address, val); } ***/ /*** Перейти к следующему адресу, или, если находимся в конце, вернуться в начало.
Большие AVR контроллеры обладают большим размером EEPROM, например: – Arduno Duemilanove: 512b EEPROM; – Arduino Uno: 1kb EEPROM; – Arduino Mega: 4kb EEPROM; Вместо жесткого прописывания размера EEPROM, лучше использовать предусмотренную функцию определения размера. Это сделает ваш код портируемым на все микроконтроллеры AVR.
***/ address = address + 1; if (address == EEPROM.
length()) { address = 0; } /*** Поскольку размеры EEPROM являются степенями двойки, то возврат к началу EEPROM можно было бы реализовать с помощью операции побитового И со значением (размер – 1). ++addr &= EEPROM.length() – 1; ***/ delay(100); }
get()
ОписаниеСчитывает любой тип данных или объект из EEPROM.СинтаксисEEPROM.get(address, data)Параметрыaddress: адрес для чтения, начинается с 0 (int).
data: данные для чтения, могут быть примитивным типом (например, float) или пользовательской структурой struct.Возвращаемое значениеСсылка на переданные данные.
Пример/*** Чтобы предварительно записать данные в EEPROM, используйте пример для функции put(). Можно обойтись и без этого, но значения, выводимые этим скетчем, зависят от того, что содержится в EEPROM.
Это может заставить объект последовательного порта вывести на печать длинную строку мусора, если в загруженной строке не будет найден нулевой символ. ***/ #include void setup() { float f = 0.00f; // Переменная для хранения данных, прочитанных из EEPROM. int eeAddress = 0; // Адрес EEPROM, откуда следует начать чтение. Serial.
begin(9600); while (!Serial) { ; // ждать подключения последовательного порта. Необходимо только для встроенного USB порта. } Serial.print(“Read float from EEPROM: “); // Получить данные типа float из EEPROM в месте 'eeAddress' EEPROM.get(eeAddress, f); Serial.
println(f, 3); // Это может напечатать 'ovf, nan', если данные в EEPROM // не корректны для float. /*** Так как get возвращает ссылку на 'f', вы можете использовать ее в качестве аргумента То есть: Serial.print( EEPROM.get( eeAddress, f ) ); ***/ /*** Get может использоваться и с пользовательскими структурами.
Пример с ними выделен в отдельную функцию. ***/ secondTest(); // Запустить следующий тест. } struct MyObject { float field1; byte field2; char name[10]; }; void secondTest() { int eeAddress = sizeof(float); // Переместить адрес к байту, следующему после float 'f'.
MyObject customVar; // Переменная для хранения данных, прочитанных из EEPROM. EEPROM.get(eeAddress, customVar); Serial.println(“Read custom object from EEPROM: “); Serial.println(customVar.field1); Serial.println(customVar.field2); Serial.println(customVar.name); } void loop() { /* Пустой цикл */ }
put()
ОписаниеЗаписывает любой тип данных или объект из EEPROM.СинтаксисEEPROM.put(address, data)Параметрыaddress: адрес для записи, начинается с 0 (int).
data: данные для записи, могут быть примитивным типом (например, float) или пользовательской структурой struct.Возвращаемое значениеСсылка на переданные данные.
ПримечаниеФункция используетEEPROM.update()для реализации записи, поэтому она не перезаписывает значение, если оно не изменилось.Пример/*** Этот скетч также можно использовать для предварительной записи в EEPROM данных, используемых в примере для функции get(). Обратите внимание, что, в отличие от однобайтной версии EEPROM.
write(), функция put использует обновление. То есть байт будет записан, только если он отличается от записанных в EEPROM данных. ***/ #include struct MyObject { float field1; byte field2; char name[10]; }; void setup() { Serial.begin(9600); while (!Serial) { ; // ждать подключения последовательного порта.
Необходимо только для встроенного USB порта. } float f = 123.456f; // Переменная для записи в EEPROM. int eeAddress = 0; // Место, куда мы хотим положить данные. // Простой вызов с адресом и переменной в качестве аргументов. EEPROM.put(eeAddress, f); Serial.
println(“Written float data type!”); /** Put поддерживает и пользовательские структуры. **/ //Data to store. MyObject customVar = { 3.14f, 65, “Working!” }; eeAddress += sizeof(float); // Переместить адрес к байту, следующему после float 'f'. EEPROM.put(eeAddress, customVar); Serial.
print(“Written custom data type!
View the example sketch eeprom_get to see how you can retrieve the values!”); } void loop() { /* Пустой цикл */ }
EEPROM[]
ОписаниеДанный оператор позволяет использовать идентификатор 'EEPROM', как массив. Ячейки EEPROM могут быть прочитаны и записаны непосредственно с помощью этого оператора.СинтаксисEEPROM[address]Параметрыaddress: адрес для чтения/записи, начинается с 0 (int).
Возвращаемое значениеСсылка на ячейку EEPROM.Пример#include void setup(){ unsigned char val; // Прочитать первую ячейку EEPROM. val = EEPROM[ 0 ]; // Записать первую ячейку EEPROM. EEPROM[ 0 ] = val; // Сравнить содержимое if( val == EEPROM[ 0 ] ){ // Сделать что-то…
} } void loop(){ /* Пустой цикл */ }
Источник:
На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.
Источник: https://radioprog.ru/post/117
Работа с энергонезависимой памятью в Arduino (EEPROM)
Микроконтроллеры Atmega328, работающие в Arduino UNO и NANO имеют на борту 1024 байт EEPROM – энергонезависимой памяти, в которой можно сохранять какие-либо данные, которые будут доступны после отключения питания.
Это может пригодиться для хранения каких-нибудь данных или значений.
Для работы с данной памятью в составе Arduino IDE уже есть удобная библиотека EEPROM (hardwarelibrariesEEPROM).
byte EEPROM.read(address)
Описание:
Считывает байт из энергонезависимой памяти EEPROM. Если байт до этого никогда не перезаписывался – вернёт значение 255.
Параметры:
address: порядковый номер ячейки памяти для чтения — от 0 до 511 (int)
Возвращаемое значение:
Байт, хранимый в ячейке памяти.
Пример (File-Examples-EEPROM-eeprom_read):
/* * Чтение EEPROM * * Считывает значения всех байтов энергонезависимой памяти * EEPROM и выводит их в COM-порт */ #include // начальный адрес памяти EEPROM int address = 0; byte value; void setup() { Serial.begin(9600); } void loop() { // считываем значение по текущему адресу EEPROM value = EEPROM.read(address); Serial.print(address); Serial.print(” “); Serial.print(value, DEC); Serial.println(); // устанавливаем следующую ячейку памяти address = address + 1; // EEPROM содержит всего 512 байт: от 0 до 511, поэтому // если адрес достиг 512, то снова переходим на 0 if (address == 512) address = 0; delay(500); }
void EEPROM.write(address, value)
Описание:
Записывает байт в энергонезависимую память
Параметры:
address: порядковый номер ячейки памяти для записи — от 0 до 511 (int)
value: байт для записи – от 0 до 255 (byte)
Возвращаемое значение:
ничего
Примечание:
Документация (datasheet) на микроконтроллеры Atmega8/168 говорит, что возможное количество циклов перезаписи данных в памяти ограничено 100000 раз (Write/Erase Cycles). Это следует учитывать при использовании данной памяти.
Так же документация указывает, что время, требуемое для завершения цикла записи составляет 3.3 ms.
Если в это время попытаться что-либо считать/записать в EEPROM, то такая попытка окончится неудачей 🙁
Однако, данная задержка уже учитывается библиотекой EEPROM, поэтому в дополнительном вызове delay() нет необходимости.
Пример (File-Examples-EEPROM-eeprom_write):
/* * EEPROM Write * * Сохраняет в энергонезависимой памяти EEPROM значения, * считанные с аналогового входа analog input 0. * Данные значения останутся в памяти и после отключения питания * от платы и в будущем могут быть доступны для * другого скетча. */ #include // текущее значение адреса EEPROM int addr = 0; void setup() { } void loop() { // деление на 4 необходимо, чтобы перевести значение от // 0-1023 к одному байту, т.к. EEPROM может хранить только // значения от 0 до 255. int val = analogRead(0) / 4; // записываем значение в энергонезависимую память EEPROM.write(addr, val); // устанавливаем следующую ячейку памяти. // т.к. EEPROM содержит всего 512 ячеек – при достижении // конца памяти – возвращаемся на начало 🙂 addr = addr + 1; if (addr == 512) addr = 0; delay(100); }
В примере eeprom_clear (File-Examples-EEPROM-eeprom_clear)
показано, как произвести очистку памяти – просто заполнить её нулями:
// записываем 0 во все 512 байт памяти EEPROM for (int i = 0; i > 0) & 0xFF); byte highByte = ((p_value >> 8) & 0xFF); EEPROM.write(p_address, lowByte); EEPROM.write(p_address + 1, highByte); } //Эта функция прочитает 2 байта типа integer из eeprom с адресов address и address + 1 unsigned int EEPROMReadInt(int p_address) { byte lowByte = EEPROM.read(p_address); byte highByte = EEPROM.read(p_address + 1); return ((lowByte
Источник: http://www.2150692.ru/faq/38-arduino-eeprom
Как хранить данные в Arduino
У плат семейства плат Arduino есть несколько видов памяти. Во-первых, это статическое ОЗУ (оперативное запоминающее устройство), которая используется для хранения переменных в процессе выполнения программы. Во-вторых, это флеш-память, в которой хранятся написанные вами скетчи.
И в-третьих, это EEPROM, которую можно использовать для постоянного хранения информации. Первый тип памяти – энергозависимый, он теряет всю информацию после перезагрузки Arduino. Вторые два типа памяти хранят информацию пока она не будет перезаписана новой, даже после отключения питания.
Последний тип памяти – 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 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
Смотрите, монитор последовательного порта в ячейку 0 просто выводит число, меньшее, чем 255. В ячейках 1 и 2 хранится большое число 789. При этом ячейка 1 хранит множитель переполнения 3, а ячейка 2 – недостающее число 21 (т.е. 789 = 3×256 + 21).
Запись целых чисел в EEPROM Arduino
Чтобы заново «собрать» большое число, разобранное на байты, есть функция word(): int val = word(hi, low), где “hi” и “low” – это значения старшего и младшего байтов числа “val”.
Во всех остальных ячейках, которые не были нами ни разу записаны, хранятся числа 255.
4Запись чисел с плавающей запятой и строк в EEPROM
Для записи чисел с плавающей запятой и строк нужно использовать метод EEPROM.put(), а для чтения – EEPROM.get().
#include // подключаем библиотеку 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
В процедуре setup() сначала запишем число с плавающей запятой “f”. Затем сдвинемся на количество ячеек памяти, которое занимает тип “float”, и запишем строку символов “char” ёмкостью 20 ячеек.
В процедуре loop() будем считывать все ячейки памяти и пытаться расшифровать их сначала как тип “float”, а затем как тип “char”, и выводить результат в последовательный порт.
Запись чисел с плавающей запятой в EEPROM Arduino
Видно, что значение в ячейках с 0 по 3 правильно определилось как число с плавающей точкой, а начиная с 4-ой – как строка.
Появляющиеся значения ovf (переполнение) и nan (не число) говорят о том, что значение в ячейке памяти по этому адресу не может быть корректно преобразовано в число с плавающей точкой. Если вы точно знаете, какого типа данные какие ячейки памяти занимают, то у вас не будет возникать проблем.
5Работа с EEPROM как с массивом
Очень удобная возможность – обращение к ячейкам памяти как к элементам массива EEPROM. В данном скетче в процедуре setup() мы сначала запишем данные в 4 первых байта, а в процедуре loop() ежеминутно будем считывать данные из всех ячеек и выводить их в последовательный порт.
#include 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Работа с ячейками памяти EEPROM Arduino как с элементами массива
Источник: https://soltau.ru/index.php/arduino/item/378-kak-khranit-dannye-v-arduino
Библиотеки Arduino
Для более легкой и продуктивной работы с Arduino есть возможность использовать дополнительные библиотеки. Библиотеки Arduino — это части программы для выполнения конкретных задач. С помощью библиотек можно выполнять сложные действия всего парой строк кода, потому что кто-то другой уже написал часть кода за вас.
В Arduino IDE удобный интерфейс для работы с библиотеками ардуино. Прямо из меню программы вы можете скачать, установить и подключить в свой скетч множество библиотек. Для большинства библиотек Arduino можно посмотреть примеры использования. Это поможет понять принцип работы библиотеки. Примеры можно доработать под свои нужды и использовать для реализации своих устройств.
Существуют стандартные библиотеки, которые устанавливаются вместе с Arduino IDE. Некоторые из них даже автоматически подключаются в скетч (например Serial).
Скачать стандартные библиотеки Arduino
Скачать стандартные библиотеки можно на официальном сайте Arduino.
Там вы найдете описания и примеры использования стандартных библиотек. Так же вы можете скачать все стандартные библиотеки одним архивом здесь. В этом архиве не только стандартные библиотеки, но и множество дополнительных самых популярных библиотек для Ардуино.
Ниже приведены подробные описания и примеры использования стандартных библиотек Arduino.
- Serial — Библиотека для обмена данными через последовательный порт (UART).
- Servo — Библиотека для легкого и точного управления сервоприводами.
- Wire — Библиотека для работы с интерфейсами связи TWI/I2C. Упрощает обмен данными с устройствами, датчиками и сенсорами.
- WiFi — Подключение к интернету с использованием WiFi шилда.
- TFT — Нужна для отрисовки текста, изображений и картинок на TFT дисплее Arduino.
- Stepper — Библиотека для управления шаговыми моторами.
- LiquidCrystal — Для работы Arduino с жидкокристаллическими экранами (LCD)
- Ethernet — Для подключения к интернету с использованием Arduino Ethernet shield.
- SD — Библиотека для записи и считывания информации с SD карт памяти.
- GSM — Библиотека для подключения Ардуино к GSM сети. Необходима для отправки и получения SMS и звонков, а так же для выхода в интернет с помощью GPRS. Используется с GSM shield.
- EEPROM — Библиотека для чтения и записи в энергонезависимую память Arduino.
Источник: https://all-arduino.ru/biblioteki-arduino/
Работа с EEPROM
Как вы знаете, микроконтроллер используемый в модуле DINduino имеет 1 (один) килобайт EEPROM (электрически стираемое перепрограммируемое ПЗУ).
EEPROM является одним из видов энергонезависимой памяти и позволяет нам с вами сохранять некоторые параметры, значения или константы на то время, пока контроллер DINduino отключен. Все нижеописанное может быть использовано на любом Arduino совместимом контроллере.
В среде Arduino существует стандартная библиотека поддержки операций с EEPROM. Именно на примере этой библиотеки мы будем разбираться в особенностях работы с энергонезависимой памятью. Однако, справедливости ради, необходимо сказать что в репозитории менеджера библиотек доступно еще несколько достаточно талантливых библиотек сторонних разработчиков.
Из них необходимо отметить следующие: Arduino EEPROMex – расширяет стандартные функции работы с EEPROM и облегчает хранение различного рода структур в энергонезависимой памяти. Хранение различных типов данных и переменных. MiniPirate – библиотека-интерпретатор командной строки с возможностью доступа к энергонезависимой памяти.
Когда-нибудь мы обязательно посвятим отдельную статью этой библиотеке.
Итак, прежде всего нам необходимо подключить библиотеку EEPROM к нашему проекту на Arduino
#include
А теперь благодаря кратким примерам кода давайте разберемся с основными функциями нашей новой библиотеки.
WRITE();
Записывает значение в память
int addr = 0; // Текущий адрес в EEPROM int val = 0; // Записываемое в память значение EEPROM.write(addr, val); // Записываем значение в память // увеличиваем значение адреса до конца области EEPROM addr = addr + 1; if (addr == EEPROM.length()) addr = 0;
Не трудно догадаться, что процедура очистки памяти и удаления ранее записанных данных будет выглядеть следующим образом:
for (int i = 0 ; i < EEPROM.length() ; i++) { EEPROM.write(i, 0); }
READ();
Чтение из памяти
int address = 0; byte value; value = EEPROM.read(address)
Update();
Как вы знаете есть ограничение в 100.000 записей которые можно сделать в одну ячейку памяти. После исчерпания этого лимита возможны сбои и ошибки. Кроме того, память EEPROM является достаточно медленной. Операция записи занимает 3.3 миллисекунды времени. Именно эту проблему и пытается решить функция Update(); которая предотвращает перезапись не изменившихся данных.
EEPROM.update(address, val);
Put();
В связи с очевидным неудобством функций Write() и Update() осуществляющих работу побайтно, функция Put позволяет сохранять структуры и переменные в памяти EEPROM
// Определяем структуру struct MyObject { float field1; byte field2; char name[10]; }; // Заполняем структуру данными MyObject customVar = { 3.14f, 65, “Работаем!” }; // Помещаем данные в память EEPROM.put(eeAddress, customVar);
Get();
Аналогичным образом осуществляется чтение структуры из памяти
struct MyObject { float field1; byte field2; char name[10]; }; EEPROM.get(eeAddress, customVar);
Контрольная сумма
Не будем скрывать, использование EEPROM все-таки не самый надежный метод хранения данных. Именно по этой причине в последние годы все производители интегральных схем активно стараются заменить EEPROM на прочие виды памяти. Для нас же остается только подсчитывать и контролировать контрольную сумму сохраняемых и читаемых данных. Для этого можно воспользоваться следующей функцией:
unsigned long eeprom_crc(void) { const unsigned long crc_table[16] = { 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; unsigned long crc = ~0L; for (int index = 0 ; index < EEPROM.length() ; ++index) { crc = crc_table[(crc ^ EEPROM[index]) & 0x0f] ^ (crc >> 4); crc = crc_table[(crc ^ (EEPROM[index] >> 4)) & 0x0f] ^ (crc >> 4); crc = ~crc; } return crc; }
Прямой доступ
Последней, заслуживающей внимания, функцией библиотеки EEPROM является возможность доступа к памяти как к массиву используя цикл
for (int index = 0 ; index < EEPROM.length() ; index++) { EEPROM[ index ] += 1; // Пример, увеличиваем на единицу значение каждой ячейки памяти }
Цикл с предусловием
int index = 0; while (index < EEPROM.length()) { EEPROM[ index ] += 1; index++; }
Цикл с постусловием
int index = 0; do { EEPROM[ index ] += 1; index++; } while (index < EEPROM.length());
Источник: http://dinduino.ru/ru/specification/eeprom
Подключение внешней SRAM 512 Кбайт к Arduino Mega. Часть 1 – Теория
Очень часто при разработке приложений и систем на базе Arduino встает проблема нехватки памяти, присущая среде программирования микроконтроллеров.
С помощью серия Arduino Mega возможно решение данной проблемы, т.к.имеются варианты исполнения данной платформы с Flash-памятью программ 128 КБайт или 256 КБайт.
А как насчет встроенной памяти SRAM? Максимальный объем в нашем случае (Arduino Mega) не превышает 8 КБайт.
К счастью, серия Mega позволяет работать с внешней SRAM, и что самое главное, программа может получать беспрепятственно доступ к ней, как если бы это была внутренняя SRAM микроконтроллера. В статье мы рассмотрим аппаратное и программное решение задачи расширения памяти на базе микроконтроллера Atmel AVR ATmega1280.
XMEM
В техническом описании на микроконтроллер ATmega1280 содержится вся информация для инженеров, необходимая для расширения памяти. В документе это 9 глава «External Memory Interface» (XMEM). Блок схема ниже поясняет, как микроконтроллер организует связь с внешней памятью.
Рис. 1. | Блок-схема организации работы с внешней памятью с помощью XMEM. Блок в центре – высокоскоростной 8-битный регистр-защелка. |
Наиболее интересная часть в этой схеме – блок в центре, который представляет собой высокоскоростную 8-разрядную защелку.
Мультиплексирование и защелка
Адресное пространство Arduino Mega позволяет подключать внешнюю память объемом до 64 КБайт при 8-битной организации. Обычно для этого требуется немалое количество линий ввода/вывода: 16 для адресной шины, 8 для шины данных и как минимум еще 2 для управления.
Чтобы сократить количество используемых линий ввода/вывода, для подключения внешней памяти, микроконтроллер мультиплексирует младшие 8 адресных линий с 8 линиями данных, экономя при этом 8 линий. При такой реализации используется 8-битная защелка (8-разрядный регистр), при этом временные диаграммы работы с внешней памятью следующие:
Рис. 2. | Временные диаграммы работы интерфейса внешней памяти. |
Период времени, обозначенный красной линией, показывает промежуток между установкой действительного адреса (достоверные данные на линиях A0 – A15) и началом передачи данных (действительные данные на линиях D0 – D7).
Логика микроконтроллера начинает операции с внешней памятью, подтверждая действительный адрес на линиях A0 – A15. Мультиплексированные линии проходят через 8-разрядный регистр-защелку, который работает в «прозрачном» режиме (transparent mode).
Затем регистр переключается в режим хранения (Hold mode), при котором игнорируются изменения на входе и продолжается хранение последних принятых данных на выходе.
Микроконтроллер затем устанавливает действительные данные на мультиплексированных линиях (это уже будут данные D0 – D7) и, в результате, мы имеем успешно установленные все линии для работы с внешней памятью и сохранили 8 линий ввода/вывода, которые могут понадобиться при работе основного приложения.
Стоит заметить, что регистр-защелка должен успевать работать на тактовой частоте 16 МГц, поэтому серия 74HC не подойдет для нашей цели. В техническом описании на микроконтроллер рекомендуется серия 74AHC.
Адресация при объеме SRAM более 64 КБайт
Теперь, основная задача – это работа с внешней памятью объемом 512 КБайт с адресным пространством всего на 64 КБайт. Решение – разделить 512 КБайт на 8 банков памяти по 64 КБайт и сделать управление «видимостью» одного банка в каждый момент времени с использованием 3 выводов микроконтроллера.
Память объемом 512 КБайт при обращении к ней требует 19-битного адреса (адресные линии A0 – A18). Линии A0 – A15 мы подключим по интерфейсу xmem (интерфейс внешней памяти микроконтроллера) и управление 3 оставшимися (A16 – A18) осуществим с помощью цифровых линий ввода/вывода микроконтроллера.
Рис. 3. | Разделение внешней SRAM на 8 банков памяти объемом 64 КБайт. |
Следующее ограничение, которое накладывает карта памяти микроконтроллеров ATmega – нижние 8 КБайт SRAM (адресное пространство для такого объема) всегда занимаются внутренней памятью микроконтроллера.
Это означает, что адресация внешней памяти осуществляется в диапазоне 0х2200 – 0хFFFF, т.е. мы теряем 69632 байта из общего объема 524288 Байт внешней SRAM.
В спецификации на микроконтроллер объясняется метод адресации этих потерянных 8 КБайт, однако автор посчитал, что в данной задаче это не актуально.
Во второй части статьи представлены принципиальная схема подключения внешней ОЗУ к микроконтроллеру, список компонентов и рисунки печатной платы.
Источник: https://www.rlocman.ru/shem/schematics.html?di=109611
Подключаем память 24LC16 к микроконтроллеру AVR :AVR devices
Иногда при разработке устройства возникает потребность сохранять какие-либо данные в энергонезависимую память. В таких случаях обычно используют внутреннюю EEPROM микроконтроллера. Если её недостаточно, то как правило применяются внешние микросхемы EEPROM из серии 24lxx. Микросхемы этой серии очень популярны.
Чаще всего их можно встретить в старых мобильных телефонах, некоторых материнских платах, картриджах от принтеров да еще много где. Цена данных микросхем тоже очень привлекательная. Например 24LC16 у нас стоит 11 рублей.
Данная микросхема выпускается в различных корпусах, самые популярные из которых это DIP и SOIC.
Микросхема имеет следующую распиновку:
Как видите выводов совсем немного. Итак попробуем разобраться для что к чему.
A0, A1, A2 — в данной микросхеме не используются. Их можно подсоединить к земле или к плюсу питания.
В некоторых других микросхемах серии 24lxx, этими выводами можно задавать адрес микросхемы, для того чтобы можно было подсоединить на одну шину i2c аж сразу 8 микрух памяти.
Vss — земля.
SDA — линия данных
SCL — линия тактовых импульсов
WP — Защита от записи. Когда на данном выводе логический 0, то запись в память разрешена. Если подать логическую единицу, то возможно только чтение из памяти.
Vcc — питание микросхемы.
Согласно даташиту питается она напряжением от 2.5 вольта до 5.5 вольта.<\p>
Подключение к контроллеру.
Подключить память к МК очень просто. Из обвязки потребуются только пара резисторов сопротивлением около 4.7 кОм.
Программное обеспечение
Для работы с памятью была разработана библиотека реализующая следующие функции:<\p>
i2c_init — настраивает скорость тактовых импульсов идущих по линии SCL.
Микросхема 24LC16 поддерживает частоту до 400 кГц. Рассчитать частоту можно так:
CPU Clock frequency — частота на которой работает микроконтроллер<\p>
TWBR — число записанное в одноименный регистр.<\p>
TWPS — предделитель. Значения предделителя задаются битами TWPS1 и TWPS0 в регистре TWSR
Для контроллера Atmega 32 справедлива такая таблица:
i2c_start — отсылает стартовую посылку
i2c_stop — отсылает стоповую посылку
i2c_send — отсылает байт
i2c_recive — принимает байт
i2c_recive_last — принимает последний байт. Отличие от предыдущей функции состоит в том, что когда байт принят, микроконтроллер не отсылает бит подтверждения. Если при приёме последнего байта использовать i2c_recive то линия SDA останется прижатой к земле.
Запись данных в микросхему памяти
Записывать данные можно как в произвольном порядке так и постранично. Поскольку на шине i2c могут быть сразу несколько устройств, то для того чтобы обратится к какому либо устройству нужно знать его семибитный адрес. Адрес микросхемы 24LC16 в двоичном виде выглядит так:
Биты A,B,C служат для выбора блока памяти. Блоков памяти в микросхеме 8 штук по 256 байт каждый. Соответственно биты ABC принимают значения от 000 до 111.
Для того чтоб записать в микросхему байт нужно выполнить следующую последовательность действий:
- Инициализировать интерфейс i2c
- Отослать стартовую посылку
- Отослать адрес микросхемы+адрес блока памяти
- Отослать адрес ячейки памяти в которую будет производится запись
- Отослать байт данных
- Отослать стоповую посылку
Пример: Нужно записать байт 0xFA по адресу 0x101.
rcall i2c_init //Инициализируем интерфейс i2c
rcall i2c_start // Отправляем стартовую посылку
ldi temp,0b10100010//Адрес микросхемы где:
// 1010 – адрес микросхемы
//001 – адрес блока памяти (Ячейка 0x101 принадлежит блоку 1)
// – бит чтения/записи. 0 – запись , 1 – чтение rcall i2c_send
ldi temp,1 //Адрес ячейки памяти. (блок 1, ячейка 1)
rcall i2c_send
ldi temp,0xFA //Загружаем в регистр байт который нужно записать
rcall i2c_send //Записываем байт
rcall i2c_stop //Отправляем стоповую посылку
https://www.youtube.com/watch?v=O6t-n3_fnQ0
Записывать данные в память можно не только побайтно но и постранично. Размер страницы — 16 байт.
Постараничная запись подразумевает следующее: Отправляем адрес нулевого байта нужной страницы и после этого 16 раз отправляем нужные данные. Счётчик адреса будет увеличивать на единицу автоматически.
Если отправить данные в 17-й раз, то будет перезаписан нулевой байт, если отправить байт 18-й раз , то он затрет байт номер 1 итд.
Пример: Требуется записать первую страницу блока 0.
rcall i2c_init //Инициализируем интерфейс i2c
rcall i2c_start // Отправляем стартовую посылку
ldi temp,0b10100000//Адрес микросхемы где:
// 1010 – адрес микросхемы
//000 – адрес блока памяти (нас интересует нулевой блок)
// – бит чтения/записи. 0 – запись , 1 – чтение rcall i2c_send
ldi temp,16 //Адрес первой страницы
rcall i2c_send
ldi temp,0x01 //Загружаем в регистр байт номер 0
rcall i2c_send //Записываем байт
ldi temp,0x02 //Загружаем в регистр байт номер 1
rcall i2c_send //Записываем байт
/// тут пишем остальные байты…..
ldi temp,0x0E //Загружаем в регистр байт номер 14
rcall i2c_send //Записываем байт
ldi temp,0x0F //Загружаем в регистр байт номер 15
rcall i2c_send //Записываем байт
rcall i2c_stop //Отправляем стоповую посылку
Чтение данных из микросхемы
С записью вроде разобрались, теперь приступим к чтению. Чтобы прочитать байт нужно сделать следующее:
- Инициализировать интерфейс i2c (если он не инициализировался ранее)
- Отправить стартовую посылку
- Отправить адрес микросхемы и адрес блока памяти откуда будем читать
- Отправить адрес ячейки памяти
- Отправить стартовую посылку повторно
- Отправить адрес микросхемы и адрес блока памяти с битом «чтение»
- Получить байт
- Отправить стоповую посылку
Пример: Нужно прочитать байт находящийся по адресу 0x341
rcall i2c_init //Инициализируем интерфейс i2c
rcall i2c_start // Отправляем стартовую посылку
ldi temp,0b10100110 //Адрес микросхемы + адрес 3-го блока памяти.
//Бит чтение/запись по прежнему 0 ! rcall i2c_send
ldi temp,0x41 //Адрес ячейки памяти
rcall i2c_send
rcall i2c_start //Повторная отправка стартовой посылки
ldi temp,0b10100111 //Адрес микросхемы+адрес блока памяти+бит чтения/записи стал 1
rcall i2c_send //теперь можно читать данные
rcall i2c_recive_last //Читаем байт. Первый и последний.
rcall i2c_stop //Отправляем стоповую посылку
Чтение может производится последовательно байт за байтом, т.е. просто вызывать i2c_recive столько сколько нужно. Команду увеличения адреса на единицу посылать не нужно. Переключать адреса блоков при последовательном чтении так же не нужно. Т.е. можно взять и разом прочитать всю микросхему без всяких проблем.
Библиотека для работы с i2c разрабатывалась и была испытана на микроконтроллере Atmega32. Я думаю что она будет работать на многих других контроллерах без каких либо изменений. Естественно в контроллере должна быть аппаратная поддержка i2c или как его еще называют TWI.
Конечно реализовать i2c можно и программно, но я не стал заморачиваться да и не было нужды. Демонстрационный пример представляет собой программу которая записывает по первым 16 адресам байты от 0 до 15, а после записи выводит их в порт A.
Наблюдать как это работает можно не только в живую но и в Proteus’е.
Ну и напоследок прикладываю осциллограмму:
Вот так выглядит шина i2c глазами моего осциллографа 🙂
Все вопросы и предложения жду в комментариях.
P.S. для понимания работы шины рекомендую ознакомится со статьёй Di Halt’a
Скачать исходник + демонстрационный пример
Источник: http://avrdevices.ru/podkluchaem_pamyat_24lc16_k_avr/
Энергонезависимая память AT24C02 и Arduino – Любимые видео
1 лет назад
Если необходимо сохранить значение переменных во время отключения питания, то наилучший выход – использовать EEPROM. Это енергонезависимая память, которая поможет сохранить значение переменных и настройки устройства. Подключение по I2C делает использование EEPROM AT24C128 очень простым. Библиотека и скетч: https://drive.google.
com/open?id=0Bw5SzGwQf6yzaE5Nb3lwblB0TU0 Друзья, поддержите канал! R340967932571 Z422687519909 U139735106020 4149625807630874 Приват Банк (Украина) Полезные ссылки: EEPROM 24C128: http://ali.pub/1w3yh5 Дисплей 1602 + расширитель портов: http://ali.pub/1vb6ja Дисплей 1602: http://ali.pub/1uo1ww Модуль Micro SD: http://ali.
pub/1tkx36 TFT дисплеи: http://ali.pub/1ny2rc Ардуино Мега 2560: http://ali.pub/1ny34r Самые дешевые Ардуино Про Мини для поделок: http://ali.pub/1ny2z4 Макетка: http://ali.pub/1ny3ae Перемычки: http://ali.pub/1ny3la Качественные перемычки: http://ali.pub/1ny3j5 Самый удобный Кешбек: LetyShops: https://goo.
gl/Ev5XgI Самый выгодный Кешбек на алиекспресс: Ecomerce: https://goo.gl/3dDc2w
2 месяцев назад
В этом видео я покажу подборку из трех разных схем: детектор электрического поля, генератор колебаний на реле, кодовый замок на одном транзисторе. Представленные конструкции простые, но достаточно интересные и отлично подойдут для начинающих. Схемы для начинающих: http://cxem.net/beginner/beginner.php
4 лет назад
электронный замок на arduino с самодельными ключами на еепром 24C02 // код http://arrduinolab.blogspot.com/2015/02/eeprom-24c02.html // библиотека для 24C01-02 https://github.
com/jlesech/24C01-02 // библиотека для 24C04-16 https://github.com/jlesech/24C04-08-16 // библиотека для 24C32-64 https://github.com/jlesech/24C32-64 поддержать канал материально. http://www.
donationalerts.ru/r/arduinolab
1 лет назад
Каждый в определенный момент сталкивается с проблемой низкой скорости выполнения программы на Ардуино. Но на самом ли деле Ардуино такая медленная как кажется? Попробуем разобраться в чем проблема: в слабом железе или в неоптимизированном коде? Использование Ардуино IDE накладывает определенные ограничения в скорости работы.
Это цена, которую мы платим за простоту написания команд и языка в целом. А что если попробовать написать команды, которые работают на прямую с микроконтроллером? Каков будет результат? Об этом мы увидим в видео. Библиотека CyberLib: https://istarik.ru/blog/arduino/1.html Очень полезные статьи: https://geektimes.ru/post/255744/ http://robotosha.
ru/arduino/digitalwrite-optimizing-arduino.html http://microsin.net/programming/avr/accessing-avr-ports-with-winavr-gcc.html http://4a4ik.blogspot.com/2014/05/avr.html http://easyelectronics.ru/avr-uchebnyj-kurs-ustrojstvo-i-rabota-portov-vvoda-vyvoda.html http://mkprog.ru/avr/avr-dlya-nachinayushhih-urok-3-porty-vvoda-vyvoda.
html Друзья, поддержите канал! R340967932571 Z422687519909 U139735106020 4149625807630874 Приват Банк (Украина) Полезные ссылки: Модуль Micro SD: http://ali.pub/1tkx36 TFT дисплеи: http://ali.pub/1ny2rc Ардуино Мега 2560: http://ali.pub/1ny34r Самые дешевые Ардуино Про Мини для поделок: http://ali.pub/1ny2z4 Макетка: http://ali.pub/1ny3ae Перемычки: http://ali.
pub/1ny3la Качественные перемычки: http://ali.pub/1ny3j5 Самый удобный Кешбек: LetyShops: https://goo.
gl/Ev5XgI Самый выгодный Кешбек на алиекспресс: Ecomerce: https://goo.gl/3dDc2w
6 лет назад
Источник: https://1-tube.ru/watch/ArM9e5pOPEs