Print this page

Учебный курс AVR. Работа с EEPROM. Низкоуровневое чтение и запись данных. Ч2

20/07/2013 - 00:00 Павел Бобков

Введение

   Если вы читали предыдущий материал, то знаете как объявлять, читать и записывать данные в EEPROM. Но давайте разберемся, как же на самом деле происходят эти операции и что от нас скрывает компилятор. Это позволит лучше понимать работу микроконтроллера, и при желании написать свои специфические функции для работы с EEPROM.

Регистры для работы с EEPROM

   Для работы с EEPROM используются три регистра ввода/вывода: регистр адреса, регистр данных и регистр управления. 


   Регистр адреса EEAR (EEPROM Address Register) предназначен для адресации однобайтной ячейки EEPROM памяти, к которой будет производиться обращение.
   Для полной адресации хотя бы минимального объема EEPROM памяти (512-ти байт) требуется 9 разрядов (2 в 9-ой степени = 512), поэтому регистр EEAR является 16-ти разрядным и физически расположен в двух регистрах ввода/вывода – EEARH и EEARL. Регистр EEARL полностью доступен для записи/чтения. А в регистре EEARH для записи/чтения доступны только младшие разряды, используемые для адресации. Остальные разряды доступны только для чтения и содержат «0».

 

   
   Для чтения/записи используется один и тот же регистр данных – EEDR (EEPROM Data Register). Если выполняется процедура записи, мы должны поместить в EEDR байт данных, если выполняется процедура чтения, прочитать байт данных из EEDR. 

 

   
   Регистр управления EECR (EEPROM Control Register)
предназначен для управления доступом к EEPROM.

 

   
   Бит EERIE (EEPROM Ready Interrupt Enable) – разрешение/запрещение прерывания по событию готовности EEPROM. Если бит EERIE установлен в 1, установлен флаг глобального разрешения прерываний (бит I регистра SREG) и бит EEWE очищен, то микроконтроллер будет генерировать прерывание “EEPROM Ready Interrupt”.

   Бит EEMWE (EEPROM Master Wirte Enable) - главное разрешение записи. Если в течении 4-ех тактов после установки этого бита, устанавливается бит EEWE, то микроконтроллер выполняет запись в EEPROM. Бит EEMWE аппаратно сбрасывается в ноль после 4-ех периодов тактовой частоты.

   Бит EEWE (EEPROM Write Enable) - разрешение записи. Этот бит выполняет роль стартового сигнала записи в EEPROM. Когда установлен адрес, данные и бит EEMWE, установка бита EEWE инициирует запись в EEPROM. Бит EEWE должен быть установлен в течении 4-ех тактов после установки EEMWE. Если это произойдет позже, то запись в EEPROM не будет произведена. Чтобы избежать возможных проблем, рекомендуется запрещать прерывания на время выполнения записи в EEPROM.
   Бит EEWE аппаратно сбрасывается, после завершения операции записи. Поэтому перед каждой операцией записи в EEPROM нужно проверять состояние этого разряда. 

   Бит EERE (EEPROM Read Enable) - разрешение чтения. Установка бита EERE инициирует процесс чтения из EEPROM. Перед каждым циклом чтения нужно проверять состояние разряда EEWE, если выполняется операция записи, то чтение из EEPROM не даст результата.

Низкоуровневая запись в EEPROM на Си

   Разберем вариант низкоуровневой работы с EEPROM, то есть без использования встроенных макросов/функций компиляторов. Допустим, мы объявили в EEPROM переменную, как нам изменить ее значение из своего приложения? 


   Процедура записи в EEPROM состоит из следующих шагов:

1. Ожидаем готовности EEPROM, опрашивая бит EEWE регистра EECR.
2. Устанавливаем адрес в регистре EEAR.
3. Записываем байт данных в регистр EEDR.
4. Устанавливаем основной флаг разрешения записи EEMWE регистра EECE
5. Устанавливаем флаг разрешения записи EEWE регистра EECE

   Тут есть два нюанса.
   Запись в EEPROM не может производиться одновременно с записью во флэш память. Если ваше приложение в какие то моменты пишет во флэш, то перед записью EEPROM`а, проверьте флаг SPMEN регистра SPMCR. Он должен быть сброшен в ноль. Более подробную информацию смотрите в даташите.

   Второй нюанс связан с флагом EEWE. Он должен быть установлен в течении 4 циклов после флага EEMWE. Если в проекте используются прерывания, то на каком-то из этапов этой последовательности их нужно запрещать. Это можно сделать или в самом начале или перед установкой флага EEMWE.

   В Си коде описанная последовательность будет выглядеть так:


while (EECR & (1<<EEWE));
EEAR = adr;
EEDR = value;
EECR |= (1<<EEMWE);
EECR |= (1<<EEWE);


где adr- это адрес байта в EEPROM, value - данные для записи, EEAR - 16-ти разрядный регистр адреса.

   Использовать абсолютный адрес байтов EEPROM не удобно и чаще всего адрес берется у переменной, объявленной в EEPROM. Код в этом случае будет таким:


while (EECR & (1<<EEWE));
EEAR = (uint16_t) &data;
EEDR = value;
EECR |= (1<<EEMWE);
EECR |= (1<<EEWE);


где data - переменная объявленная в EEPROM, а & - оператор взятия адреса.

   Этот код будет одинаковым для IAR AVR, CodeVison и Atmel Studio, а если оформить его в виде функции, то возникнут отличия.
   
Для IAR AVR и CodeVision AVR:

void EEPROM_WriteByte(uint8_t __eeprom*adr, uint8_t value)
{
while (EECR & (1<<EEWE));
EEAR = (uint16_t)adr;
EEDR =value;
EECR |= (1<<EEMWE);
EECR |= (1<<EEWE);
}


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


__eeprom uint8_t data;
...
EEPROM_WriteByte(&data, 125);


Для Atmel Studio:

void EEPROM_WritByte(uint8_t *adr, uint8_t value)
{
while (EECR & (1<<EEWE));
EEAR = (uint16_t)adr;
EEDR =value;
EECR |= (1<<EEMWE);
EECR |= (1<<EEWE);
}


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

uint8_t data EEMEM;
...
EEPROM_WriteByte(&data, 125);


   Вариант функции записи с запрещением прерываний будет выглядеть следующим образом.
Для IAR AVR и CodeVision AVR:

#define I_FLAG 7

void EEPROM_WriteByte(uint8_t __eeprom*adr, uint8_t value)
{
uint8_t save;
while (EECR & (1<<EEWE));
EEAR = (uint16_t)adr;
EEDR =value;
save = SREG;
SREG &= ~(1 << I_FLAG);
EECR |= (1<<EEMWE);
EECR |= (1<<EEWE);
SREG = save;
}

Низкоуровневое чтение из EEPROM на Си

Процедура чтения EEPROM состоит из следующих шагов:

1. Ожидаем готовность EEPROM, опрашивая бит EEWE регистра EECR.
2. Устанавливаем адрес в регистре EEAR.
4. Устанавливаем флаг разрешения чтения EERE регистра EECR
5. Считываем содержимое регистра данных EEDR

На Си описанная последовательность будет выглядеть так:


while (EECR & (1<<EEWE));
EEAR = adr;
EECR |= (1<<EERE);
value = EEDR;


Доступ к объявленной в EEPROM переменной:


while (EECR & (1<<EEWE));
EEAR = (uint16_t)&data;
EECR |= (1<<EERE);
value = EEDR;


Код оформленный в виде функции будет выглядеть следующим образом. 
Для IAR AVR и CodeVision AVR:


uint8_t EEPROM_ReadByte(uint8_t __eeprom*adr)
{
while (EECR & (1<<EEWE));
EEAR = (uint16_t)adr;
EECR |= (1<<EERE);
return EEDR;
}


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


__eeprom uint8_t data;
uint8_t tmp;
...
tmp = EEPROM_ReadByte(&data);


Для Atmel Studio:

uint8_t EEPROM_ReadByte(uint8_t *adr)
{
while (EECR & (1<<EEWE));
EEAR = (uint16_t)adr;
EECR |= (1<<EERE);
return EEDR;
}


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


uint8_t data EEMEM;
uint8_t tmp;
...
tmp = EEPROM_ReadByte(&data);


Продолжение следует...

Related items