Учебный курс 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);


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

Comments   

# nomorewar 2013-07-12 15:57
спасибо, статья полезная
Reply | Reply with quote | Quote
# Pashgan 2013-07-13 07:29
Пожалуйста. Надеюсь кому-нибудь пригодится.
Reply | Reply with quote | Quote
# Peter 2013-07-13 20:29
Забавно. Прерывания запрещаются при перед операцией |= (об опасности этой операции тоже могу рассказать, если кому-то интересно), а регистры EEAR да и EEDR остаются совершенно незащищенными от использования в прерываниях. А вдруг в обработчике понадобится прочитать ДРУГУЮ ячейку EEPROM? :) По этой причине IARовский встроенный тип __eeprom совершенно недопустим для использования взрослыми программистами.
Reply | Reply with quote | Quote
# Pashgan 2013-07-13 20:56
Замечание верное. Рассказывай.
Reply | Reply with quote | Quote
# Peter 2013-07-13 21:26
Теперь насчет |=. Оптимизатор выискивает одинаковые участки кода и по своему разумению выделяет это в отдельные асм-функции. Есть реальная опасность, когда в проекте есть запись и char и int или long - переменных в EPROM (но, строго говоря, касается всех "4-хтактовых" случаев периферии AVR, например, переуставки WDT), что оптимизатор сделает основную часть действий в подпрограмме, и к финишной операции EECR |= (1 < < EEWE); перейдёт с помощью RJMP(это еще хорошо, пара тактов всего), а то и через RET из подпрограммы(во т это плохо - RET минимум 4 такта). Запись не произойдет. Лечение простое: перед функциями записи (а повторяю, квалификатор __eeprom лучше не пользовать, надо делать свои функции) подавлять оптимизацию, ставя (для ИАР) #pragma optimize = none.
Reply | Reply with quote | Quote
# _Артём_ 2013-07-20 12:47
Quoting Peter:
Теперь насчет |=. Оптимизатор выискивает одинаковые участки кода и по своему разумению выделяет это в отдельные асм-функции. Есть реальная опасность, когда в проекте есть запись и char и int или long - переменных в EPROM (но, строго говоря, касается всех "4-хтактовых" случаев периферии AVR, например, переуставки WDT), что оптимизатор сделает основную часть действий в подпрограмме, и к финишной операции EECR |= (1 < < EEWE); перейдёт с помощью RJMP(это еще хорошо, пара тактов всего), а то и через RET из подпрограммы(во т это плохо - RET минимум 4 такта). Запись не произойдет.

Есть такая проблема, особенно для WDT актуальна.

Quoting Peter:

Лечение простое: перед функциями записи (а повторяю, квалификатор __eeprom лучше не пользовать, надо делать свои функции) подавлять оптимизацию, ставя (для ИАР) #pragma optimize = none.

Можно по-другому - запрещать для файла или функции Cross call optimizaion (ключ --no_cross_call ). Тогда компилятор не будет искать одинаковые куски кода.
Reply | Reply with quote | Quote
# _Артём_ 2013-07-20 12:42
Quoting Peter:
а регистры EEAR да и EEDR остаются совершенно незащищенными от использования в прерываниях. А вдруг в обработчике понадобится прочитать ДРУГУЮ ячейку EEPROM? :)

Не нужно читать или писать в EEPROM из основной программы и прерываний.
Если программа написано с использованием RTOS, то для доступа к EEPROM надо использовать мьютексы.

Quoting Peter:

По этой причине IARовский встроенный тип __eeprom совершенно недопустим для использования взрослыми программистами.

Тип вполне допустим, хотя бы для объявления переменных такого вида.
Reply | Reply with quote | Quote
# Peter 2013-07-20 19:46
_Артём_.
1. Кто сказал что нельзя читать? Почему нельзя ЧИТАТЬ-ТО? Это не писать же! Это быстро же! Не согласен.
2. Про РТОС откуда появилось? Не будем размазываться.
3. (и самое неприятное): Если переменная объявлена с квалификатором __eeprom - она немедленно затеряется среди других обычных переменных проекта, потому как по виду ничем не отличается от них. Поэтому лучший вариант - это создание и применение специальной функции.
Безопасно. Наглядно. Сердито. Уж нечаянно не применишь.
Макросы? Ну, возможно, макросы кто их любит.
Reply | Reply with quote | Quote
# _Артём_ 2013-07-21 15:13
Quoting Peter:

1. Кто сказал что нельзя читать? Почему нельзя ЧИТАТЬ-ТО? Это не писать же! Это быстро же!

Я не говорил, что нельзя читать или писать. Это просто нежелательно, так как может вызвать проблемы-сбои в работе программы.

Quoting Peter:

Это быстро же!

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


Quoting Peter:

2. Про РТОС откуда появилось? Не будем размазываться.

Ситуация с RTOS аналогична предыдущему случаю, только потоков где выполняется работа с EEPROM может быть больше.

Quoting Peter:
Если переменная объявлена с квалификатором __eeprom - она немедленно затеряется среди других обычных переменных проекта, потому как по виду ничем не отличается от них.

Почему затеряется?
Она отличается спецификатором __eeprom. Он заметен.

Quoting Peter:

Поэтому лучший вариант - это создание и применение специальной функции.

Я тоже использую самописные функции чтения-записи, но переменные объявляю со спецификатором __eeprom. Так удобней, имхо.
Reply | Reply with quote | Quote
# Peter 2013-07-13 21:38
:) Поправки: не асм-функции, конечно, а асм-подпрограмм ы. И еще - зря я "похвалил" RJMP. Это тоже плохо кончится. Потому что 2 такта RJMP + 3 такта для |= это уже 5 тактов.
Reply | Reply with quote | Quote
# Pashgan 2013-07-13 21:53
Сейчас посмотрел как компилятор оптимизирует код записи многобайтового числа. При максимальной оптимизации по размеру он действительно "запихивает" часть операций в подпрограммы, но строчки установки разрядов EEMWE и EEWE не разделяет.
Code:
\ ?Subroutine2:
\ 00000000 9AE2 SBI 0x1C, 0x02
\ 00000002 9AE1 SBI 0x1C, 0x01
Reply | Reply with quote | Quote
# Peter 2013-07-13 22:39
Никто не знает, чтО на уме у оптимизатора. Возможно, здесь и есть доля паранойи, но вот для другой приферии - уставки WDT mega640/1280... , у которых регистр управления лежит выше битовых операций этот случай более реален. Там SBI не сработает. А конкретно для нашего случая EEPROM - зачем отдавать судьбу на волю оптимизатора, когда речь идёт о тактах? Легче сделать единообразно: просто убрать оптимизацию и получите то, что имели в виду.
Reply | Reply with quote | Quote
# Peter 2013-07-14 06:54
Оптимизатор может разделить эти операции, если программист в каком-то месте программы устанавливает особый, отличный от других функций режим записи в EEPROM, да еще разрешает прерывание: EECR |= ( (1 < < EEMWE) | ( 1 < < EEPM0) | ( 1 < < EERIE) ); - тут уж SBI не будет, а будет триада инструкций: IN-ORI-OUT. после которой элементарно может быть RJMP на общий для всех функций эпилог со стартом записи: SBI EECR, EEWE.
Поэтому однозначно: оптимизацию давить.
Reply | Reply with quote | Quote
# Pashgan 2013-07-14 07:26
Я думаю, что оптимизатор может разделить эти операции, если использовать свои функции доступа к eeprom написанные на Си, но в оооочень редких случаях. Если использовать встроенный тип __eeprom и внутренние функции доступа, этого вообще не случиться. Исходники кодов для работы с eeprom описаны на ассемблере - файл eeprom.s90. И, кстати, там учтены запрещения прерываний.
Code:
CLI
SBI EECR,EEMWE
SBI EECR,EEWE
OUT SREG,T0
Reply | Reply with quote | Quote
# Peter 2013-07-14 07:47
1. Вот ниже код, проверено в Иар. С оптимизацией по SPEED разделяет для второй функции старт записи. Оно Вам надо?
2. __eeprom без ручного запрета прерываний использовать нельзя, потому как НЕ ЗАЩИЩАЕТ EEAR и EEDR. Что толку защищать 4 такта, когда в обработчике прерывании адрес EEPROM может сбиться и запись произойдет не туда куда надо. Лучше писать свои.
//////
void EEPROM_WriteByt e(unsigned int adr, unsigned char value)
{
while (EECR & (1 < < EEWE));
EEAR = adr;
EEDR =value;
EECR |= (1 < < EEMWE);
EECR |= (1 < < EEWE);
}
void EEPROM_WriteByt eIRQ(unsigned int adr, unsigned char value)
{
while (EECR & (1 < < EEWE));
EEAR = adr;
EEDR =value;
EECR |= ( (1 < < EEMWE) | (1 < < EERIE));
EECR |= (1 < < EEWE);
}
//////// Вот во что превращается запись для EEPROM_WriteByteIRQ:
IN R16, 0x1C
ORI R16, 0x0C
OUT 0x1C, R16
RJMP ?Subroutine0 ; !!!!!!!!
Reply | Reply with quote | Quote
# Pashgan 2013-07-14 07:51
1. Я же говорю - если писать на Си и в очень редких случаях.
2. Чтобы защитить eear и eedr нужно их в прерывании сохранять, а перед выходом восстанавливать .
Reply | Reply with quote | Quote
# Peter 2013-07-14 08:13
Дело конечно личное, как и что использовать. Можно и регистр TIFR сбрасывать операцией |=, если не знать последствий... Но:
1. Случаи с авариями самолётов и ракет из-за сбоев ПО - тоже редкие события. Редкие но меткие)) Хотите нечто подобного в своих разработках? Это дело времени - закон подлости работает всегда))
2. Квалификатор __eeprom мало того что все равно требует геморроя с сохранением / восстановлением двух IO-регистров, так еще и не совместим со студией. Не лучше ли иметь свою надёжную версию?
Reply | Reply with quote | Quote
# King-live 2013-07-21 06:03
Добрый день! Я уже много лет пользуюсь таким шаблоном при работе с памятью (представлен ниже)в AVR Studio. Единственно были проблемы с устройставми (иногда!!!!), это при отключении питания, или при проблемах в сети. Я так думаю, что когда дело доходило до записи данных в EEPROM, на пол пути исчезало питание достаточное для работы контроллера. И после устройства оставались с частично утерянными данными EEPROM. Долго не думая в FUSE установил галочку в BODLEVEL(порог работы контроллера по напряжению). И на этом проблема решилась. Вот спрашиваю у знатоков, может в таком решении тоже есть подводные камни? Спасибо.


