Использование eeprom на arduino

Содержание

Downloading

This package can be downloaded in different manners

  • By directly loading fetching the Archive from GitHub:
  1. Go to https://github.com/thijse/Arduino-EEPROMEx
  2. Click the DOWNLOAD ZIP button in the panel on the
  3. Rename the uncompressed folder Arduino-EEPROMEx-master to EEPROMEx.
  4. You may need to create the libraries subfolder if its your first library.
  5. Place the EEPROMEx library folder in your arduinosketchfolder/libraries/ folder.
  6. Restart the IDE.

If you want to have a package that includes all referenced libraries, use the pre-packaged library

  1. Download the package as a zipfile here or as a tarball here .
  2. Copy the folders inside the libraries folder to you your arduinosketchfolder/libraries/ folder.
  3. Restart the IDE.

Using different data formats

The aim of the library is to also support other standard data types: it currently implements writing and reading to int, long, float and double.

For writing:

The update functions are different from the write functions: they will check per byte if the current value differs and only update the the cell with a different value. This will not only reduce wear, and can also significantly reduce write time.

Manipulating Single bits

The following functions implements reading and writing single bits:

Where bit is the write position in the byte, ranging from , with bit 0 being the right-most. The return value is the read bit.

Data blocks

Using the block functions any data can be read, written and updated:

where T is the type of the data to read/write/update. This can be a basic type, but also a more complex type like a struct. The return value gives the number of bytes that have been read, written or updated.

One can also read/write arrays of data-blocks:

Debugging EEPROM applications

It is easy to burn out a memory cell in few minutes, so during debugging it would be very useful to limit the number of allowed writes. It is easy to put a bracket at the wrong location, and placing an EEPROM write inside of a loop, rather than outside, and introduce extensive writing causing wear. The following function helps limit the number of writes.

More writes than allowed will be refused and result in an error message. You can also set the address range used by the library:

The lower value is used by the getAddress function, the upper value is used for setting the EEPROM size. Writing outside the maximum size will result in an error message. The following EEPROM sizes are predefined

Based on processor:

Based on board:

EEPROM performance

All of the read/write functions make sure the EEPROM is ready to be accessed. Since this may cause a delay of max 4ms, time-critical applications should first poll the EEPROM e. g. using the isReady function before attempting any actual I/O:

Read and Write

You can easily read and write into the EEPROM using the EEPROM library.

To include the EEPROM library:

Write

To write data into the EEPROM, you use the EEPROM.write() function that takes in two arguments. The first one is the EEPROM location or address where you want to save the data, and the second is the value we want to save:

For example, to write 9 on address 0, you’ll have:

Read

To read a byte from the EEPROM, you use the EEPROM.read() function. This function takes the address of the byte has an argument.

For example, to read the byte stored previously in address 0.:

This would return 9, which is the value stored in that location.

Update a value

The EEPROM.update() function is particularly useful. It only writes on the EEPROM if the value written is different from the one already saved.

As the EEPROM has limited life expectancy due to limited write/erase cycles, using the EEPROM.update() function instead of the EEPROM.write() saves cycles.

You use the EEPROM.update() function as follows:

At the moment, we have 9 stored in the address 0. So, if we call:

It won’t write on the EEPROM again, as the value currently saved is the same we want to write.

Предотвращение повреждения EEPROM

Если напряжение питания VCC слишком низкое (например, во время включения питания), данные в EEPROM можно повредить, потому что нарастание напряжения может оказаться слишком медленным для процессора и EEPROM, что не позволит им работать правильно. Эти вопросы такие же, как для систем, использующих память EEPROM, и должны быть применены такие же проектные решения.

Повреждение данных EEPROM может быть вызвано двумя ситуациями, когда напряжение слишком низкое. Во-первых, последовательная запись в EEPROM требует определённого напряжения для правильной работы. Во-вторых, сам процессор может неправильно выполнять инструкции, если напряжение питания слишком низкое.

Повреждения данных EEPROM можно легко избежать, следуя этому совету по проектированию:

