Работа с параметрами в eeprom, как не износить память

Содержание

Введение

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

А именно поговорим о том, как хранить параметры, которые необходимо писать в EEPROM постоянно.

Многим может показаться, что это очень специфическая проблема, но на самом деле множество устройств именно этим и занимаются — постоянно пишут в EEPROM. Счетчик воды, тепловычислитель, одометр, всяческие журналы действий пользователя и журналы, хранящие историю измерений, да просто любое устройство, которое хранит время своей работы.

Особенность таких параметров заключается в том, что их нельзя писать просто так в одно и то же место EEPROM, вы просто израсходуете все циклы записи EEPROM. Например, если, необходимо писать время работы один раз в 1 минуту, то нетрудно посчитать, что с EEPROM в 1 000 000 циклов записей, вы загубите его меньше чем за 2 года. А что такое 2 года, если обычное измерительное устройство имеет время поверки 3 и даже 5 лет.

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

Поэтому давайте попробуем решить эту проблему, и сделать так, чтобы обращение к параметрам было столь же простым как в прошлой статье, но при этом EEPROM хватало бы на 30 лет, ну или на 100 (чисто теоретически).

Итак, в прошлой статье, я с трудом показал, как сделать, так, чтобы с параметрами в EEPROM можно было работать интуитивно понятно, не задумываясь, где они лежат и как осуществляется доступ к ним

Напомню:

Для начала проясню, для чего вообще нужно обращаться по отдельности к каждому параметру, этот момент был упущен в прошлой статье. Спасибо товарищам @Andy_Big и @HiSER за замечания.

Все очень просто, существует огромный пласт измерительных устройств, которые используют полевые протоколы такие как HART, FF или PF, где пользовательские команды очень атомарные. Например, в HART протоколе есть отдельные команды — запись единиц изменения, запись верхнего диапазона, запись времени демпфирования, калибровка нуля, запись адрес опроса и т.д. Каждая такая команда должна записать один параметр, при этом успеть подготовить ответ и ответить. Таких параметров может быть до 500 — 600, а в небольших устройствах их около 200.

Если использовать способ, который пользователь @HiSER- это будет означать, что для перезаписи одного параметра размером в 1 byte, я должен буду переписать всю EEPROM. А если алгоритм контроля целостности подразумевает хранение копии параметров, то для 200 параметров со средней длиной в 4 байта, мне нужно будет переписать 1600 байт EEPROM, а если параметров 500, то и все 4000.

Малопотребляющие устройства или устройства, питающиеся от от токовой петли 4-20мА должны потреблять, ну скажем 3 мА, и при этом они должны иметь еще достаточно энергии для питания модема полевого интерфейса, графического индикатора, да еще и BLE в придачу. Запись в EEPROM очень энергозатратная операция. В таких устройствах писать нужно мало и быстро, чтобы средний ток потребления был не высоким.

Очевидно, что необходимо, сделать так, чтобы микроконтроллер ел как можно меньше. Самый простой способ, это уменьшить частоту тактирования, скажем до 500 КГц, или 1 Мгц (Сразу оговорюсь, в надежных применениях использование режима низкого потребления запрещено, поэтому микроконтроллер все время должен работать на одной частоте). На такой частоте, простая передача 4000 байт по SPI займет около 70 мс, прибавим к этому задержку на сохранение данных в страницу (в среднем 7мс на страницу), обратное вычитывание, и вообще обработку запроса микроконтроллером и получим около 3 секунд, на то, чтобы записать один параметр.

Поэтому в таких устройствах лучше чтобы доступ к каждому параметру был отдельным, и обращение к ним должно быть индивидуальным. Их можно группировать в структуру по смыслу, или командам пользователя, но лучше, чтобы все они не занимали больше одной страницы, а их адреса были выравнены по границам страницы.

Но вернемся к нашей основной проблеме — мы хотим постоянно писать параметры.

Принцип действия

Принцип работы EEPROM основан на изменении и регистрации электрического заряда в изолированной области (кармане) полупроводниковой структуры.

