Таймер счетчик по переполнению
Электротехника
воскресенье, 9 августа 2020 г.
Прерывание по переполнению таймера на ассемблере для ATtiny2313
Таймеры/счетчики есть в большинстве современных микроконтроллеров. Они используются для множества разнообразных целей. Например для каких нибудь часов, широтно-импульсной модуляции (ШИМ), динамической индикации и ещё очень много для чего. В микроконтроллере ATtiny2313 их два. Один из них 8ми битный другой 16ти битный. Тот что 8ми битный может считать только до 255 т.к. для большего числа нужно большее количество бит. На основе 8ми битного счетчика можно делать что либо с малыми промежутками времени напр. ШИМ, динамическую индикацию, какие либо короткие импульсы напр. для каких нибудь протоколов связи по какому нибудь редкому интерфейсу или для чего нибудь ещё для чего нужны не долгие импульсы. На основе 16ти битного таймера счетчика можно делать длинные задержки (но короткие делать тоже можно). 16ти битный таймер/счетчик более универсальный поэтому его стоит изучить первым. На его основе можно сделать светодиодную мигалку
Рассмотрим ассемблерный код светодиодной мигалки на таймере:
Вначале подключаем специальный файл а также указываем директиву начала сегмента кода (об этом подробно написано в предыдущем уроке). Далее после директивы ORG с адресом с которого начинается выполнение программы ставим безусловный переход для того чтобы код изменения состояния светодиода (зажечь когда погашен или погасить когда зажжен) не выполнялся сразу после запуска микроконтроллера или после сброса. Далее инициализация стека (см. предыдущий урок). После инициализации стека идет инициализация таймера. Для настройки таймера есть некоторое количество регистров. В простейшем варианте (как сейчас) достаточно настроить два регистра TCCR1B — для запуска таймера и настройки его тактирования а также TIMSK — для разрешения прерывания. У разных микроконтроллеров регистры настраиваются по разному поэтому при переходе на другой микроконтроллер надо смотреть документацию и возможно устанавливать другие биты. В данном случае в регистре TCCR1B наиболее интересны первые 3 бита которыми устанавливается тактирование таймера. Если все три этих бита нули то раймер не тактируется и не работает. Если нулевой бит с единицей а остальные с нулем то таймер тактируется от того же генератора что и центрайльный процессор микроконтроллера и без предделителя. Если первый бит с единицей а остальные с нулями то тактирование также от генератора для центрального процессора но добавляется предделитель на 8. Более наглядно все опции представлены в таблице в документации:
Чтобы разрешить прерывание по переполнению 16ти битного таймера нужно в 7ой бит регистра TIMSK записать 1. Этот регистр в ATtiny2313 отличается от того же регистра в например ATmega8 т.е. TIMSK ATtiny2313 не TIMSK ATmega8 и настраивать их нужно по разному.
Далее в коде пин со светодиодом настраивается на выход (об этом в предыдущих уроках).
Командой SEI разрешаются все прерывания в том числе и то что было настроено для таймера.
Далее идет основной цикл в котором ничего интересного не происходит. Когда таймер досчитывает до максимального числа которое может поместиться во все его 16 бит, происходит переполнение этого таймера и прерывание по этому переполнению. Выполнение прогрвммы переноситься из основного цикла на ячейку флешь памяти с адресом обозначаемым как OVF1addr. А в этой ячейке организован безусловный переход на подпрограмму обработки этого прерывания. В этой подпрограмме меняется состояние светодиода (с погашенного на горящий или с горящего на погашенный). Команда RETI осуществляет выход из подпрограммы обработки прерывания. Выход происходит обратно в основной цикл где ничего интересного. Таймер опять считает до максимального значения и далее все повторяется.
Неоднозначность регистрации временных интервалов при переполнении таймера-счетчика МК.
В статье пойдет речь о захвате состояния встроенного таймера-счетчика, используемого для регистрации времени наступления внешнего события, возникающих при этом проблемах и возможных способах их решения.
Допустим, стоит задача средствами микроконтроллера регистрировать некую последовательность внешнего дискретного сигнала с сохранением времени каждого изменения для дальнейшей обработки.
Упрощенный алгоритм работы мне видится таким. Встроеный таймер/счетчик запускается на подсчет тактовых импульсов внутреннего генератора с выбранным под задачу предделителем или c использованием внешнего генератора с подходящей частотой. В обработчике прерывания смены уровня входа будет производиться захват состояния счетчика (штамп времени) и исследуемых входов с последующим сохранением этих данных в буфере. Запуск регистрации можно осуществить вручную или по срабатыванию запускающего триггера, как это делается, например, в осциллографах. Для рассмотрения затронутой темы это не важно. То же самое касается как останова захвата, так и дальнейшей обработки сохраненных данных. В предлагаемой статье я сосредоточусь на процессе регистрации.
Давайте несколько конкретизируем постановку задачи. Пусть максимальное количество регистрируемых входных линий будет (для упрощения) равно восьми (1 байт). И пусть микроконтроллер, на котором мы будем решать поставленную задачу, имеет следующие параметры:
- архитектура AVR 8-бит, тактовая частота ядра 8 МГц,
- встроенное ОЗУ 512 байт,
- 16-разрядный таймер-счетчик Т1 с режимом захвата по фронту.
Приведенные в дальнейшем расчеты базируются на этой конфигурации.
Поскольку объем ОЗУ невелик, постараемся обойтись минимальным стеком в 32 байта и переменными, расположенными в регистровом файле. То есть для буфера отдадим 480 байт памяти. Немного, но для рассмотрения темы не существенно.
Таймер настроим на циклический подсчет тактовых импульсов и захват его состояния по фронту на выводе ICP. Сигнал захвата формирует внешняя схема сравнения. При наступлении события захвата будет вызываться соответствующее прерывание для сохранения данных в ОЗУ. Собственно, захват можно производить и программно по прерыванию смены уровня порта МК, сути это не меняет. Встроенная же в таймер аппаратная функция захвата обеспечит более точное определение времени наступления внешнего события.
Выберем источником тактирования таймера внутренний тактовый сигнал с предделителем 1/8, тогда минимальный квант времени регистрации будет 1 мкс. При этом объема Т1 хватит на интервал времени в 65,535 мс до наступления переполнения счета, чего может хватить для анализа коротких последовательностей, записываемых с триггерного запуска. При записи более продолжительных последовательностей с вышеуказанными исходными данными теряется однозначность записи моментов времени.
- при наступлении переполнения Т1 производить «пустую» запись в ОЗУ для последующего подсчета переполнений. Размерность записи составит 3 байта (2 – время, 1 – сигналы).
- ввести дополнительный 8-разрядный регистр счета переполнений, инкрементируемый в соответствующем прерывании таймера, тем самым увеличив разрядность счетчика времени. Размерность записи составит 4 байта (3 – время, 1 – сигналы).
Проведем анализ предложенных вариантов.
Этот вариант позволит сохранить в буфере памяти до 160 событий, время следования которых не превысит 65,5 мс. При более продолжительной последовательности в буфере окажутся «пустые» записи, сигнализирующие о наступлении переполнения Т1 и требующие дополнительного пересчета интервалов времени. Соответственно уменьшится количество записанных внешних событий.
«Пустая» запись содержит нулевое значение времени и состояние входов предыдущего захвата.
Максимальное время регистрации при единственном событии после старта составит около 160 х 65,536 мс = 10,48 секунд. Соответственно, тем короче, чем больше событий произойдет до заполнения буфера ОЗУ.
При использовании однобайтного дополнительного регистра количество возможных записей уменьшится до 120, а общее время регистрации составит 255 х 65,536 мс = ок.16,7 секунд. Но зато отпадает необходимость записывать и обрабатывать «пустышки».
При использовании дополнительного регистра иной разрядности, например 16, количество возможных записей соответственно уменьшится, а максимальное время регистрации увеличится.
При выборе иного кванта времени приведенные временные интервалы примут другое значение.
Возможное искажение «временной оси»
Поскольку установка флагов прерываний от таймера происходит синхронно с тактовыми импульсами ЦПУ, возможна одновременная установка обоих флагов (переполнения – 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 нужно принудительно сбросить.
Соответствующая условная запись:
Для варианта б) в этой ситуации производить каких либо дополнительных действий нет необходимости, поскольку счет времени в этой последовательности обработки остается не нарушен.
Имеет смысл, однако, в обработчике переполнения проверить состояние флага ICF1 и при его наличии тут же выполнить необходимые операции, как и в обработчике захвата, с последующим его сбросом.
В написанных мной на языке ассемблера процедурах обработки прерываний выполнение дополнительных инструкций проверки и последующего сброса флага занимают примерно такое же время, как и вход в прерывание. Зато такой подход позволит избавиться от одного выхода из прерывания, как если бы эти прерывания обрабатывались привычным образом друг за другом.
Поскольку возникает интерес к продолжительности обработки прерываний, привожу результаты работы реализованных мной процедур.
Продолжительность выполнения при тактовой частоте 8 МГц.
Реализация а) — с «пустыми» записями при переполнении таймера.
- Обработчик ICF1, включая часть процедуры переполнения: 28 / 43 тактов ядра.
- Обработчик TOV1, включая часть процедуры захвата: 25 / 43 тактов.
Реализация б) — с дополнительным счетным регистром (8 бит).
- Обработчик ICF1, включая часть процедуры переполнения: 30 / 38 тактов.
- Обработчик TOV1, включая часть процедуры захвата: 17 / 36 тактов.
Количество тактов выполнения учитывает вход в процедуру и возврат в основную программу. Минимальное значение соответствует обработке только аппаратно вызваного прерывания. Максимальное – обработка обоих запросов на прерывание: вызванного первым и вложенного.
Как видно по результатам подсчета, вариант б) незначительно выигрывает по времени обработки.
Разумеется, предложеные здесь варианты реализации задачи не претендуют на исчерпаемость. Вполне возможно, что найдутся варианты с лучшими результатами. Тем более, что все вышеизложеное еще не подвергалось надлежащим испытаниям в железе.
Метки времени на основе аппаратного таймера со счетчиком переполнения
Я хочу реализовать функциональность timestamp для моей платформы на базе msp430.
Моя цель состоит в том, чтобы использовать аппаратный таймер и подсчитать количество раз, когда он переполняется, чтобы сгенерировать длинное значение timestamp (обычно uint32 для счетчика переполнения в сочетании со значением uint16 аппаратного таймера).
Вот что у меня есть:
- Счетчик переполнения увеличивается при прерывании,
- его значение проверяется каждый раз, когда запрашивается timestamp (это защищено блокировками прерываний).
- значение счетчика переполнения объединяется с текущим значением аппаратного таймера до большого значения timestamp.
Я сталкиваюсь с проблемами, когда принимаю во внимание время прерываний.
Первая наивная реализация у меня была:
Это произойдет, когда переполнение произойдет после получения значения таймера, но до того, как прерывания будут отключены. Тогда overflow_count_local будет на 1 больше того, что было при назначении timer_value .
Я попытался добавить другие проверки, чтобы обнаружить это возможное прерывание
Это тоже не сработает, на этот раз потому, что таймер мог переполниться после отключения прерываний, но до назначения second_timer_value . Тогда это сделало бы overflow_count_local одним слишком маленьким.
Как бы я ни старался это изменить, кажется, всегда есть случай, который не освещается. Есть ли известный способ заставить это работать?
- Таймер используется и для других функций (с capture/compare) и не может быть остановлен.
- Модуль RTC msp430 не может быть использован для этого, потому что он используется для сохранения фактического времени суток.
- 32-битный таймер не доступен.
timerembeddedinterruptmsp430
2 ответа
- Насколько точен Thread.sleep?
Я изучаю компьютерное оборудование, где мы узнаем, что использование аппаратного таймера дает более точные результаты, чем программная задержка. Я написал программную задержку в assembly на 1 миллисекунду, и я могу запустить процесс, который повторяет каждый миллисекод, используя эту задержку, и.
У меня есть ATMega8515 , и я пытаюсь настроить прерывание таймера так, чтобы, если процесс займет слишком много времени, он отключился. Я настраиваю таймер с помощью: void init_software_interupt(double time) < OCR1A = time; TCCR1A = 0; TCCR1B = 0; TCCR1B |= (1
Следующий алгоритм не требует блокировки (отключение прерываний не требуется):
Получите переполнение, затем таймер (в таком порядке), пока переполнение не будет одинаковым по обе стороны от считываемого таймера.
Обычно это должно занимать не более нуля или одной итерации, если только не происходит исключительно длительного переключения контекста на другой поток или прерывания, во время которого таймер снова переполняется.
Может быть, что-то вроде этого. Я не вижу необходимости отключать прерывания с помощью этого.
Похожие вопросы:
У меня есть таблица с тремя столбцами. Я хочу создать unique index обо всех трех столбцах со счетчиком, который говорит мне, где находятся дубликаты. Я также хочу удалить их.
В настоящее время я пишу простое мини-приложение на основе таймера в C#, которое выполняет действие n раз каждые k секунд. Я пытаюсь принять стиль разработки, основанный на тестировании, поэтому моя.
Мне нужно иметь дело со счетчиком, который дает мне тики для моего приложения. Счетчик имеет 32 бита, так что мне нужно знать, как с ним справиться, когда он обернется. например: У меня есть.
Я изучаю компьютерное оборудование, где мы узнаем, что использование аппаратного таймера дает более точные результаты, чем программная задержка. Я написал программную задержку в assembly на 1.
У меня есть ATMega8515 , и я пытаюсь настроить прерывание таймера так, чтобы, если процесс займет слишком много времени, он отключился. Я настраиваю таймер с помощью: void.
Мне нужно выполнить процедуру через постоянные интервалы времени. Процедура занимает много времени для выполнения, и в течение этого времени другое прерывание должно быть активным. Кроме того, очень.
Такой question.I я пытаюсь понять, как использовать семафор. Для упражнения я взял классическую задачу читателей / писателей с циклическим буфером памяти. Я хотел бы говорить только о писателях.
У меня есть проект c# и лог-файл с ошибками. Я хочу выдать все ошибки в новом файле журнала со счетчиком, если есть какие-то ошибки дважды или более. Я использовал команду: bool alreadyExist =.
Мне нужно скрыть значок переполнения в кнопке аппаратного меню в моем приложении Xamarin Android (три точки) — см. снимок экрана. Как мне это сделать?
Я хочу реализовать таймер в моем приложении, если пользователь нажимает часы в кнопке таймер должен начинаться со времени устройства, и таймер должен запускаться при подаче паузы таймер должен.
Прерывания по переполнению таймера-счётчика
Прерывания в контроллерах могут быть внешние и внутренние. Если запрос на прерывание пришёл по внешним входам контроллера, то такое прерывание называют внешним. Внешние прерывания мы рассмотрели лабораторной работе 2.2. Источником внешних прерываний являются как правило датчики, переключатели и пр.
Если запрос на прерывание вырабатывается внутри контроллера – его функциональными блоками или процессором, то такое прерывание называют внутренним. Одним из источников внутренних прерываний являются таймеры-счётчики.
Контроллер ATmega8 имеет следующие таймеры:
v T/C0 (Timer/Counter0) – 8-разрядный таймер-счётчик, имеющий предделитель частоты и режим сравнения;
v T/C1 (Timer/Counter0) – 16-разрядный таймер-счётчик, имеющий все основные режимы работы таймеров-счётчиков, в том числе режим захвата событий;
v T/C2 (Timer/Counter0) – 8-разрядный таймер-счётчик, имеющий предделитель частоты, режим сравнения и режим широтно-импульсной модуляции (ШИМ).
v WDT (Watch Dog Timer) – сторожевой таймер, имеющий предделитель частоты и собственный генератор с частотой 1 МГц.
На этом практическом занятии рассмотрим задание прерывания по переполнению T/C0 и его обработку. Счётный 8-битный регистр TCNT0 (Timer/Counter0) этого таймера хранит текущую сумму импульсов от предделителя частоты. При переходе содержимого этого регистра с 255 к 0 наступает переполнение T/C0, что и вызывает так называемое прерывание по переполнению T/C0. Этот регистр доступен по чтению и по записи в любой момент времени.
Для инициализации прерывания по переполнению необходимо установить бит разрешения прерывания TOIE0 (Timer/Counter0 Overflow Interrupt Enable) в регистре масок прерываний TIMSK (Timer/Counter Interrupt Mask Register) и задать коэффициент предварительного деления частоты PRESCALER в регистре управления таймером-счётчиком TCCR0 (Timer/Counter Control Register0). На рисунках 2.3.1 и 2.3.2 описаны биты регистров TIMSK и TCCR0 соответственно. Биты выбора CS (Clock Select) источника тактирования описаны в Таблице 2.3.1.
Таблица 2.3.1. Описание битов выбора источника тактирования
CS02 | CS01 | CS00 | Описание |
Нет источника (T/C0 – остановлен) | |||
Источник – тактовый генератор с fтакт | |||
Источник – тактовый генератор с fтакт/8 | |||
Источник – тактовый генератор с fтакт/64 | |||
Источник – тактовый генератор с fтакт/256 | |||
Источник – тактовый генератор с fтакт/1024 | |||
Источник – спадающий фронт импульсов на линии T0 | |||
Источник – возрастающий фронт импульсов на линии T0 |
Как видно из таблицы мы можем выбирать всего пять коэффициентов предварительного делителя частоты. Время до срабатывания прерывания можно оценить по суммарной длительности импульсов тактового генератора, которые заполнят регистр TCNT0 от 0 до 255, т.е. всего 256 (таймер у нас 8-битный). Для этого надо выбранный из Таблицы 1 коэффициент предделителя частоты умножить на число 256 и разделить на тактовую частоту генератора fтакт.
Например, пусть fтакт = 8МГц, коэффициент предделителя PRESCALER = 010, т.е. тактовая частота делится на восемь (к регистру TCNT0 будет проходить только каждый восьмой импульс от генератора). Значит время до срабатывания прерывания T
T = 8*256 / 8*10 6 = 0,000256 сек.
Таким образом, время срабатывания прерывания равно 256 микросекундам. Для увеличения этого времени надо увеличить коэффициент предделителя. К примеру, при PRESCALER = 101, T = 0,032768 сек.
Пример 2.3.1. Рассмотрим задачу управления группой реле с помощью прерывания по переполнению T/C0. На рис. 2.3.3 изображена схема подключения шести реле Р1-Р6 к линиям порта B через схему ULN2003A, содержащую семь транзисторных ключей на составных транзисторах (схема Дарлингтона), шесть из которых нами используются, а один – нет. Они позволяют управлять нагрузкой до 500 mA при напряжении Uпит до 50 В. Внутри этой микросхемы уже имеется встроенный защитный диод, который можно подключать или отключать, осуществляя внешние соединения. На листинге 2.3.1 представлена программа подачи напряжения на управляющие обмотки реле Р1-Р6 на строго заданное время T = 0,016 сек., по истечению которого напряжение снимается. Включение реле производится подачей на соответствующую линию порта B логической единицы. Отключается реле логическим нулём.
В начале программы расписана таблица векторов прерываний, в которой инициализируются только два прерывания: INIT – для инициализации контроллера при подаче питания и TIM0_OVF – для обработки прерывания по переполнению разрядной сетки таймера-счётчика T/C0. Процедура ERROR обслуживает остальные прерывания, если они случайно возникнут.
Далее в программе задаётся коэффициент предделителя частоты в шестнадцатеричном представлении PRESCALER = 0x04, то есть fтакт/256. На тактовой частоте 4 Мгц такой предделитель обеспечит время срабатывания T = 0,016384 сек.
.INCLUDE «m8def.inc» ; AtMega8 definitions
.ORG 0x00 rjmp INIT ; начальные установки
.ORG INT0addr rjmp ERROR ; ловушка для всех
.ORG INT1addr rjmp ERROR ; неинициализированных прерываний
.ORG OC2addr rjmp ERROR
.ORG OVF2addr rjmp ERROR
.ORG ICP1addr rjmp ERROR
.ORG OC1Aaddr rjmp ERROR
.ORG OC1Baddr rjmp ERROR
.ORG OVF1addr rjmp ERROR
.ORG OVF0addr rjmp TIM0_OVF ; прерывание Timer/Counter0
.ORG SPIaddr rjmp ERROR
.ORG URXCaddr rjmp ERROR
.ORG UDREaddr rjmp ERROR
.ORG UTXCaddr rjmp ERROR
.ORG ADCCaddr rjmp ERROR
.ORG ERDYaddr rjmp ERROR
.ORG ACIaddr rjmp ERROR
.ORG TWIaddr rjmp ERROR
.ORG SPMRaddr rjmp ERROR
ERROR: rjmp ERROR ; бесконечный цикл ловушки
.EQU PRESCALER = 0x04 ; настройка делителя таймера
Cli ; запрет прерываний
ldi r16,high(RAMEND) ; указ-ль стека на последний байт ОЗУ
out DDRB,r16 ; инициализируем порт B на вывод
out PORTB,r16 ; снимаем напряжение с реле
ldi r16,0x01 ; TOIE0=1
out TIMSK,r16 ; прерывание по переполнению регистра TCNT0
out TCCR0,r16 ; включаем прерывание от Timer/Counter0
out PORTB,r16 ; подаём напряжение на реле
sei ; разрешаем прерывания
out PORTB,r16 ; снимаем напряжение с реле
out TCCR0,r16 ; отключаем прерывание от Timer/Counter0
Скопируйте указанную программу в окно редактора исходного текста, ассемблируйте и запустите отладчик (рис. 2.3.4). Так как в нашей программе используется порт B для взаимодействия с группой реле Р1-Р6 и таймер-счётчик T/C0, то для удобства просмотра регистров порта B и регистров таймера T/C0 отобразим их на панели регистров. Для этого щёлкнем по порту B на панели модулей и, удерживая клавишу CTRL, щёлкнем по таймеру-счётчику T/C0, добавив тем самым регистры таймера к регистрам порта на панели регистров (рис. 2.3.4).
Делать пошаговое выполнение мы не будем, так как для этого потребуется нащёлкать мышкой несколько тысяч тактов генератора. Поэтому вначале поставим курсор перед основным циклом программы – командой sei и выполним нашу программу до указанного положения курсора с помощью пункта меню Debug ® Run to Cursor или кнопкой на панели инструментов, указанной на рис. 2.3.4. На рисунке 2.3.5 представлен внешний вид программы и содержимое регистров контроллера по достижении курсора.
Для того чтобы убедиться, что контроллер накапливает сумму тактов в регистре TCNT0, выполним несколько тактов вручную с помощью клавиши F11 или соответствующей кнопки на панели инструментов. Удерживайте клавишу F11 и наблюдайте за увеличением счётчика тактов. На 257 такте в счётчик TCNT0 запишется первая единица.
На рисунке 2.3.6 показано содержимое регистров после выполнения 259 тактов. Команда sei устанавливает флаг разрешения прерываний и далее программа входит в основной цикл, где и будет выполняться до тех пор, пока счётчик таймера T/C0 досчитав от 0 до 255 и вернувшись на 0, вызовет прерывание TIM0_OVF. Однако на данный момент времени счётчик таймера равен единице. Этот счётчик будет инкрементироваться каждые 256 тактов тактовой частоты, так как константой PRESCALER мы задали деление тактовой частоты на 256.
Дальше выполнять вручную не имеет смысла, поэтому поставим курсор на начало процедуры обслуживания прерывания, т.е. на метку TIM0_OVF и выполним программу до указанного положения курсора.
Как можно увидеть (рис. 2.3.7) счётчик тактов стал равен 65544. Таким образом, программа ожидала прерывания, включив реле, 65544‑16=65528 тактов. Тактовая частота процессора равна 4 МГц. Следовательно, прошло ровно 65528/4000000 = 0,016382 сек. или »16 мс.
Выполнив вручную по шагам процедуру TIM0_OVF вплоть до возвращения в основной цикл программы (рис. 2.3.8) можно увидеть, как сначала отключаются все реле, потом запрещается прерывания таймера T/C0, потом по команде возврата из прерывания устанавливается флаг разрешения прерываний.
После этого программа будет находиться в вечном цикле и реле больше не включатся, так как мы в процедуре TIM0_OVF запретили прерывания таймера T/C0.
Задание 2.3.1. Разработать электрическую схему и программу подачи питания на управляющие обмотки 12 маломощных реле на 0,065536 сек. (65мс.) по включении контроллера.
Задание 2.3.2. Разработать электрическую схему и программу для управления реле, которое должно попеременно включаться и отключаться с периодом 130 мс. (65 мс. + 65мс.) и скважностью 1/2.
Задание 2.3.3. Разработать электрическую схему и программу для управления тремя светодиодами, которые должны включаться и отключаться с периодом 4 сек. и скважностью 1/2. Подсказка: организуйте дополнительный цикл, который будет считать нужное количество срабатываний прерываний по переполнению таймера.