Поддерживайте активный (низкий) сброс AVR во время недостаточного напряжения питания. Это можно сделать, включив внутренний детектор (Brown-out Detector (BOD)). Если уровень напряжения BOD не соответствует необходимому уровню напряжения, то можно использовать внешнюю цепь сброса, поддерживающую низкий уровень на выводе сброса и предохраняющую EEPROM от порчи. Если сброс происходит во время выполнения операции записи, операция записи будет завершена при условии, что напряжение питания достаточно.

ПРИМЕЧАНИЕ
Обычно все типовые схемы предусматривают такую защиту без дополнительного программирования (без использования BOD). Самое простое решение — подключить вход сброса микроконтроллера к плюсу источника питания через резистор, а к минусу — через конденсатор. При подаче питания на входе сброса будет низкий уровень, пока не зарядится конденсатор. То есть микроконтроллер не запустится, пока не зарядится конденсатор. Остаётся только рассчитать номиналы резистора и конденсатора, но это уже другая тема…

Accessing AVR EEPROM memory

Standard C functions don’t understand how one or another memory is accessed. So reading and writing EEPROM has to be done by following a special logical system. Simple  EEPROM byte read and write operations have to be done via special registers. Atmega328 has the following registers to control EEPROM:

  • 16-bit EEAR (EEARH and EEARL) – EEPROM address register;
  • EEDR – EEPROM 8-bit data register;
  • EECR – EEPROM control register.

As Atmega328 has 1K of EEPROM memory highest cell address will be 0x3FF, meaning that only 10 bits of 16-bit EEAR register are needed. The EEPROM writing process is controlled via the EECR register. To avoid failures, there is some sequence of the writing process:

  • Wait until EEPE bit becomes zero;
  • Write EEPROM address to EEAR register;
  • Write data to EEDR;
  • Set EEMPE bit while keeping EEPE bit zero;
  • In four clock cycles, set EEPE bit to initiate writing.

You will find a nice read and write examples in the datasheet, so there is no need to repeat this. Let’s do a more interesting thing – use an interrupt to write to EEPROM. As you may know, Atmega328 has one dedicated interrupt EE_READY_vect, that may be set to occur whenever EEPROM becomes ready for writing or reading. Usually, you would have to poll for EEPE bit become zero in the loop – this requires active processing power. Interrupt-driven EEPROM writing may be more efficient, especially when memory blocs have to be accessed. Let’s write a simple Interruption-driven AVR EEPROM writing routine and write a simple message string.

#include <avr/io.h>
#include <avr/interrupt.h>
//EEPROM address initial 0
volatile uint16_t eepromaddress;
//string array index initial 0
volatile uint8_t i;
//message to be written to EEPROM
uint8_t message[] ="Write to EEPROM";
//EEPROM writing ISR
ISR(EE_READY_vect)
{
/*check if not end of string and address
didn't reach end of EEPROM*/
if ((message)&&(eepromaddress<=E2END))
{
    //loads address and increments for next load
    EEAR=eepromaddress++;
    //loads current byte and increments index for next load
    EEDR=message;
    //master write enable
    EECR|=(1<<EEMPE);
    //strobe eeprom write
    EECR|=(1<<EEPE);
}
else
{
    //disable eeprom ready interrupt
    EECR &= ~(1<<EERIE);
}
}
int main(void)
{
//Enable eeprom ready interrupt
EECR |= (1<<EERIE);
//enable global interrupts
sei();
    while(1)
    {
        //do other tasks or sleep
    }
}

As you can see, ISR takes bytes from the message buffer and writes them to EEPROM memory until the buffer is empty or the EEPROM end is reached. Using EE_READY interrupt leaves EEPROM writing completely to hardware and frees MCU resources for other tasks. In other words, EEPROM itself asks for another byte when ready. This way, you improve performance and reduce power consumption. The same interrupt source can be used for reading EEPROM.