Ячейка памяти EEPROM представляет собой транзистор, в котором затвор выполняется из поликристаллического кремния. Затем этот затвор окисляется и в результате он будет окружен оксидом кремния — диэлектриком с прекрасными изолирующими свойствами. Изменение заряда («запись» и «стирание») производится приложением между затвором и истоком большого потенциала, чтобы напряженность электрического поля в тонком диэлектрике между каналом транзистора и карманом оказалась достаточна для возникновения туннельного эффекта. Для усиления эффекта туннелирования электронов в карман при записи применяется небольшое ускорение электронов путём пропускания тока через канал полевого транзистора (явление инжекции горячих носителей). После снятия программирующего напряжения индуцированный заряд остаётся на плавающем затворе, и, следовательно, транзистор остаётся в проводящем состоянии. Заряд на его плавающем затворе может храниться десятки лет.Чтение выполняется полевым транзистором, для которого карман выполняет функцию затвора. Потенциал плавающего затвора изменяет пороговые характеристики транзистора, что и регистрируется цепями чтения.

Ранее подобная конструкция ячеек применялась в ПЗУ с ультрафиолетовым стиранием (EPROM).Сейчас особенностью классической ячейки EEPROM можно назвать наличие второго транзистора, который помогает управлять режимами записи и стирания. Стирание информации производится подачей на программирующий затвор напряжения, противоположного напряжению записи. В отличие от ПЗУ с ультрафиолетовым стиранием, время стирания информации в EEPROM памяти составляет около 10 мс. Структурная схема энергонезависимой памяти с электрическим стиранием не отличается от структурной схемы масочного ПЗУ. Единственное отличие — вместо плавкой перемычки используется описанная выше ячейка.

Некоторые реализации EEPROM выполнялись в виде одного трёхзатворного полевого транзистора (один затвор плавающий и два обычных). Эта конструкция снабжается элементами, которые позволяют ей работать в большом массиве таких же ячеек. Соединение выполняется в виде двумерной матрицы, в которой на пересечении столбцов и строк находится одна ячейка. Поскольку ячейка EEPROM имеет третий затвор, то, помимо подложки, к каждой ячейке подходят 3 проводника (один проводник столбцов и 2 проводника строк).

Упрощенная структурная схема EEPROM.

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. удалив из кода строчки для записи данных, вы можете каждый раз при запуске программы считывать все сохраненные данные из ПЗУ Ардуино.

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.

Example: Arduino EEPROM remember stored LED state

In this example, we’re going to show you how to make the Arduino remember the stored LED state, even when we reset the Arduino or the power goes off.

The following figure shows what we’re going to exemplify:

Parts required

Here’s the parts required for this project (click the links below to find the best price at Maker Advisor):

  • Arduino UNO – read Best Arduino Starter Kits
  • 1x LED 
  • 1x 220Ω resistor 
  • 1x Pushbutton 
  • 1x 1kΩ resistor 
  • 1x Breadboard 
  • Jumper wires 

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Here’s the circuit schematics for this project. This is just a pushbutton that will turn an LED on and off.

Code

Copy the following code to the Arduino IDE and upload it to your Arduino board. Make sure you have the right board and COM port selected.

This is a debounce code that changes the LED state every time you press the pushbutton. But there’s something special about this code – it remembers the saved LED state, even after resetting or powering of the Arduino.

Basically, we save the current LED state in the ledState variable and save it to the EEPROM with the following line:

At the beginning of the code on the setup(), we check the ledState saved on EEPROM and set the led on or off accordingly to that state when we restart the program. We do that with a function we’ve created at the end of the code, checkLedState()

How we’re going to write a String into EEPROM

Writing an Arduino String into the EEPROM memory is not something that you can do out of the box.

You can only write bytes into the EEPROM. A String is an object which may contain many bytes. Also, be sure to understand that the String data type is specific to the Arduino language. It’s different from std::string and any other string data type you may find elsewhere.

To save a String into the EEPROM, we’ll have to write each byte separately, one by one.

Also, when you write the String, you know how long it is. But if you upload another program to read a String you previously stored, how can you know how many bytes you have to read from EEPROM?

To solve this issue, every time we’ll write a String to EEPROM, we’ll first save the length of the String.

This will make things easier to handle:

  • When you write a String, first you write the length, and then you write each byte in a different address – incrementing the address for each byte.
  • When you read a String, first you get the first byte which gives you the length. Then you know how many bytes to read, and from that you can retrieve the String from all the following bytes.

Methods

extEEPROM myEEPROM(kbits_256, 2, 64);
byte i2cStat = myEEPROM.begin(extEEPROM::twiClock400kHz);
if ( i2cStat !=  ) {
	//there was a problem
}
byte myData;
//write 10 bytes starting at location 42
byte i2cStat = myEEPROM.write(42, &data, 10);
if ( i2cStat !=  ) {
	//there was a problem
	if ( i2cStat == EEPROM_ADDR_ERR) {
		//bad address
	}
	else {
		//some other I2C error
	}
}

