Наш магазин на eBay Наш магазин на AliExpress Наш канал в telegram

Неоднозначность регистрации временных интервалов при переполнении таймера-счетчика МК.

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

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

Упрощенный алгоритм работы мне видится таким. Встроеный таймер/счетчик запускается на подсчет тактовых импульсов внутреннего генератора с выбранным под задачу предделителем или c использованием внешнего генератора с подходящей частотой. В обработчике прерывания смены уровня входа будет производиться захват состояния счетчика (штамп времени) и исследуемых входов с последующим сохранением этих данных в буфере. Запуск регистрации можно осуществить вручную или по срабатыванию запускающего триггера, как это делается, например, в осциллографах. Для рассмотрения затронутой темы это не важно. То же самое касается как останова захвата, так и дальнейшей обработки сохраненных данных. В предлагаемой статье я сосредоточусь на процессе регистрации.

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

  • архитектура AVR 8-бит, тактовая частота ядра 8 МГц,
  • встроенное ОЗУ 512 байт,
  • 16-разрядный таймер-счетчик Т1 с режимом захвата по фронту.

Приведенные в дальнейшем расчеты базируются на этой конфигурации.

Поскольку объем ОЗУ невелик, постараемся обойтись минимальным стеком в 32 байта и переменными, расположенными в регистровом файле. То есть для буфера отдадим 480 байт памяти. Немного, но для рассмотрения темы не существенно.

Таймер настроим на циклический подсчет тактовых импульсов и захват его состояния по фронту на выводе ICP. Сигнал захвата формирует внешняя схема сравнения. При наступлении события захвата будет вызываться соответствующее прерывание для сохранения данных в ОЗУ. Собственно, захват можно производить и программно по прерыванию смены уровня порта МК, сути это не меняет. Встроенная же в таймер аппаратная функция захвата обеспечит более точное определение времени наступления внешнего события.

Выберем источником тактирования таймера внутренний тактовый сигнал с предделителем 1/8, тогда минимальный квант времени регистрации будет 1 мкс. При этом объема Т1 хватит на интервал времени в 65,535 мс до наступления переполнения счета, чего может хватить для анализа коротких последовательностей, записываемых с триггерного запуска. При записи более продолжительных последовательностей с вышеуказанными исходными данными теряется однозначность записи моментов времени.

Варианты решения:

  1. при наступлении переполнения Т1 производить «пустую» запись в ОЗУ для последующего подсчета переполнений. Размерность записи составит 3 байта (2 – время, 1 – сигналы).
  2. ввести дополнительный 8-разрядный регистр счета переполнений, инкрементируемый в соответствующем прерывании таймера, тем самым увеличив разрядность счетчика времени. Размерность записи составит 4 байта (3 – время, 1 – сигналы).