EECR – Регистр управления EEPROM (EEPROM Control Register)


  • Бит 7 – Res: зарезервированный бит.
    Этот бит зарезервирован для будущего использования и всегда будет
    считываться как 0 в ATtiny13A. Для совместимости с будущими устройствами AVR всегда записывайте этот бит в ноль. После прочтения замаскируйте этот бит.
  • Бит 6 – Res: зарезервированный бит.
    Этот бит зарезервирован для будущего использования и всегда будет
    считываться как 0 в ATtiny13A.
  • Биты 5 и 4 – EEPM: биты режима программирования EEPROM. Установка битов режима программирования EEPROM определяет, какое действие программирования будет выполняться при записи единицы в бит EEPE. Можно запрограммировать данные в одной атомарной операции (стереть старое значение и запрограммировать новое значение) или выполнить операции стирания и записи в двух разных операциях. Время программирования для различных режимов показано в таблице 5-1 (ниже). Если бит EEPE (см. ниже) установлен, то любая запись в EEPMn будет отклонена. Во время сброса биты EEPMn будут сброшены в значение 0b00, если EEPROM не занят программированием.
  • Бит 3 – EERIE: включить прерывание EEPROM (EEPROM Ready Interrupt Enable).
    Запись единицы в бит EERIE позволяет включить прерывания EEPROM,
    если установлен I-бит в регистре . Запись нуля в бит EERIE отключает прерывание. Флаг готовности прерывания EEPROM вызывает прерывание, когда энергонезависимая память готова для программирования.
  • Бит 2 – EEMPE: мастер включения программирования EEPROM (EEPROM Master Program Enable). Бит EEMPE определяет, будет ли запись единицы в бит EEPE иметь эффект или нет. Когда бит EEMPE установлен, установка бита EEPE в течение четырех тактов запрограммирует EEPROM по выбранному адресу. Если EEMPE равен нулю, установка EEPE не будет иметь никакого эффекта. Если EEMPE был установлен с помощью программного обеспечения, аппаратное обеспечение очищает бит (сбрасывает в ноль) после четырех тактов.
  • Бит 1 – EEPE: включение программирования (записи) EEPROM. Бит EEPE — это сигнал для EEPROM. Если бит EEPE установлен, то EEPROM будет запрограммирована согласно состоянию битов EEPMn. Бит EEMPE должен быть установлен, прежде чем логическая единица будет записана в EEPE, иначе запись EEPROM не произойдёт. Когда время доступа к записи истекло, бит EEPE очищается аппаратным обеспечением. После установки EEPE процессор останавливается на два такта, прежде чем будет выполнена следующая инструкция.
  • Бит 0 – EERE: включение чтения EEPROM. Бит EERE — разрешение чтения из EEPROM. Если в регистр EEARL записан правильный адрес, то бит EERE должен быть записан в единицу, чтобы вызвать чтение EEPROM. Доступ чтения из EEPROM принимает одну инструкцию, а запрошенные данные доступны немедленно. Когда EEPROM считывается, процессор останавливается на четыре такта до выполнения следующей инструкции. Пользователь должен опросить бит EEPE перед началом операции чтения. Если операция записи выполняется, невозможно ни прочитать EEPROM, ни изменить регистр EEARL.

На рисунках:

  • Сверху указаны номера битов регистра.
  • Слева надписи:
    • Адрес регистра.
    • Read/Write — это чтение/запись. Буква под битом говорит о том, какой доступ возможен к этому биту: R — только чтение, W — только запись, R/W — чтение и запись.
    • Начальное значение (Initial Value) — значение, которое изначально (по умолчанию) хранится в бите. Если Х — то значение может быть любым (не определено).

Таблица 5.1. Биты режимов EEPROM.

EECR Время программирования Операция
EEPM1 EEPM0
3,4 мс Стирание и запись за одну операцию (Атомарная операция)
1 1,8 мс Только стирание
1 1,8 мс Только запись
1 1 Зарезервировано для будущего использования

Applications in Arduino projects – Remember last state

The EEPROM is useful in Arduino projects that need to keep data even when the Arduino resets or when power is removed.

It is specially useful to remember the last state of a variable or to remember how many times an appliance was activated.

For example, imagine the following scenario:

  • You’re controlling a lamp with your Arduino and the lamp is on;
  • The Arduino suddenly loses power;
  • When the power backs on, the lamp stays off – it doesn’t keep its last change.

