Занимательная электроника — страница 99 из 128

рерывания (см. далее), то такой дополнительный резистор следует подключать обязательно.

* * *

Процедура чтения уровня на выводе порта, если он находится в состоянии работы на вход, не совсем тривиальна. Возникает искушение прочесть данные из регистра данных PORTx, но это ничего не даст — вы прочтете только то, что там записано вами же ранее. А для чтения того, что действительно имеется на входе (непосредственно на выводе микросхемы), предусмотрена другая возможность. Для этого нужно обратиться к некоторому массиву, который обозначается PINx. Обращение осуществляется так же, как и к отдельным битам обычных РВВ (см. главу 19), но PINx не есть регистр — это просто некий диапазон адресов, чтение по которым предоставляет доступ к информации из буферных элементов на входе порта. Записывать что-либо по адресам PINx, естественно, нельзя.


Прерывания

Как и в ПК, прерывания (interrupts) в микроконтроллерах бывают двух видов. Но если в ПК прерывания делятся на аппаратные (например, от таймера или клавиатуры) и программные (фактически не прерывания, а подпрограммы, записанные в BIOS, — с распространением Windows это понятие почти исчезло из программистской практики), то в МК, естественно, все прерывания — аппаратные, а делятся они на внутренние и внешние. Любое прерывание отдельно, а также вообще возможность их возникновения требуют предварительного специального разрешения.

Следует твердо усвоить, что для инициализации любого прерывания надо в программе сделать четыре действия: разрешить прерывания вообще (по умолчанию они запрещены), затем разрешить это конкретное прерывание, установить для него один из доступных режимов и, наконец, установить вектор прерывания — указатель на метку, по которой расположена процедура подпрограммы-обработчика прерывания. И, конечно, после этого надо написать сам обработчик, иначе все это будет происходить вхолостую. Подробнее об этом также говорится в главе 19.

Внутренние прерывания могут возникать от любого устройства, которое является дополнительным по отношению к ядру системы: от таймеров, от аналогового компаратора, от последовательного порта и т. д. Внутреннее прерывание — это событие, которое возникает в системе и прерывает выполнение основной программы.

Система внутренних прерываний в AVR довольно разветвленная и представляет собой основную систему взаимодействия устройств с ядром системы, и к этому вопросу мы еще будем неоднократно возвращаться.

Внешних прерываний у МК AVR как минимум два: INT0 и INT1 (у большинства Mega есть еще третье — INT2, а у основы Arduino Mega, контроллера ATmega2560, их целых семь). Кроме основных внешних прерываний, в ряде моделей (включая и применяющиеся в Arduino ATmega328/2560) есть еще внешние прерывания типа PCINT, на которых мы здесь не будем останавливаться. Внешнее прерывание — событие, которое возникает при появлении сигнала на одном из входов, специально предназначенных для этого. Различаются три вида событий, вызывающих прерывание, и их можно устанавливать в программе: это может быть низкий уровень напряжения, а также положительный или отрицательный фронт на соответствующем выводе. Любопытно, что прерывания по всем этим событиям выполняются, даже если соответствующий вывод порта сконфигурирован на выход.

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

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

* * *

Подробности

У внимательного читателя возникает законный вопрос — а зачем вообще нужен режим внешнего прерывания по уровню? Дело в том, что оно во всех моделях выполняется асинхронно — в тот момент, когда низкий уровень появился на выводе МК. Конечно, обнаружение прерывания может произойти только по окончании текущей команды, так что очень короткие импульсы могут и пропасть. Но прерывания INT0 и INT1 в режиме управления по фронту у большинства моделей определяются наоборот, только синхронно — в момент перепада уровней тактового сигнала контроллера, поэтому их длительность не должна быть короче одного периода тактового сигнала.

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

И старые МК, вроде AT90S8515 семейства Classic (но не его mega-аналога!), могли выводиться из глубокого «сна» только внешним прерыванием по уровню, которое не всегда удобно использовать. У большинства же моделей семейства Меqа (из младших моделей — кроме ATmega8 и, кстати, «ардуиновских» 168/328) имеется еще одно прерывание INT2, которое происходит только по фронтам (по уровню не может), но, в отличие от INT0 и INT1, асинхронно. В ATtiny2313 (но не в его «классическом» аналоге!) такое асинхронное прерывание может происходить по сигналу с любого из 8 выводов порта В. Асинхронно обнаруживаются и имеющиеся во многих моделях прерывания типа PCINT, на которых мы обещали здесь не останавливаться. Это значительно повышает удобство пользования контроллером в режиме энергосбережения.


Таймеры-счетчики

В большинстве МК AVR присутствуют два или три таймера-счетчика, один из которых 16-разрядный, а остальные — 8-разрядные (в старших моделях Mega общее число счетчиков может быть до 6). Все счетчики имеют возможность предварительной загрузки значений и могут работать непосредственно от тактовой частоты (СК) процессора или от нее же, поделенной на 8, 64, 256 или 1024 (в отдельных случаях еще на 16 и 32), а также от внешнего сигнала. В целом устройство таймеров в МК, как мы говорили, похоже на счетчики 561ИЕ11/14 (см. главу 15), только функциональность их значительно расширена.

В архитектуре AVR 8-разрядным счетчикам-таймерам присвоены номера 0 и 2, а 16-разрядным — 1, 3 и далее. Некоторые 8-разрядные счетчики (обычно Timer 2, если их два) могут работать в асинхронном режиме от отдельного тактового генератора, причем продолжать функционировать даже в случае «спящего» состояния всей остальной части МК, что позволяет использовать их в качестве часов реального времени.

При использовании счетчиков-таймеров как обычных счетчиков внешних импульсов (причем возможна реакция как по спаду, так и по фронту импульса) частота подсчитываемых импульсов не должна превышать половины частоты тактового генератора МК (причем при несимметричном внешнем меандре инструкция рекомендует еще меньшее значение предельной частоты — 0,4 от тактовой). Это обусловлено тем, что при счете внешних импульсов их фронты обнаруживаются синхронно (в моменты положительного перепада тактового сигнала). Кроме того, стоит учитывать, что задержка обновления содержимого счетчика после прихода внешнего импульса может составлять до 2,5 периода тактовой частоты.

Это довольно сильные ограничения, поэтому, например, использовать МК в качестве универсального частотомера не очень удобно — быстродействующие схемы лучше проектировать на соответствующей комбинационной логике или на ПЛИС (программируемых логических интегральных схемах).

При наступлении переполнения счетчика возникает событие, которое может вызывать соответствующее прерывание. 8-разрядный счетчик Timer 0 в ряде случаев этой функцией и ограничивается. Счетчик Timer 2, если он имеется, может также вызывать прерывание по совпадению подсчитанного значения с некоторой заранее заданной величиной. 16-разрядные счетчики — более «продвинутые» и могут вызывать прерывания по совпадению с двумя независимо заданными числами А и В. При этом счетчики могут обнуляться или продолжать счет, а на специальных выводах при этом могут генерироваться импульсы (аппаратно, без участия программы).

Кроме того, 16-разрядные счетчики могут осуществлять «захват» (capture) внешних одиночных импульсов на специальном выводе. При этом может вызываться прерывание, а содержимое счетчика помещается в некий регистр. Сам счетчик при этом может обнуляться и начинать счет заново или просто продолжать счет. Такой режим удобно использовать для измерения периода внешнего сигнала или для подсчета неких нерегулярных событий (вроде прохождения частиц в счетчике Гейгера). Немаловажно, что источником таких событий может быть и встроенный аналоговый компаратор, который тогда используется как формирователь импульсов.