###write(unsigned long addr, byte value)
#####Description
Writes a single byte to external EEPROM.
#####Syntax

#####Parameters
addr (unsigned long): The EEPROM location to write.values (byte): The value to write.
#####Returns
Same as multiple-byte write() above.
#####Example

//write the value 16 to EEPROM location 314.
byte i2cStat = myEEPROM.write(314, 16);
byte myData;
//read 10 bytes starting at location 42
byte i2cStat = myEEPROM.read(42, &data, 10);
if ( i2cStat !=  ) {
	//there was a problem
	if ( i2cStat == EEPROM_ADDR_ERR) {
		//bad address
	}
	else {
		//some other I2C error
	}
}

#####Example

int myData;
//read a byte from location 42
int readValue = myEEPROM.read(42);
if ( readValue <  ) {
	//there was a problem
	if ( -readValue == EEPROM_ADDR_ERR) {
		//bad address
	}
	else {
		//some other I2C error
	}
}
else {
	//data read ok
}

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 Basics

Before we get into the hookup it’s probably a good idea to familiarize ourselves with EEPROM and the history of ROM in general. That said, if you don’t nerd-out on computer history it’s probably safe to skip that section.

What is ROM?

Read-Only Memory (ROM) is a type of computer memory which, generally speaking, is only programmed once (or very occasionally) and then gets read from the rest of the time. This is because it’s very slow — or impossible — to write new data to ROM. The trade-off for very slow write times — traditionally — is that it’s also non-volatile meaning that the data doesn’t go away when power is removed from the device. This makes it ideal for things like firmware which need to be «remembered» by the computer, but never actually change. The BIOS in your PC is stored on a form of ROM.

A Brief History of ROM

Early «Stored-Program» type computers — such as desk calculators and keyboard interpreters — began using ROM in the form of Diode Matrix ROM. This was memory made up of discrete semiconductor diodes placed on a specially organized PCB. This gave way to Mask ROM with the advent of integrated circuits. Mask ROM was a lot like Diode Matrix ROM only it was implemented on a much smaller scale. This meant, however, that you couldn’t just move a couple of diodes around with a soldering iron and reprogram it. Mask ROM had to be programmed by the manufacturer and was thereafter not alterable.

Unfortunately, Mask ROM was expensive and took a long time to produce because each new program required a brand new device to be manufactured by a foundry. In 1956, however, this problem was solved with the invention of PROM (Programmable ROM) which allowed developers to program the chips themselves. That meant manufacturers could produce millions of the same unprogrammed device which made it cheaper and more practical. PROM, however, could only be written to once using a high-voltage programming device. After a PROM device was programmed, there was no way to return the device to its unprogrammed state.

A UV Erasable Microcontroller. The window gives it away.

This changed in 1971 with the invention of EPROM (Erasable Programmable ROM) which — besides adding another letter to the acronym — brought with it the ability to erase the device and return it to a «blank» state using a strong UV light source. That’s right, you had to shine a bright light on the IC to reprogram it, how cool is that? Well, it turns out it’s pretty cool unless you’re a developer working on firmware in which case you’d really like to be able to reprogram the device using electrical signals. This finally became a reality in 1983 with the development of EEPROM (Electrically Erasable Programmable ROM) and with that, we arrive at the current day unwieldy acronym.

Quirks of EEPROM

There are two major drawbacks to EEPROM as a method of data storage. In most applications the pros outweigh the cons, but you should be aware of them before incorporating EEPROM into your next design.

First of all, the technology that makes EEPROM work also limits the number of times that it can be re-written. This has to do with electrons becoming trapped in the transistors that make up the ROM and building up until the charge difference between a «1» and a «0» is unrecognizable. But don’t worry, most EEPROMs have a maximum re-write number of 1 million or more. As long as you’re not continuously writing to the EERPROM it’s unlikely you’ll hit this maximum.

Secondly, EEPROM will not be erased if you remove power from it, but it won’t hold onto your data indefinitely. Electrons can drift out of the transistors and through the insulator, effectively erasing the EEPROM over time. That said, this usually occurs over the course of years (although it can be accelerated by heat). Most manufacturers say that your data is safe on EEPROM for 10 years or more at room temperature.

Функция чтения 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