You don’t want this to happen. You want the Arduino to remember what was happening before losing power and return to the last state.

To solve this problem, you can save the lamp’s state in the EEPROM and add a condition to your sketch to initially check whether the state of the lamp corresponds to the state previously saved in the EEPROM.

We’ll exemplify this with an example later in this post in the Example: Arduino EEPROM remember stored LED state.

Detailed Description

#include <avr/eeprom.h>

This header file declares the interface to some simple library routines suitable for handling the data EEPROM contained in the AVR microcontrollers. The implementation uses a simple polled mode interface. Applications that require interrupt-controlled EEPROM access to ensure that no time will be wasted in spinloops will have to deploy their own implementation.

Notes:
  • In addition to the write functions there is a set of update ones. This functions read each byte first and skip the burning if the old value is the same with new. The scaning direction is from high address to low, to obtain quick return in common cases.
  • All of the read/write functions first make sure the EEPROM is ready to be accessed. Since this may cause long delays if a write operation is still pending, time-critical applications should first poll the EEPROM e. g. using before attempting any actual I/O. But this functions are not wait until SELFPRGEN in SPMCSR becomes zero. Do this manually, if your softwate contains the Flash burning.
  • As these functions modify IO registers, they are known to be non-reentrant. If any of these functions are used from both, standard and interrupt context, the applications must ensure proper protection (e.g. by disabling interrupts before accessing them).
  • All write functions force erase_and_write programming mode.
  • For Xmega the EEPROM start address is 0, like other architectures. The reading functions add the 0x2000 value to use EEPROM mapping into data space.

Результат

Собственно все, теперь мы можем регистрировать в списке любые параметры:

Замечу, что пользователю параметров нужно только объявить параметр и список, а вся портянка с кодом, до этого, пишется один раз. Используются параметры точно также как и .

Что произойдет в этом примере, когда мы будем писать 10,11,12…15 в наш параметр. Каждый раз при записи, адрес параметра будет смещаться на размер параметра + размер индекса + размер копии параметра и индекса. Как только количество записей превысит максимальное количество, параметр начнет писаться с начального адреса.

На картинке снизу как раз видно, что число 15 с индексом 5 записалось с начального адреса, а 10 теперь нет вообще.

В данном случае после сброса питания, при инициализации, будет найдена запись с индексом 5 и значением 15 и это значение и индекс будут записаны в кэшируемую копию нашего параметра.

Вот и все, надеюсь в этой статье цель получилось пояснить более детально, спасибо за то, что прочитали до конца.

Arduino EEPROM примеры использования

Для начала рассмотрим запись в EEPROM Arduino числа больше, чем 255, например число 999. При записи в EEPROM число 999 будет разбиваться на множитель (старший байт) и недостающее число (младший байт), занимая при этом уже две ячейки в энергонезависимой памяти (т.е. 999 = 3×256 + 231). Чтобы вывести сохраненное число на монитор порта, его нужно будет «собрать» с помощью функции .

Скетч. Запись в память EEPROM int, float

#include <EEPROM.h>  // импортируем библиотеку    int num = 999;                      // разбиваем число на 2 байта  byte hi  = highByte(num);   // старший байт  byte low = lowByte(num);  // младший байт    void setup() {    Serial.begin(9600);    // запускаем монитор порта       EEPROM.update(1, hi);     // записываем старший байт в ячейку 1     EEPROM.update(2, low); // записываем младший байт в ячейку 2      delay(1000);      byte val1 = EEPROM.read(1);  // считываем 1 байт по адресу ячейки    byte val2 = EEPROM.read(2);  // считываем 1 байт по адресу ячейки      Serial.println("highByte - "+String(val1));  // выводим старший байт на монитор    Serial.println("lowByte  - "+String(val2));  // выводим младший байт на монитор      int NUM = word(hi, low);       // "собираем" число из байтов    Serial.println("int num  - "+String(NUM));    // выводим полученное число  }    void loop() {  }

