AVR104: буферизированная запись в EEPROM с управлением по прерыванию Печать
Добавил(а) microsin   

В даташите AVR104 рассматриваются следующие вопросы:

• Гибкий блочный доступ к EEPROM через буфер
• Эффективная работа с EEPROM с точки зрения энергопотребления
• Контроль доступа к буферу
• Перезапись буфера EEPROM

Множество приложений используют EEPROM, встроенное в AVR, для сохранения в нем энергонезависимых данных: когда система обесточивается, эти данные могут быть восстановлены после нового включения питания. Время, необходимое для сохранения одного байта данных в EEPROM, встроенное в ядро AVR, составляет от 3 до 8.5 мс, так что доступ на запись ограничивается этим временем. Обычно реализации программного обеспечения, которые записывают память EEPROM, используют метод опроса (polling), чтобы определить - когда завершилась текущая операция записи в EEPROM. Этот апноут разъясняет более эффективный метод доступа к EEPROM на запись - используется буферизация данных с обработкой события готовности EEPROM по прерыванию. Этот метод значительно увеличивает быстродействие и снижает энергопотребление в сравнении с реализацией, работающей по простому опросу флага.

Улучшенное быстродействие и снижение потребляемой мощности напрямую связано с уменьшением затрат рабочего времени ядра - такое преимущество имеет реализация доступа к EEPROM по прерыванию в сравнении с традиционным доступом, где применяется опрос. Причина проста - когда реализовано ожидание готовности EEPROM к записи по опросу флага, все остальные выполняемые задачи программы (за исключением тех, что обрабатываются в прерываниях) останавливаются на время опроса. Реализация с прерываниями освобождает ядро микроконтроллера для выполнения любого кода, либо ядро может перейти в режим сна. В момент завершения операции записи сработает прерывание EEPROM и вызовет обработчик прерывания, который выполнит работу, связанную с завершением записи. Таким образом, драйвер EEPROM при записи освобождает до 8.5 мс полезного процессорного времени, которое в реализации по опросу тратилось бы впустую. Экономия времени зависит от времени программирования (типа используемого микроконтроллера) и тактовой частоты системы.

[Немного теории: как это работает]

Микроконтроллеры AVR компании Atmel могут записывать во встроенную память EEPROM либо с помощью прерываний по флагу занятости, либо с помощью опроса этого флага. Каждый из методов имеет свои достоинства и недостатки, но с точки зрения получения максимальной производительности и/или пропускной способности предпочтительнее выбрать метод доступа по прерываниям. Давайте подробнее рассмотрим работу каждого метода доступа к EEPROM.

Polling (метод опроса флага занятости EEPROM). Подпрограмма (как для чтения, так и для записи) опрашивает флаг состояния EEWE, чтобы удостовериться, что предыдущий цикл записи был завершен. Если подсистема записи все еще выполняет свою работу, то доступ к EEPROM запрещен, и микроконтроллер в цикле опроса бита EEWE ждет, когда сброс бита сигнализирует об освобождении EEPROM. Это также необходимо для проверки - активна или нет операция самопрограммирования (под самопрограммированием понимается процесс записи памяти программ FLASH под управлением программы микроконтроллера, подробнее см. [2]). Если это так, то подпрограмма должна ждать, пока не завершится операция SPM. Если самопрограммирование не используется, то этот шаг может быть пропущен. Как только подсистема записи освободилась, то может быть дан доступ на начало следующей операции с EEPROM. Достоинство polling-реализации в компактном и простом коде, хотя при этом есть проигрыш в лишней трате циклов микроконтроллера - ядро тратит свои циклы, ожидая когда можно будет начать новую операцию с EEPROM. Ниже показан типичный пример подпрограммы, записывающей 1 байт (этот метод описан в даташите AVR100 [3]).

EEPROM_WR: ;Подпрограмма записи в EEPROM
      sbic EECR, EEWE      ;Если бит EEWE не очищен,
      rjmp EEPROM_WR       ; то подождем еще...
      
SPM_BUSY: ;(этот шаг можно опустить, если самопрограммирование
          ; не используется)
      sbic SPMCR, SPMEN    ;Если бит SPMEN не очищен,
      rjmp SPM_BUSY        ; то подождем еще...
      
      out EEARH, r16       ;Вывод старшего байта адреса
      out EEARL, r17       ;Вывод младшего байта адреса
      out EEDR, r18        ;Вывод байта данных
      cli                  ;Запрет всех прерываний
      sbi EECR, EEMWE      ;Установка флага Master Write Enable
      sbi EECR, EEWE       ;Установка флага строба записи в EEPROM