Чтобы было понятней, как работает EEPROM и как ее использовать, попробуем реализовать небольшой пример. Подключим к ардуино подстроечный резистор, кнопку и пять светодиодов(не забыв про резисторы, чтобы не сжечь светодиоды). И напишем небольшой скетч: по нажатию на кнопку будем считывать значения с подстроечного резистора и конвертировать их в число от 0 до 4х. По повторному нажатию на кнопку, будем сохранять полученное значение в EEPROM. И уже в зависимости от записанного значения, зажжем соответствующее количество светодиодов. Если после перезагрузки или после отключения питания arduino, будет гореть нужное количество огоньков, значит все работает и эксперимент можно считать успешным.
Чтобы было понятней, ниже приведена схема подключения всех элементов.

Single Type Method Sketch1

The Idea here is to store a set of simple type variables sequentially in the EEPROM at a specific EEPROM address.

Just attach a push button connected to ground and pin 5 of the
Arduino. On start up the EEPROM values are retrieved from the EEPROM and
sent to serial Monitor.

When you push the button random values are saved to the EEPROM. To
retrieve the values simply press the reset button on the Arduino and
these same numbers are displayed (having been read from the EEPROM).

When you hit the button you can also see write execution time.

Copy Sketch

Here’s an example of the output from the serial monitor:

Press button to write to EEPROM
EEPROM Written
MIN x 58478
MAX x 58479
MIN y 58480
MAX y 58481
EEPROM Write time  (us) 23300
EEPROM Write time per byte (us) 2912
Press button to write to EEPROM
Press button to write to EEPROM
Press button to write to EEPROM
Press button to write to EEPROM
EEPROM variable read and write.
MIN x 58478
MAX x 58479
MIN y 58480
MAX y 58481
Press button to write to EEPROM

Библиотеки для работы с датой и временем ардуино

Библиотека RTClib

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

Пример использования:

#include <RTClib.h>

RTC_DS1307 RTC; – выбор датчика (в данном случае DS1307).

rtc.adjust(DateTime( Date, Time)); – настройка времени и календаря.

dayOfTheWeek () – вывод дня недели. Аргумент от 0 до 6, 0 – воскресенье.

Библиотека Timelib

Позволяет Ардуино получать информацию о дате и времени в данный момент.

Пример использования:

#include <TimeLib.h>

Time(); – создание экземпляра.

setTime (t); – установка времени. Аргумент t – час, минута, секунда, день, месяц и год.

timeStatus(); – показывает, установлено ли время.

adjustTime(adjustment); – настройка времени.

Библиотека Ds1307

Библиотека для удобного взаимодействия часов DS1307 с Ардуино c использованием библиотеки Wire.

Пример использования:

#include <DS1307RTC.h>

class DS1307RTC – создание объекта DS1307.

SetTime() – установка времени.

get() – считывает RTC, возвращает полученную дату в формате POSIX.

Set(time_t t) – запись даты в RTC

Библиотека DS 3231

Предназначена для управления датой и временем в модуле ds3231.

#include “ds3231.h”

DS3231  Clock(SDA, SCL); – создание объекта DS3231, подключение к линии тактирования и линии данных.

getTime(); – считывание даты и времени с часов.

setDate(date, mon, year); – установка даты.

Запись EEPROM

Для записи ячейки памяти пользователь должен записать адрес в EEARL, а данные — в EEDR. Если биты EEPMn равны 0b10, то запись EEPE (в течение четырех тактов после записи EEMPE) вызовет только операцию записи (время программирования приведено выше в таблице 5-1). В EEPE бит остается установленным до тех пор, пока операция записи не завершится. Если записываемая ячейка не была очищена (стерта) перед записью, сохраненные данные должны считаться потерянными. Пока микроконтроллер занят программированием, невозможно выполнить какие-либо другие операции с EEPROM.

Калибровка генератора тактовой частоты с помощью регистра калибровки (Oscillator Calibration Register) используется для настройки времени доступа к EEPROM. Убедитесь, что частота генератора соответствует требованиям, описанным в разделе “OSCCAL – Oscillator Calibration Register » (в оригинальной документации стр. 27, на моём сайте будет описана позже).

В следующих примерах кода показана одна функция на Ассемблере и C для стирания, записи или атомарной записи EEPROM. В примерах предполагается, что прерывания управляются (например, отключением прерываний глобально) таким образом, что во время выполнения этих функций прерывания не будут происходить.