#...
#include


#define EEPROM_SECTION __attribute__ ((section (".eeprom")))

EEMEM uint8_t Mem1=0;
EEMEM uint8_t Mem2=0;
EEMEM uint8_t Mem3=0;
EEMEM uint8_t Mem4=0;



unsigned char x1;
unsigned char x2;
unsigned char x3;
unsigned char x4;



void Memory()
{
eeprom_update_byte(&Mem1,x1);
eeprom_update_byte(&Mem2,x2);
eeprom_update_byte(&Mem3,x3);
eeprom_update_byte(&Mem4,x4);
}


int main()
{
x1=eeprom_read_byte(&Mem1);
y1=eeprom_read_byte(&Mem2);
x2=eeprom_read_byte(&Mem3);
x4=eeprom_read_byte(&Mem4);
.....;


while(1)
{
.....;
}
}
Reply | Reply with quote | Quote
# Peter 2013-07-21 07:18
А тут рекомендаций только две, и они стары как мир:
1. Борьба с известной детской болезнью АВР, когда при изменении напряжения питания, контроллер EEPROM может (не обязатеьно даже при программном доступе к нему, а и просто ни с того ни с сего) произвести несанкционирова нную запись. Записывает, естественно, в ячейку, на которую указывает EEAR. Лечение(скорее не лечение, а паллиатив): самом конце функции (макроса) доступа к EEPROM писать строку EEAR=0 и не использовать эту ячейку в своей работе. Пусть себе портится нулевая ячейка. И пусть себе отчаянные головы используют квалификатор переменных __eeprom, который этого действия, конечно, не делает.
2. Установка BODEN/BODLEVEL, как у Вас. Тут какие грабли? Только потребление немного возрастёт... Ну, и конечно устанавливать уровень срабатывания BODLEVEL таким, чтобы соответствовал тактовой частоте процессора. То есть не ставить BODLEVEL на уровень срабатывания 1.8 Вольт, если у Вас кварц 16МГц. :)
Reply | Reply with quote | Quote
# Peter 2013-07-21 07:26
3. Ну и, самое главное: мажорировать(ил и применять какие-то еще меры, позволяющее увидеть и исправить) данные в EEPROM, чтобы одиночная ошибка всегда могла быть исправлена.
Reply | Reply with quote | Quote
# _Артём_ 2013-07-21 15:48
Quoting Peter:

1. Борьба с известной детской болезнью АВР, когда при изменении напряжения питания, контроллер EEPROM может (не обязатеьно даже при программном доступе к нему, а и просто ни с того ни с сего) произвести несанкционированную запись. Записывает, естественно, в ячейку, на которую указывает EEAR. Лечение(скорее не лечение, а паллиатив): самом конце функции (макроса) доступа к EEPROM писать строку EEAR=0 и не использовать эту ячейку в своей работе.

Ни разу не видел, чтобы запись сама стартовала. Правда я всегда BOD разрешаю.

Quoting Peter:

И пусть себе отчаянные головы используют квалификатор переменных __eeprom, который этого действия, конечно, не делает.

__eeprom делает только одно действие - указывает компилятору в какой области памяти размещена переменная. Большего от него не требуется.

Quoting Peter:

2. Установка BODEN/BODLEVEL, как у Вас. Тут какие грабли? Только потребление немного возрастёт... Ну, и конечно устанавливать уровень срабатывания BODLEVEL таким, чтобы соответствовал тактовой частоте процессора. То есть не ставить BODLEVEL на уровень срабатывания 1.8 Вольт, если у Вас кварц 16МГц. :)

Тут согласен.
Reply | Reply with quote | Quote
# Peter 2013-07-21 18:34
Кто запускает порчу EEPROM при изменении питания - сходящее с ума ЦПУ или EEPROM сам находит это нужным - сие предмет обсуждений.
Однако, если Вы не слышали про эту особенность - почитайте http://www.avrfreaks.net/index.php?name=PNphpBB2&file=printview&t=75638 или просто погуглите на тему: AVR EEPROM портится EEAR и т.п.
И еще: не понял смысла объявления __eeprom, если к ней обращаетесь самодельными функциями. Для меня - нет и не должно быть переменных __eeprom, потому как они не являются переменными, которые можно просто a=eeval; eeval=b; b=&eeval; Это слишком легкомысленно на мой взгляд. Для меня "переменная EEPROM" - это группа мажорированных ячеек EEPROM (с определенными адресами, размещаемые неблизко друг от друга), доступ к которым осуществляется специальными функциями.
Reply | Reply with quote | Quote
# _Артём_ 2013-07-21 20:54
Quoting Peter:
Кто запускает порчу EEPROM при изменении питания - сходящее с ума ЦПУ или EEPROM сам находит это нужным - сие предмет обсуждений.

Тут не обсуждать надо, а обеспечить чтобы процессор работал в тех условиях, где его работоспособность гарантируется.

Quoting Peter:

Однако, если Вы не слышали про эту особенность - почитайте http://www.avrfreaks.net/index.php?name=PNphpBB2&file=printview&t=75638 или просто погуглите на тему: AVR EEPROM портится EEAR и т.п.

Не попадались такие случаи, программа с ошибками может портить - было у меня пару раз такое, но чтобы само - как-то не верится.


Quoting Peter:

И еще: не понял смысла объявления __eeprom, если к ней обращаетесь самодельными функциями.

Смысл простой - каждая eeprom-переменная будет расположена по уникальному адресу, не надо самому об этом думать. Компилятор обеспечит.

Quoting Peter:

Для меня - нет и не должно быть переменных __eeprom

Зачем же так строго?

Quoting Peter:

Для меня "переменная EEPROM" - это группа мажорированных ячеек EEPROM (с определенными адресами, размещаемые неблизко друг от друга), доступ к которым осуществляется специальными функциями.

В мажорировании иногда есть смысл, но применять его для каждого байта на мой взгляд как-то слишком. Но это уже от задачи зависит и от характера самих данных.
Если в ячейке хранится какая-нибудь настройка, которую изменяют раз в несколько лет, то как показывает практика, ничего с ней не случится. Тут мажорирование не нужно.
Но это зависит от условий использования аппаратуры - может у вас приборы работают в условиях радиации или сильных эл.-магнитных помех?
Reply | Reply with quote | Quote
# Peter 2013-07-22 04:58
Строго - потому как это значения, которым надо верить. Потому что EEPROM ненадёжна по сути и имеет ограниченный ресурс. Кроме мажорирования еще и контрольной суммой умные люди пользуются. Видимо, Вы родились под счастливой звездой, раз у Вас не случалось сбоев. Просто мои изделия стОят дорого, стоЯт в труднодоступных местах и цена ошибки велика.
Reply | Reply with quote | Quote
# king-live 2013-08-16 18:06
Quoting Peter:
Кроме мажорирования еще и контрольной суммой умные люди пользуются.

К сожалению в кристалл Attiny13 в 1Кб Flash такие функции не записать, просто места нет. В него и так вместил программу, которую фактически все делают на 2Kб Flash. А так как ГОСТ стандарт подтвердил работаспособнос ть приборов сигнализации на маленьком кристалле. То пришлось лечить эту уязвимость EEPROM, только выставлением нужных Fuse. Приборы сигнализации, с таким лечением работают без сбоев уже 2 года. Но одну вещь я так и не осмыслил, за счёт чего портились данные EEPROM, (с точки зрения электрических процессов в самом кристалле). Ведь и в первом случаее до выставления BOODLEVEL и во втором случае после выставления BOODLEVEL, процессор отключается при падении напряжения до определённого критического уровня. Почему стабильность работы EEPROM стала зависить от этого BOODLEVEL, когда поднял я режим работоспособнос ти кристалла до 4,7V?
Reply | Reply with quote | Quote
# ArtemKAD 2013-09-14 17:12
Если уж делать универсальные процедуры чтения/записи EEPROM лучше не пожалеть двух локальных переменных в которые сохранить EEAR и EEDR на входе и потом их восстановить перед выходом из процедуры.

Тогда не будет Вас поджидать северный пушной зверек в самом интересном месте...
Reply | Reply with quote | Quote
# _Артём_ 2013-09-15 11:10
Quoting ArtemKAD:
Если уж делать универсальные процедуры чтения/записи EEPROM лучше не пожалеть двух локальных переменных в которые сохранить EEAR и EEDR на входе и потом их восстановить перед выходом из процедуры.

Тогда не будет Вас поджидать северный пушной зверек в самом интересном месте...

Зачем? Что даст сохранение EEAR и EEDR? От какого сбоя спасёт?
Reply | Reply with quote | Quote
# ArtemKAD 2013-09-15 12:23
От изменения текущих EEAR и EEDR не вовремя вылезшим прерыванием. Можно конечно запрещать/разре шать прерывания, но это через задницу т.к. функции должны работать как в прерывании так и в основном цикле.
По сути EEAR и EEDR это такой-же глобальный ресурс как CREG и поступать при его изменении желательно так-же
Reply | Reply with quote | Quote
# _Артём_ 2013-09-15 13:11
Quoting ArtemKAD:
От изменения текущих EEAR и EEDR не вовремя вылезшим прерыванием.

Обычно в прерываниях и одновременно в основной программе eeprom не читают. У вас не так? Как тогда организуете этот процесс?
Quoting ArtemKAD:

функции должны работать как в прерывании так и в основном цикле.

Допустим основная программа запустила запись ячейки eeprom и сразу после этого возникло прерывание, которое должно прочитать другую ячейку eeprom. Как это работать будет? Чем поможет сохранение EEAR и EEDR?
Reply | Reply with quote | Quote
# ArtemKAD 2013-09-15 19:13
Quoting _Артём_:
Обычно в прерываниях и одновременно в основной программе eeprom не читают.