;Эта инструкция занимает по времени 4 такта частоты ядра.
      sei                  ;Разрешить прерывания
      ret                  ;Возврат из подпрограммы

Interrupt (метод обработки события освобождения EEPROM по прерыванию). В этом случае не нужно постоянно опрашивать бит статуса EEWE, чтобы проверить завершение предыдущего цикла записи. Прерывание готовности (EEPROM Ready Interrupt) постоянно будет срабатывать, когда бит статуса EEWE очистится. Однако все еще нужно опрашивать бит статуса SPMEN, если используется самопрограммирование - чтобы гарантировать, что сейчас не происходит самопрограммирование. Главное достоинство interrupt-метода в том, что аппаратура EEPROM может запросить поддержку со стороны кода ядра (в обработчике прерывания) только тогда, когда это действительно необходимо; это уменьшает загрузку ядра микроконтроллера.

Управляемый по прерываниям доступ к EEPROM становится еще более эффективным, если используется буфер: в буфере хранятся значения данных, которые должны быть записаны в EEPROM, и обработчик прерывания может брать оттуда данные для записи.

Сложность кода обработчика прерывания (Interrupt Service Routine, ISR) связана с количеством записываемых байт в EEPROM. Подпрограмма ISR довольно проста для обнобайтного буфера, но когда нужно записать несколько байт, то код ISR становится сложнее. Чтобы обработать в нужном порядке несколько байт в буфере, вместе с буфером нужно применить переменную счетчика. Счетчик будет отслеживать количество используемых в настоящий момент ячеек. Таким образом, счетчик работает в качестве индексного указателя на данные буфера.

[Как организован буфер EEPROM]

Чтобы упростить и улучшить доступ к EEPROM по прерываниям, во встроенной оперативной памяти SRAM будут созданы 2 буфера. Два буфера будут отвечать за хранение адреса и байта данных перед их передачей в регистры EEPROM. Самый маленький размер памяти EEPROM у микроконтроллеров семейства ATmega составляет 512 байт, так что под адрес нужно 2 байта. Следовательно, общий размер буфера для однобайтного буфера EEPROM будет занимать 3 байта SRAM.

AVR104-LIFO-Buffers-fig1

Рис. 1. Буферы LIFO для данных и адреса, используемые для работы с EEPROM.

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

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

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

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

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

[Общие проблемы при использовании EEPROM]

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

Режимы сна AVR не влияют на содержимое буфера, так как содержимое регистрового файла и SRAM остается неизменным, когда микроконтроллер выходит из режима сна. Однако обработчику прерывания готовности (EE_RDY ISR) нужно уделить дополнительное внимание при использовании режимов сна AVR: подпрограмма EE_RDY ISR разбудит микроконтроллер, когда он находится либо в режиме Idle, либо в режиме ADC Noise Reduction, но не разбудит из других режимов сна. Чтобы добавить возможность использования в приложении других режимов сна, режим сна должен быть модифицирован, если данные помещены в буфер EEPROM. Режим сна возвращается в свое предыдущее состояние, когда буфер опустошен. Это подразумевает, что режим сна не должен быть изменен, пока буфер EEPROM содержит какие-то данные. Это позволит остальной части приложения вызвать инструкцию SLEEP, не рассматривая вопрос, должен ли быть изменен режим, чтобы удовлетворить требованиям к записи в EEPROM.

Должны быть соблюдены методы предосторожности, когда определяется возможность начать действительную операцию записи в EEPROM. Нельзя записывать в EEPROM, пока активен режим самопрограммирования! Таким образом, запись в EEPROM должна быть отложена, пока не завершится процедура самопрограммирования FLASH микроконтроллера. Кроме того, чтобы избежать порчи содержимого EEPROM, рекомендуется избегать записи во время пониженного напряжения питания VCC (подробности см. в даташите на используемый микроконтроллер).

[Реализация]

Пример реализации доступа к EEPROM с использованием буфера и прерываний написан в среде компилятора IAR Systems EWAVR v2.28A. Однако с незначительными модификациями исходный код может быть портирован на ImageCraft, CodeVisionAVR, или любой другой компилятор языка C.

Код примера основан на двух функциях и одном ISR. Функция EEPROM_PutChar() помещает в буфер значение, которое должно быть записано в EEPROM. Функция EEPROM_GetChar() получает и возвращает данные из нужной ячейки EEPROM. И наконец, обработчик прерывания EE_RDY отвечает за фактическую запись в EEPROM. Ниже показаны алгоритмы функций EEPROM_PutChar(), EEPROM_GetChar() и обработчика EE_RDY ISR.

