Параллельные порты ввода/вывода
Портов ввода/вывода (повторим, что их не следует путать ни с регистрами ввода/вывода, ни с последовательными портами МК для обмена информацией с внешними устройствами) в разных моделях может быть от 1 до 7. Номинально порты 8‑разрядные, в некоторых случаях разрядность ограничена числом выводов корпуса и может быть меньше восьми. Порты обозначаются буквами А, В, С, D и т. д., причем необязательно по порядку, – в младших моделях могут наличествовать, например, только порты В и D (как в ATtiny2313) или вообще только один порт В (как в ATtinylx).
Для сокращения числа контактов корпуса в подавляющем большинстве случаев внешние выводы, соответствующие портам, кроме своей основной функции (двунаправленного ввода/вывода) несут также и дополнительную. Отметим, что кроме как для вывода Reset , если он может работать в альтернативном режиме, никакого специального переключения выводов портов не требуется. Если вы, к примеру, в своей программе инициализируете последовательный порт UART, то соответствующие выводы порта (например, в ATmega8335 это выводы порта PD0 и PD1) будут работать именно в альтернативной функции – как ввод и вывод UART. При этом в промежутках между таким специальным использованием выводов их можно задействовать в качестве обычных двунаправленных выводов. На практике приходится применять схемотехнические меры для изоляции функций друг от друга, поэтому злоупотреблять этой возможностью не рекомендуется.
Типичным примером многообразия функций портов может служить базовый для Arduino ATmega168/328. В нем три порта (В, С и D ), но все восемь линий доступны лишь для порта D , порты В и С – лишь частично, так что программисту доступны максимум 20 цифровых линий. Шесть из них могут быть так же использованы, как аналоговые входы встроенного АЦП (на платах Arduino помечены буквой А ), а некоторая часть остальных также задействована под различные альтернативные функции (последовательные порты, прерывания), применяемые по мере надобности.
Выводы портов в достаточной степени автономны, и их режим может устанавливаться независимо друг от друга. По умолчанию при включении питания все дополнительные устройства отключены, а порты работают на вход, причем находятся в третьем состоянии с высоким импедансом (т. е. с высоким входным сопротивлением). Работа на выход требует специального указания, для чего в программе нужно установить соответствующий нужному выводу бит в регистре направления данных (этот регистр обозначается DDRx, где х – буква, обозначающая конкретный порт, например для порта А это будет DDRa). Если бит сброшен (т. е. равен логическому нулю), то вывод работает на вход (как по умолчанию), если установлен (т. е. равен логической единице) – то на выход.
Для установки выхода в состояние единицы нужно отдельно установить соответствующий бит в регистре данных порта (обозначается 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, на которых мы обещали здесь не останавливаться. Это значительно повышает удобство пользования контроллером в режиме энергосбережения.
Дата добавления: 2016-05-11; просмотров: 1736;