Работа со встроенной EEPROM в микроконтроллерах на Arduino

Добавлено 11 апреля 2016 в 22:30

Микроконтроллеры 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()

Описание
Считывает байт из EEPROM. Ячейки, которые никогда не были записаны, содержат значение 255.
Синтаксис
EEPROM.read(address)
Параметры
address: адрес ячейки для считывания, начинается с 0 (int).
Возвращаемое значение
Значение, записанное в ячейку (byte).
Пример
/*
 * EEPROM Read
 *
 * Считывает значение каждого байта в EEPROM и выводит его
 * на компьютер.
 */

#include <EEPROM.h>

// начать чтение с первого байта (адрес 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("\t");
  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.h>

/** текущий адрес в 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.h>

/** текущий адрес в 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 <EEPROM.h>

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 <EEPROM.h>

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! \n\nView 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 <EEPROM.h>

void setup(){

  unsigned char val;

  // Прочитать первую ячейку EEPROM.
  val = EEPROM[ 0 ];

  // Записать первую ячейку EEPROM.
  EEPROM[ 0 ] = val;

  // Сравнить содержимое
  if( val == EEPROM[ 0 ] ){
    // Сделать что-то...
  }
}

void loop(){ /* Пустой цикл */ }

Теги

ArduinoEEPROMПамятьПрограммирование

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.