Проведем анализ предложенных вариантов.

  1. Этот вариант позволит сохранить в буфере памяти до 160 событий, время следования которых не превысит 65,5 мс. При более продолжительной последовательности в буфере окажутся «пустые» записи, сигнализирующие о наступлении переполнения Т1 и требующие дополнительного пересчета интервалов времени. Соответственно уменьшится количество записанных внешних событий.

    «Пустая» запись содержит нулевое значение времени и состояние входов предыдущего захвата.

    Максимальное время регистрации при единственном событии после старта составит около 160 х 65,536 мс = 10,48 секунд. Соответственно, тем короче, чем больше событий произойдет до заполнения буфера ОЗУ.

  2. При использовании однобайтного дополнительного регистра количество возможных записей уменьшится до 120, а общее время регистрации составит 255 х 65,536 мс = ок.16,7 секунд. Но зато отпадает необходимость записывать и обрабатывать «пустышки».

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

  3. При выборе иного кванта времени приведенные временные интервалы примут другое значение.

    Возможное искажение «временной оси»

    Поскольку установка флагов прерываний от таймера происходит синхронно с тактовыми импульсами ЦПУ, возможна одновременная установка обоих флагов (переполнения – TOV1 и захвата – ICF1). Даже, если оба флага установятся в разных тактах ЦПУ, во время обработки «длинной» операции (2-3 такта), реакция на них будет такой же, как если бы оба флага установились одновременно. То же самое действительно в случае, когда по каким-либо причинам прерывания были временно запрещены, т.е. на момент их разрешения АЛУ выбирает из нескольких запросов на прерывание самое приоритетное.

    Установленный флаг (лог.1) будем считать признаком наличия события, а сброшеный (лог.0) – признаком его отсутствия.

    Ситуация 1. Одновременная установка флагов TOV1 и ICF1

    При наступлении этой ситуации в регистре захвата (ICR1) окажется (предположительно) число 0. Согласно приоритету первым будет обработано прерывание захвата.

    В варианте а) при этом событии обработчики произведут две одинаковые записи:

    • первая – состояния [ICR1]=0 и входных сигналов на момент обработки захвата
    • вторая – «пустая» с теми же значениями

    В варианте б) обработчиком ICF1 в память будет записано нулевое состояние ICR1, еще не инкрементированое (старое) значение дополнительного регистра и новое состояние входов. Т.е. штамп времени окажется неверным.

    Ситуация 2. Прерывание TOV1 опережает ICF1

    Рассмотрим ситуацию, когда захват произошел чуть позже наступления переполнения Т1 – в самом начале обработки прерывания TOV1. Поскольку состояние Т1 не меняется в течении восьми (предделитель 1/8) тактов ядра, то наступивший захват в течении этого времени сохранит в ICR1 нулевое состояние счетчика.

    «Пустая» запись обработчика TOV1 в варианте а) сохранит нули времени и предыдущее состояние входов. Последующая за ней запись обработчика ICF1 запишет также нули времени и новое состояние входов.

    Неоднозначность в этой ситуации возникнет, если до следующего переполнения Т1 не произойдет изменения входов. Поскольку последующая «пустая» запись окажется копией записи последнего захвата, то будет невозможно определить, какая из этих двух записей является «пустышкой». Это необходимо для понимания, было ли переполнение обработано до захвата или после, что важно для определения, какая из двух описываемых ситуаций имела место.

    Для наглядности приведу условную запись, в которой время отображается цифрой, а состояние входов – буквой:

    … 123A, 0A, 0B, 0B, 456B …

    В такой ситуации невозможно понять, когда произошел захват: в комбинации 0A,0B или 0B,0B. Первая комбинация соответствовала бы наступлению ситуации 2, когда прерывание переполнения обрабатывается первым. Вторая же – ситуации 1, когда переполнение обрабатывается вторым.

    В варианте б) обработчиком ICF1 в память будет записано нулевое состояние ICR1, уже инкрементированое (новое) значение дополнительного регистра и новое состояние входов. То есть истинное значение счета.

    Таким образом оба варианта не гарантируют однозначности трактовки штампов времени.

    Попытка найти выход из такого положения.

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

    В ситуации 1 для варианта б) в обработчике ICF1 необходимо проверить состояние флага TOV1 и при его наличии проверить захваченное значение таймера. Если захвачен ноль, то произвести необходимую коррекцию дополнительного счетного регистра. То есть перед записью в буфер его состояние следует инкрементировать, а перед выходом из прерывания принудительно сбросить флаг TOV1, чтобы не вызвать уже ненужное прерывание переполнения.

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

    Для варианта а) в ситуации 1 видится нескольно иное решение. В обработчике захвата необходимо проверить значение ICR1. Если оно окажется нулевым (захват в момент переполнения), то необходимо декрементировать это значение, принеся в жертву точность записи времени. Будем считать, что захват произошел ранее переполнения именно на 1 квант времени. В ОЗУ при этом запишется значение ICR1 = 0xFFFF.

    А поскольку уже известно, что наступило переполнение Т1, то следующую «пустую» запись стоит произвести тут же и сбросить флаг TOV1 за ненадобностью вызова его обработчика. Тем самым сократить время обработки события.

    Условная запись этих состояний будует выглядеть так:

    … 0A, -1B, 0B, …   (-1 = 0xFFFF)

    В ситуации 2 для варианта а) предлагается такое решение. В обработчике переполнения произвести «пустую» запись и проверить состояние флага ICF1. Если он установлен, считать состояние входов и проверить ICR1.

    При его нулевом значении произвести запись инкрементированного значения ICR1 вместе с новым состоянием сигналов. Здесь мы тоже жертвуем точностью в 1 квант времени в пользу разрешения неоднозначности.

    Естественно, после этого обрабатывать прерывание захвата уже нет смысла, поэтому флаг ICF1 нужно принудительно сбросить.

    Соответствующая условная запись:

    … 0A, 1B, 0B …

    Для варианта б) в этой ситуации производить каких либо дополнительных действий нет необходимости, поскольку счет времени в этой последовательности обработки остается не нарушен.

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

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

    Поскольку возникает интерес к продолжительности обработки прерываний, привожу результаты работы реализованных мной процедур.

    Продолжительность выполнения при тактовой частоте 8 МГц.

    Реализация а) — с «пустыми» записями при переполнении таймера.

    • Обработчик ICF1, включая часть процедуры переполнения: 28 / 43 тактов ядра.
    • Обработчик TOV1, включая часть процедуры захвата: 25 / 43 тактов.

    Реализация б) — с дополнительным счетным регистром (8 бит).

    • Обработчик ICF1, включая часть процедуры переполнения: 30 / 38 тактов.
    • Обработчик TOV1, включая часть процедуры захвата: 17 / 36 тактов.

    Количество тактов выполнения учитывает вход в процедуру и возврат в основную программу. Минимальное значение соответствует обработке только аппаратно вызваного прерывания. Максимальное – обработка обоих запросов на прерывание: вызванного первым и вложенного.

    Как видно по результатам подсчета, вариант б) незначительно выигрывает по времени обработки.

    Разумеется, предложеные здесь варианты реализации задачи не претендуют на исчерпаемость. Вполне возможно, что найдутся варианты с лучшими результатами. Тем более, что все вышеизложеное еще не подвергалось надлежащим испытаниям в железе.

Добавить комментарий