AVR104-EEPROM PutChar-fig2

Рис. 2. Алгоритм EEPROM_PutChar(uint, uchar).

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

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

Кроме того, перед возвратом в основную программу функция EEPROM_PutChar() разрешает прерывание EEPROM Ready, и конфигурирует режим сна для работы для работы в режиме Idle.

AVR104-EEPROM GetChar-fig3

Рис. 3. Алгоритм EEPROM_GetChar(uint).

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

AVR104-EE RDY-ISR-fig4

Рис. 4. Алгоритм EE_RDY Interrupt Service Routine.

Перед тем, как начать любые активные действия в EE_RDY ISR, нужно проверить - не активна ли сейчас операция самопрограммирования. Если это так, что запись в EEPROM не может начаться, пока операция самопрограммирования не завершится (если в Вашей разработке не используется функция самопрограммирования, то этот шаг может быть опущен).

После того, как определено, что можно начать запись в EEPROM, то начинает работать следующий простой алгоритм (который можно также найти в даташитах на микроконтроллеры AVR) для того, чтобы начать запись в EEPROM.

1. Записывается новый адрес EEPROM в регистр EEAR.
2. Записываются новые данные EEPROM в регистр EEDR.
3. Записывается лог. 1 в бит EEMWE при записи лог. 0 в бит EEWE регистра EECR.
4. В течение 4 тактовых циклов после установки EEMWE записывается лог. 1 в бит EEWE.

Когда установлен бит EEWE, микроконтроллер останавливается на 2 цикла перед тем, как будет выполнена следующая инструкция. Когда истечет время блокировки доступа на запись EEPROM, флаг EEWE автоматически очистится аппаратурой микроконтроллера. Это приведет к останову основной программы и запуску ISR, который очистит этот элемент буфера EEPROM путем записи $FFFF в поле адреса и $FF в поле данных перед тем, как сделать возврат.

Если буфер опустошен (везде во всех ячейках записаны FF), то прерывание EEPROM Ready Interrupt запрещается, и режим сна будет восстановлен (к тому режиму, который был выбран до того, как функция EEPROM_PutChar поместила данные в буфер).

В таблице показаны варианты размера кода описанного драйвера EEPROM в зависимости от разных режимов оптимизации.

Таблица 1. Место, которое занимает код драйвера EEPROM, работающего по прерыванию.

Тип памяти Без оптимизации Оптимизация по размеру Оптимизация по скорости
Code 440 388 418
Data 50(1) 50(1) 50(1)

Примечание (1): размер буфера 16 ячеек.

[Что еще можно улучшить]

Буфер, используемый в этой разработке, работает по принципу "последний вошел первым вышел" (Last In First Out, LIFO), этот принцип обычно известен как стек. При стековом методе буфера может быть ситуация, когда будут записаны данные, когда не все данные из буфера пока записаны. Поведение буфера может быть переделано по принципу "первый пришел первый вышел" (First In First Out, FIFO), этот принцип обычно известен как очередь. Буфер, реализованный в виде очереди, обеспечит запись данных в том же порядке, каком они были поставлены в очередь, однако это может значительно увеличить требуемый объем кода и нагрузку на обработчик прерывания.

Альтернативно можно реализовать приоритезацию для данных, которая гарантирует, что критические данные будут записаны в первую очередь (это может помочь при восстановлении работоспособности в случае отключения питания). Схема приоритезации может быть реализована довольно просто, если задействовать старшие биты слова адреса (они не используются, потому что размер EEPROM обычно не превышает 4 килобайт), или использовать другое битовое поле, содержащее уровень приоритета. ISR тогда записывала бы сначала все значения с самым высоким уровнем приоритета, переходя затем к менее приоритетным ячейкам. Как вариант, можно реализовать приоритет на основе значения адреса - например, чем адрес ячейки EEPROM меньше, тем выше у неё приоритет, тогда специальное поле приоритета не требуется.

[Ссылки]

1. AVR104: Buffered Interrupt Controlled EEPROM Writes site:atmel.com.
2. AVR109: самопрограммирование AVR.
3. AVR100: доступ к энергонезависимой памяти EEPROM.
4. 141229AVR103-AVR104-AVR105.zip - исходный код, документация.
5. AVR103: режимы программирования EEPROM.