Это возможно только если Вы вообще в прерывании EEPROM не читаете. Иначе "вместе" возможно.
Quoting _Артём_:
Допустим основная программа запустила запись ячейки eeprom и сразу после этого возникло прерывание, которое должно прочитать другую ячейку eeprom. Как это работать будет? Чем поможет сохранение EEAR и EEDR?

Если уже запустила - т.е. выполнила команду записи в регистр EECR программа проболтается в прерывании до завершения записи eeprom на цикле
while (EECR & (1
Reply | Reply with quote | Quote
# _Артём_ 2013-09-15 21:27
Quote:

программа проболтается в прерывании до завершения записи eeprom на цикле
Висеть в прерывании несколько мс? Может лучше переменную в ОЗУ заранее прочитать?
Или вы ещё и пишете в eeeprom в прерывании?
Reply | Reply with quote | Quote
# ArtemKAD 2013-09-16 16:57
Quoting _Артём_:
Висеть в прерывании несколько мс? Может лучше переменную в ОЗУ заранее прочитать?

Событие редкое, почему и не повисеть? Если читать в ОЗУ, то надо постоянно синхронизировать переменные. А смысл в такой работе?
Quoting _Артём_:
Или вы ещё и пишете в eeeprom в прерывании?

Бывало и такое. Если вся обработка радиоканала происходит в прерываниях, не вижу причин почему там-же не сохранять новые данные по ключам и результатам.
Reply | Reply with quote | Quote
# _Артём_ 2013-09-16 17:15
Quoting ArtemKAD:

Событие редкое, почему и не повисеть?

Что же это за прерывание на несколько миллисекунд? Как-то неправильно это.
Quoting ArtemKAD:

Если читать в ОЗУ, то надо постоянно синхронизировать переменные. А смысл в такой работе?

Смысл - синхронизировать значения. Но это можно делать нечасто.


Quoting ArtemKAD:

Бывало и такое. Если вся обработка радиоканала происходит в прерываниях, не вижу причин почему там-же не сохранять новые данные по ключам и результатам.

Как-то это идеологически неправильно. имхо.
Reply | Reply with quote | Quote
# ArtemKAD 2013-09-16 21:09
Quoting _Артём_:
Quoting ArtemKAD:

Бывало и такое. Если вся обработка радиоканала происходит в прерываниях, не вижу причин почему там-же не сохранять новые данные по ключам и результатам.

Как-то это идеологически неправильно. имхо.

Там где использовал - было правильно. Правда надо добавить, что в том прерывании прежде чем заниматься столь длительными процедурами(и криптография и сохранение) остальные прерывания таки разрешались.
PS. Сейчас конечно сделал бы иначе, но что было то было...
Reply | Reply with quote | Quote
# ArtemKAD 2013-09-15 19:17
Упс...
Code: while (EECR & (1<<EEWE));
А если попало в прерывание на границе записи EEAR или EEDR, сперва прочитает ячейку, а потом корректно продолжит процедуру записи. Если бы второй вариант был без сохранения этих регистров, могло после возврата из прерывания записать не то и не туда
Reply | Reply with quote | Quote
# _Артём_ 2013-09-15 21:30
Quote:

А если попало в прерывание на границе записи EEAR или EEDR, сперва прочитает ячейку, а потом корректно продолжит процедуру записи.
Тогда уж лучше в основной программе читать/писать в eeprom в критической секции(не до окончания записи, а на время запуска записи - это недолго, несколько десятков циклов).
Reply | Reply with quote | Quote
# ArtemKAD 2013-09-16 17:00
А когда перейдёте на камни с приоритетной системой прерываний чё делать?
Reply | Reply with quote | Quote
# _Артём_ 2013-09-16 17:18
Quoting ArtemKAD:
А когда перейдёте на камни с приоритетной системой прерываний чё делать?

Уже перешёл на xmega. Чем там отличаются приёмы работы с eeprom? Что такого приритетный контроллер прерываний вносит нового? Всё также делаю - чтение/запись в eeprom только в основной программе.
Если по вашему делать (ждать конца записи в прерывании), то получается блокировка уровня (или нескольких) на время записи. Не годится такое.
Reply | Reply with quote | Quote
# ArtemKAD 2013-09-16 21:30
Quoting _Артём_:
Если по вашему делать (ждать конца записи в прерывании), то получается блокировка уровня (или нескольких) на время записи. Не годится такое.

Самое любопытное, что в связи с некоторым глюком первых ревизий xMega в Atmel-овской процедуре записи EEPROM примерно так и делают. Готовят данные для записи, запрещают все прерывания кроме наивысшего уровня и усыпляют камень до завершения записи (собственного прерывания EEPROM).
Reply | Reply with quote | Quote
# ArtemKAD 2013-09-16 21:56
Уточняю - ситуация описана в "AVR1008: Writing EEPROM and Flash in XMEGA A3/D3/256A3B Revision B"
Reply | Reply with quote | Quote
# _Артём_ 2013-09-16 23:09
Quoting ArtemKAD:
Уточняю - ситуация описана в "AVR1008: Writing EEPROM and Flash in XMEGA A3/D3/256A3B Revision B"

ФКурсе, но то дела минувших дней...и тд. Сейчас не актуально.
Но как несколько уровней приоритета прерываний влияют на работу с eeprom вы так и не уточнили.
Reply | Reply with quote | Quote
# _Артём_ 2013-09-16 22:03
Quoting ArtemKAD:

Самое любопытное, что в связи с некоторым глюком первых ревизий xMega в Atmel-овской процедуре записи EEPROM примерно так и делают. Готовят данные для записи, запрещают все прерывания кроме наивысшего уровня и усыпляют камень до завершения записи (собственного прерывания EEPROM).

По разному: кто делает, а кто и нет.
Было пару сотен xmeg A3 rev.B. Не делали такой процедуры - такая eeprom неработоспособн а. Поставили AT25 на 1 КБ. На 10-20 центов дороже, но зато правильней и быстрей работает ещё к тому же.
Reply | Reply with quote | Quote
# ArtemKAD 2013-09-17 14:25
Quoting _Артём_:
Но как несколько уровней приоритета прерываний влияют на работу с eeprom вы так и не уточнили.

Да так-же если на нескольких уровнях читать содержимое EEPROM надо или сохранять/восст анавливать регистры или делать "критическую секцию" на каждом уровне или вообще не использовать EEPROM в прерываниях...
Reply | Reply with quote | Quote
# Виктор 2014-03-21 19:52
Ребята! ввиду стольких нюансов с обслуживанием EEPROM в основном цикле, горячие дебаты по поводу как записывать, как читать, сохранять-не сохранять, разрешать-не разрешать, выдвигаю на обсуждение такой вариант:
1.Все переменные потенциально нуждающиеся в сохранении в EEPROM, во время основного цикла хранятся в SRAM.
2.Используется внешний монитор питания который в случае снижения питающего напряжения подает сигнал на пин INT0 обладающий высшим приоритетом прерывания после RESET. В обработчик помещаются все необходимыемеро приятия для сохранения вышеупомянутых переменных хоть по две, хоть по три копии из SRAM в EEPROM. И будет всем счастье, причем не одно. Во первых не будут расходоваться лишний раз цыклы перезаписи. Во вторых во время работы основного цикла перепысывай хоть по сто раз свои данные без всяких флагов и т.п. переживаний. И в третьих, если питание пропало или падает мне уже без разницы сколько я буду занят в этом самом прерывании, устройство не работает.
Теперь как это сделать. Ставится кондер большой емкости. После команды внешнего монитора питания вся схема отключается, проц питается от кондера (какое то время, необходимо расчитать емкость) выполняет приоритетную процедуру по внешнему прерыванию и сохраняет все необходимые данные. Затем как по учебнику нужно чтобы монитор инициировал "твердый" сброс и ПАВЕР ОФФ.
вуаля.
Reply | Reply with quote | Quote
# Виктор 2014-03-24 17:43
Я открыл америку
Оказывается такая тема используется вдоль и поперек. Кому интересно почитайте по супервизорам.
Reply | Reply with quote | Quote

Add comment