EEPROM_write:
; Дождаться завершения предыдущей записи
sbic EECR, EEPE
rjmp EEPROM_write
; Установить режим программирования
ldi r16, (0<<EEPM1)|(0<<EEPM0)
out EECR, r16
; Записать начальный адрес (r17) в регистр адреса
out EEARL, r17
; Записать данные (r16) в регистр данных
out EEDR, r16
; Записать логическую единицу в EEMPE
sbi EECR, EEMPE
; Начать запись EEPROM, установив EEPE
sbi EECR, EEPE
ret
void EEPROM_write(unsigned char ucAddress, unsigned char ucData)
{
/* Дождаться завершения предыдущей записи */
while(EECR & (1<<EEPE)) ;
/* Установить режим программирования */
EECR = (0<<EEPM1)|(0>>EEPM0)
/* Настроить регистры адреса и данных */
EEARL = ucAddress;
EEDR = ucData;
/* Записать логическую единицу в EEMPE */
EECR |= (1<<EEMPE);
/* Начать запись EEPROM, установив EEPE */
EECR |= (1<<EEPE);
}

См. также пример здесь.

В следующих примерах кода показаны функции на Ассемблере и C для чтения EEPROM. В примерах предполагается, что прерывания управляются таким образом, чтобы во время выполнения этих функций не возникало прерываний.

EEPROM_read:
; Дождаться завершения предыдущей записи
sbic EECR, EEPE
rjmp EEPROM_read
; Записать начальный адрес (r17) в регистр адреса
out EEARL, r17
; Начать запись EEPROM, записав EERE
sbi EECR, EERE
; Читать данные из регистра данных
in r16, EEDR
ret
unsigned char EEPROM_read(unsigned char ucAddress)
{
/* Дождаться завершения предыдущей записи */
while(EECR & (1<<EEPE))
;
/* Настроить регистра адреса */
EEARL = ucAddress;
/* Начать чтение EEPROM, записав EERE */
EECR |= (1<<EERE);
/* Вернуть данные из регистра данных */
return EEDR;
}

См. также пример здесь.

Документация функций

void
eeprom_read_block

void * 

pointer_ram,

const void * 

pointer_eeprom,

size_t 

n

Считывает
блок из n байт
по адресу EEPROM pointer_eepromв буфер pointer_ram. Для
константы n <= 256
байт
библиотечная
функция
используется.
Для
неизвестного
размеров
блока во
времени
компиляции
или размерах
блока > 256
встроенный
цикл расширен.

Пример
использования:
#include<stdio.h>

#include
<avr/eeprom.h>

char
pointer_ram ;

void
main(void)

{

eeprom_read_block(pointer_ram, 0x01, 10);

}

eeprom_read_byte

const * 

addr

 ) 

Считывает
один байт по
адресу EEPROM addr

Пример
использования:
#include <stdio.h>

#include
<avr/eeprom.h>

unsigned
char b;

void
main(void)

{

b
= eeprom_read_byte(0x01);

}

eeprom_read_word

const * 

addr

 ) 

Считывает
16-тиразрядное
слово по
адресу EEPROM addr.

Пример
использования:
#include<stdio.h>

#include<avr/eeprom.h>

unsignedintw;

void
main(void)

{

w
= eeprom_read_word(0x01);

}

void eeprom_write_block

const void * 

pointer_ram,

void * 

pointer_eeprom,

size_t 

n

Записывает
блок из n
байт из
буфера pointer_ramпо адресу EEPROM pointer_eeprom.

Пример
использования:
#include<stdio.h>

#include<avr/eeprom.h>

charpointer_ram =»0123456789″;

void
main(void)

{

eeprom_write_block(pointer_ram,0x01,5);

}

void eeprom_write_byte

addr,

value

Записывает
байт value по
адресу EEPROM addr.

Пример
использования:
#include<stdio.h>

#include<avr/eeprom.h>

unsignedcharb=10;

void
main(void)

{

eeprom_write_byte(0x01,b);

}

void eeprom_write_word

addr,

value

Записывает16-тиразрядноесловоvalueпоадресуEEPROM addr.

Пример
использования:
#include<stdio.h>

#include
<avr/eeprom.h>

unsigned
int w=60000;

void
main(void)

{

eeprom_write_word(0x01,
w) ;

}

License

The MIT License (MIT)

Copyright (c) 2016 Diogo Miguel Rodrigues

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the «Software»), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED «AS IS», WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.