Пояснения к коду:

  1. для записи данных в ячейку в программе использована функция , которая перезаписывает ячейку только в случае различия сохраняемых данных с данными в ячейке EEPROM Arduino Uno;
  2. основная проблема с сохранением больших чисел (int, float) в память EEPROM заключается в том, чтобы случайно не перезаписать нужную ячейку новой информацией. Для этого нужно учитывать размер сохраняемых данных в ПЗУ, используя функции и .

Скетч. Запись строк в EEPROM (String)

#include <EEPROM.h>  // импортируем библиотеку    int address = 10;  // адрес первой ячейки для записи    long cod = 8904; // разбиваем телефонный номер на две части  long tel = 2768282;  String email = ""; // сохраняем в строке адрес почты    long COD; // создаём новые переменные для чистоты эксперимента  long TEL;  String EMAIL;    void setup() {    Serial.begin(9600);  // запускаем монитор порта      EEPROM.put(address, cod);      // сохраняем код телефона в памяти Ардуино    address += sizeof(cod);              // узнаем адрес следующей свободной ячейки    EEPROM.put(address, tel);       // сохраняем номер телефона в памяти Ардуино    address += sizeof(tel);                // узнаем адрес следующей свободной ячейки    EEPROM.put(address, email);  // сохраняем электронную почту в памяти      address = 10;  // адрес первой ячейки для чтения      Serial.print("Phone: ");  // выводим телефонный номер на монитор    Serial.print(EEPROM.get(address, COD));    address += sizeof(COD);    Serial.println(EEPROM.get(address, TEL));    address += sizeof(TEL);      Serial.print("Email: ");  // выводим электронную почту на монитор    Serial.println(EEPROM.get(address, EMAIL));  }    void loop() {  }

Пояснения к коду:

  1. перед сохранением новых данных в памяти, следует узнать размер данных, которые были сохранены, чтобы начать запись в новой ячейке;
  2. удалив из кода строчки для записи данных, вы можете каждый раз при запуске программы считывать все сохраненные данные из ПЗУ Ардуино.

Скачать стандартные библиотеки Arduino IDE на русском

Библиотеки для Ардуино делятся на две группы — стандартные и пользовательские. При установке Arduino IDE в папке Program Files\Arduino\libraries имеется набор стандартных библиотек для базовых функций видов, коммуникации платы и для подключения устройств: сервомоторов, шаговых двигателей, LCD-дисплеев и т.д. Стандартные библиотеки скачать можно на официальном сайте www.arduino.cc.

Список стандартных библиотек Arduino:

EEPROM — чтение и запись в энергонезависимую память (скачать eeprom.h)Ethernet — связь с Интернет с помощью Ethernet Shield (скачать ethernet.h)Firmata — для взаимодействия Arduino и ПК (скачать firmata.h)GSM — коммуникация по GSM/GRPS протоколу для GSM Shield (скачать gsm.h)LiquidCrystal — управление LCD дисплеем (скачать liquidcrystal.h)SD — чтение и запись в SD карту (скачать sd.h)Servo — управление серво двигателем (скачать servo.h)SPI — для взаимодействия Arduino и периферийных устройств (скачать spi.h)SoftwareSerial — коммуникация по цифровому порту (скачать softwareserial.h)Stepper — управление шаговым двигателем (скачать stepper.h)TFT — вывод текста и картинок на TFT дисплее (скачать ethernet.h)WiFi — связь с Интернет с помощью WiFi Shield (скачать wifi.h)Wire — коммуникация по протоколу I2C (скачать wire.h)

Емкость EEPROM

С созданием EEPROM, ROM (ПЗУ) больше не является устройством, доступным только для чтения, а является чем-то, на что вы можете записать снова, подобно RAM (Оперативное запоминающее устройство или Random Access Memory, дословно — случайный доступ к памяти).

EEPROM значительно медленнее оперативной памяти и имеет ограничение по количеству записей (обычно 1 миллион). Но это не должно стать проблемой, так как существует 2 версии EEPROM.

Первая версия — это версия, в которой можно параллельно записывать данные в байтовом формате, а вторая — последовательная с использованием I2C, в которой запись выполняется последовательно. Гарантированный срок хранения данных ограничен, как правило, 10 годами.

