Введение
При программировании микроконтроллеров AVR иногда возникает потребность сохранять данные, которые бы после выключения питания или сброса контроллера не изменяли свое значение. Для этих целей в составе AVR есть энергонезависимая память данных EEPROM (Electrically Erasable Programmable Read-Only Memory — электрически стираемое перепрограммируемое ПЗУ).
EEPROM имеет адресное пространство отличное от адресных пространств ОЗУ и flash памяти, в котором можно читать и записывать одиночные байты. В зависимости от модели микроконтроллера EEPROM может иметь объем от 512 байт (как, например, в микроконтроллере atmega16) до нескольких килобайт. Гарантированное количество циклов перезаписи этой памяти составляет не меньше 100000.
В этой статье на примере atmega16 мы разберемся, как работать с этим типом памяти, какие возможные проблемы при этом могут возникать и как с ними бороться.
Объявление переменных в EEPROM
Использование EEPROM начинается с объявления переменных, хранящиеся в этой памяти. Синтаксис объявления таких переменных отличается от объявлений обычных переменных (хранящихся в ОЗУ) только наличием ключевого слова. В зависимости от компилятора данное ключевое слово может разным.
Объявление переменной в EEPROM для IAR AVR и CodeVision AVR:
//8-ми разрядная переменная
__eeprom uint8_t setting_2
//объявление массива 16-ти разрядных переменных
__eeprom uint16_t set[8];
//объявление массива и его инициализация
__eeprom uint8_t data[3] = {11, 3, 13};
//указатель на 8-ми разрядную переменную в EEPROM, который сам хранится в RAM
uint8_t __eeprom * pSetting;
//указатель на 8-ми разрядную переменную в EEPROM, который сам храниться в EEPROM
__eeprom uint8_t __eeprom*pSetting;
Объявление переменной в EEPROM для AtmelStudio 6:
#include <avr/eeprom.h>
//8-ми разрядная переменная в EEPROM
uint8_t setting EEMEM;
//объявление массива в EEPROM
uint16_t set[8] EEMEM;
//указатель на 8-ми разрядную переменную в EEPROM, который сам хранится в RAM
uint8_t *pSetting;
//указатель на 8-ми разрядную переменную в EEPROM, который сам храниться в EEPROM
uint8_t *pSetting EEMEM;
Инициализация переменных в EEPROM
При объявлении переменных в EEPROM их можно инициализировать, то есть присвоить начальные значения.
//для IAR AVR, CodeVision AVR
__eeprom uint8_t counter = 100;
__eeprom uint16_t data[3] = {20, 08, 1981};
//для AtmelStudio 6
uint8_t counter EEMEM = 23;
uint8_t data[3] EEMEM = {21, 04, 1979};
Инициализацию переменных хранящихся в ОЗУ компилятор "запихивает" в начало программы микроконтроллера - перед вызовом функции main. И она выполняется каждый раз, когда на микроконтроллер подается питание или происходит его сброс.
С EEPROM переменными ситуация немного другая, их должен инициализировать сам пользователь путем программирования EEPROM специальным файлом (с расширением .eep).
Как сгенерировать файл для EEPROM? Если в коде есть инициализация EEPROM переменных, то AtmelStudio 6 и CodeVision AVR создадут этот файл автоматически. А вот в IAR`e для этого нужно прописывать линкеру команды. Делается это так.
Меню Project > Options...>Linker вкладка Output. Устанавливаем значения по умолчанию - галочка Override default снята, формат - Debug information for C-SPY. На вкладке Extra Options ставим галочку Use Command Options и в окошке ниже прописываем такие строчки:
-Ointel-standard,(CODE)=.hex
-Ointel-standard,(XDATA)=.eep
После компиляции и сборки проекта IAR создаст файл прошивки - .hex и файл для EEPROM`a - .eep
Полученный eep файл записывается с помощью программатора в микроконтроллер.
Чтение и запись EEPROM
В IAR`e и CodeVision AVR использование EEPROM переменных по сути ничем не отличается от использования обычных переменных (хранящихся в ОЗУ и регистрах). Вся работа по организации чтения и записи в эти EEPROM переменные выполняется компилятором.
//для IAR AVR, CodeVision AVR
__eeprom uint8_t data;
...
//читаем из EEPROM
uint8_t tmp = data;
//записываем в EEPROM
data = 129;
В AtmelStudio для чтения/записи EEPROM переменных используются специальные макросы. Они определены в файле eeprom.h. Вот некоторые из них:
uint8_t eeprom_read_byte (const uint8_t *__p) - прочитать байт
uint16_t eeprom_read_word (const uint16_t *__p) - прочитать слово (2 байта)
uint32_t eeprom_read_dword (const uint32_t *__p) - прочитать двойное слово (4 байта)
void eeprom_write_byte (uint8_t *__p, uint8_t __value) - запись байта
void eeprom_write_word (uint16_t *__p, uint16_t __value) - запись слова (2 байта)
void eeprom_write_dword (uint32_t *__p, uint32_t __value) - запись дв. слова (4 байта)
Макросы принимают в качестве параметра адрес переменной размещенной в EEPROM. Для взятия адреса переменной используется оператор &. Примеры ниже поясняют использование этих макросов.
//для AtmelStudio
#include <avr/eeprom.h>
//объявляем переменные
uint8_t data EEMEM;
uint16_t pin_code EEMEM;
...
//читаем байт из eeprom
uint8_t tmp = eeprom_read_byte(&data);
//записываем слово в eeprom
eeprom_write_word (&pin_code, 5327)
Заключение
Из этой статьи вы узнали:
- как объявить переменную в EEPROM памяти,
- как создать файл для инициализации EEPROM,
- как прочитать и записать данные в EEPROM.
В следующей статье будут разобраны регистры, используемые для чтения и записи в EEPROM, а также низкоуровневая работа с этим типом памяти.