Юрий Ревич - Занимательная электроника
Для установки выхода в состояние единицы нужно отдельно установить соответствующий бит в регистре данных порта (обозначается PORTx), а для установки в 0 — сбросить этот бит. Направление работы вывода (вход-выход, регистр DDRx) и его состояние (0–1, PORTx) путать не следует.
Регистр данных PORTx фактически есть просто выходной буфер — все, что в него записывается, тут же оказывается на выходе. Но если установить вывод порта на вход (т. е. записать в регистр направления DDRx логический ноль), как это сделано по умолчанию, то регистр данных PORTx будет играть несколько иную роль — установка его разрядов в ноль означает, что вход находится в третьем состоянии с высоким сопротивлением, а установка в единицу подключит к выводу «подтягивающий» (pull-up) резистор сопротивлением 20–50 кОм (в семействе Classic оно составляло 35-120 кОм).
* * *
Заметки и а полях
Встроенного pull-up-резистора в большинстве случаев оказывается недостаточно для надежной работы — из-за наводок МК может сбоить, и параллельно этому внутреннему лучше устанавливать дополнительный внешний резистор с сопротивлением от 1 до 5 кОм (в критичных по отношению к потреблению случаях его величину можно увеличить до 20–30 кОм). Например, если вы подключаете ко входу выносную кнопку с двумя выводами, которая коммутируется на «землю», или вывод работает на «общую шину» с удаленными (находящимися на другой плате) устройствами, или вывод осуществляет функцию внешнего прерывания (см. далее), то такой дополнительный резистор следует подключать обязательно.
* * *
Процедура чтения уровня на выводе порта, если он находится в состоянии работы на вход, не совсем тривиальна. Возникает искушение прочесть данные из регистра данных 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), только функциональность их значительно расширена.