Далее появилась флеш-память, способная хранить гораздо больше данных в том же объеме. В то время как EEPROM может записываться байт за байтом или побайтово, флеш-память записывается блоками или большими фрагментами, обычно по 512 байт за раз

Обратите внимание, что количество раз, которое вы можете записать, обычно составляет 100 000, так что вам придется с этим справляться

Размер EEPROM-памяти может ввести в заблуждение, так как обычно она задается в битах, а не в байтах. 256K EEPROM может содержать 256K бит данных или всего 32K байт, около 16 страниц простого текста. Кроме того, она работает как энергонезависимая память, что означает, что она может сохранять свою память даже после повторного включения питания (в отличие от энергозависимой, которая теряет свое содержимое после выключения питания).

Размеры и типы чипов EPROM

Изготавливаются несколько вариантов EPROM, отличающиеся как по физическим размерам, так и по ёмкости памяти. Хотя партии одного типа от разных производителей совместимы по чтению данных, есть небольшие различия в процессе программирования.

Большинство чипов EPROM программисты могут распознать через «режим идентификации», подавая 12V на контакт A9 и считывая два байта данных. Однако, поскольку это не универсально, программное обеспечение также позволяет ручную настройку на производителя и тип устройства микросхемы для обеспечения правильного режима программирования.

Тип EPROM Размер — бит Размер — байт Длина (hex) Последний адрес (hex)
1702, 1702A 2 Кбит 256 100 FF
2704 4 Кбит 512 200 1FF
2708 8 Кбит 1 Кбайт 400 3FF
2716, 27C16 16 Кбит 2 Кбайт 800 7FF
2732, 27C32 32 Кбит 4 Кбайт 1000 FFF
2764, 27C64 64 Кбит 8 Кбайт 2000 1FFF
27128, 27C128 128 Кбит 16 Кбайт 4000 3FFF
27256, 27C256 256 Кбит 32 Кбайт 8000 7FFF
27512, 27C512 512 Кбит 64 Кбайт 10000 FFFF
27C010, 27C100 1 Мбит 128 Кбайт 20000 1FFFF
27C020 2 Мбит 256 Кбайт 40000 3FFFF
27C040, 27C400 4 Мбит 512 Кбайт 80000 7FFFF
27C080 8 Мбит 1 Мбайт 100000 FFFFF
27C160 16 Мбит 2 Мбайт 200000 1FFFFF
27C320 32 Мбит 4 Мбайт 400000 3FFFFF

Как работать с EEPROM, чтобы не износить её

Те кто в курсе, можете пропустить этот раздел. Для остальных краткое, чисто мое дилетантское пояснение.

Как я уже сказал, число записей в EEPROM ограничено. Это число варьируется, и может быть 100 000, а может и 1 000 000. Так как же быть, если я хочу записать параметр 10 000 000 раз? И здесь мы должны понять, как внутри EEPROM устроен доступ к ячейкам памяти.

Итак, в общем случае вся EEPROM разделена на страницы. Страницы изолированы друг от друга. Страницы могут быть разного размера, для небольших EEPROM это, скажем, 16, 32 или 64 байта. Каждый раз когда вы записываете данные по какому-то адресу, EEPROM копирует все содержимое страницы, в которой находятся эти данные, во внутренний буфер. Затем меняет данные, которые вы передали в этом буфере и записывает весь буфер обратно. Т.е. по факту, если вы поменяли 1 байт в странице, вы переписываете всю страницу. Но из-за того, что страницы изолированы друг от друга остальные страницы не трогаются.

Таким образом, если вы записали 1 000 000 раз в одну страницу, вы можете перейти на другую страницу и записать туда еще 1 000 000 раз, потом в другую и так далее. Т.е. весь алгоритм сводится к тому, чтобы писать параметр не в одну страницу, а каждый раз сдвигаться в следующую страницу. Можно закольцевать эти действия и после 10 раз, снова писать в исходную страницу. Таким образом, вы просто отводите под параметр 10 страниц, вместо 1.