Учебный курс AVR. Проблемы при работе с EEPROM. Повышение надежности EEPROM. Ч3

Введение

   В этом материале мы рассмотрим возможные проблемы при работе с EEPROM. Часть из них связана с аппаратным стороной микроконтроллера, такие как повреждение EEPROM при пониженном напряжении питания и ограниченный ресурс EEPROM, а часть с программным обеспечением, например, при использовании EEPROM в прерываниях. Также рассмотрим способы решения этих проблем и методы повышения надежности хранения данных в EEPROM.

Повреждение EEPROM при пониженном напряжении питания

   Одна из старых проблем AVR - это повреждение EEPROM данных при пониженном питании микроконтроллера. Это может происходить в двух случаях:

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

   Этих повреждений EEPROM данных можно избежать, соблюдая следующие рекомендации:

- Нужно удерживать микроконтроллер AVR в состоянии сброса, если напряжение питания находится ниже нормы. Для этого можно использовать внешние супервизоры питания или встроенный детектор пониженного питания - Brown-out Detector (BOD). Встроенный детектор управляется с помощью fuse битов микроконтроллера - BODEN и BODLEVEL. BODEN - разрешает/запрещает работу детектора, а BODLEVEL - определяет его уровень срабатывания.
   Если сброс микроконтроллера происходит во время процесса записи в EEPROM, то операция записи будет завершена только при достаточном уровне напряжения.

- Также в многие разработчике рекомендуют не использовать 0-ую ячейку EEPROM`a, поскольку именно ее содержимое чаще всего повреждается при снижении питания микроконтроллера.

Проблемы с EEPROM из-за прерываний

   Операция записи в EEPROM состоит из нескольких шагов. Вспомним эту последовательность:


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

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

   Однако есть еще один подводный камень. Если прерывание возникло после 1-го, 2-го или 3-го шага, и в прерывании тоже используются операции с EEPROM (чтение или запись), то запись может не состояться, или запишутся не те данные и не туда, потому что содержимое регистров EEAR (адрес) и EEDR (данные) будет изменено.

   Описанное выше касается и процедуры чтения EEPROM.

   Лечить это можно следующими способами:

   - Не использовать операции чтения и записи EEPROM в прерываниях.
   Это особенно касается операции записи, потому что она медленная и выполняется с использованием внутреннего RC генератора. Например, для mega16 в даташите указано, что при записи в EEPROM используется внутренний RC генератор с частотой 1 МГц (независимо от установок fuse битов CKSEL) и время записи составляет 8.5 мс. Для прерывания это очень долго.

   - Запрещать прерывания на время всей процедуры записи (чтения) EEPROM, то есть в самом начале.

   - Сохранять в начале обработчика прерывания содержимое регистров EEAR (адрес) и EEDR (данные), а перед выходом восстанавливать их.

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

Ресурс EEPROM

   EEPROM имеет ограниченный ресурс. Atmel гарантирует, что количество циклов перезаписи EEPROM составляет не меньше 100000. Цифра довольно большая, однако и она может быть достигнута, если записывать в EEPROM часто и на протяжении долгого времени.
   Есть два приема по "увеличению" ресурса EEPROM.
   Первый - простой и состоит в том, чтобы записывать в EEPROM данные, только если они изменили свое значение.

 __eeprom uint8_t data;
 uint8_t newData;

 ... 
 if (newData != data) {
 data = newData;
 }


   Второй- хитрый и состоит в том, чтобы хранить данные не в одной ячейки памяти (или группе ячеек, если речь идет о многобайтных переменных), а в нескольких, и записывать в них по очереди.
   Допустим, нам нужно хранить в EEPROM один байт. Выделяем под него 8 байтов и каждый раз записываем в следующую ячейку, когда доходим до последней ячейки, то записываем в первую. И так по кругу, как в кольцевом буфере. Если каждая ячейка EEPROM имеет ресурс 100000, то записывая в 8 ячеек по очереди, мы получаем ресурс перезаписи байта 800000.

EEPROM и оптимизация компилятора

   Переменные, которые объявлены, но не используются, часто удаляются компилятором в процессе оптимизации. Если такие переменные нужны, перед ними следует добавлять ключевое слово volatile.

//для IAR AVR
volatile __eeprom char name[] = "prog 12.3";


   Если используются свои функции для работы с EEPROM, то могут возникнуть проблемы при высоких уровнях оптимизации компилятора. Компилятор может объединить одинаковые (с его точки зрения) части кода в одну подпрограмму и нарушить логику работы вашей функции. Чтобы этого не происходило нужно или запрещать оптимизацию данной функции, или отключать перекрестную оптимизацию (cross call optimization) для функции или файла. Как это делается зависит от компилятора. Как правило, для этого существуют определенные ключи и прагмы.

Программные способы повышения надежности EEPROM

   Один из простых способов повышения надежности хранения данных в EEPROM - это метод мажоритарного резервирования. Суть метода заключается в том, что для хранения данных выделяется нечетное количество ячеек памяти - N. При сохранении данных - запись производится во все выделенные ячейки. При чтении - читаются тоже все, но решение относительно содержимого принимается на основе равенства (N+1)/2 ячеек.

   Рассмотрим пример тройного мажоритарного резервирования байта данных. Для сохранения байта используются три байта EEPROM, а решение о содержимом принимается на основании равенства 2 байтов. Код приведен для компилятора IAR AVR.

//функция сохранения
void EEPROM_SaveByte(uint8_t value, uint8_t __eeprom *buf)
{
   buf[0] = value;
   buf[1] = value;
   buf[2] = value;
}
 
//функция считывания
uint8_t EEPROM_LoadByte(uint8_t *value, uint8_t __eeprom *buf)
{
   uint8_t a = buf[0];
   uint8_t b = buf[1];
   uint8_t c = buf[2];

    if ((a == b)||(a == c)){
       *value = a;
      return 0;
   }
   else {
      if (b == c){  
         *value = b;
         return 0;
      }
   } 
   return 1;
}

....
//пример использования

__eeprom uint8_t buffer[3];
uint8_t data;

EEPROM_SaveByte(125, buffer);
EEPROM_LoadByte(&data, buffer);


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

   Другой метод повышения надежности хранимых данных состоит в использовании контрольных сумм, например CRC. Я не использовал этот метод в своей практике, поэтому о нем мало чего могу сказать.

   На этом все...

У вас недостаточно прав для комментирования.