Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

КР1 / 10

.docx
Скачиваний:
3
Добавлен:
08.04.2022
Размер:
281.48 Кб
Скачать

10. Регистры ввода-вывода. Схема устройства вывода микроконтроллера, управляющие регистры, режимы работы.

Все регистры ввода/вывода (в дальнейшем буду писать РВВ) условно можно разделить на два типа: - служебные регистры микроконтроллера, - регистры, относящиеся к конкретным периферийным устройствам. Все регистры ввода/вывода занимают свое адресное пространство в памяти данных (SRAM) – от 32 до 95 (или в шестнадцатиричном счислении от $20 до $5F) и идут сразу за регистрами общего назначения, в так называемом адресном пространстве ввода/вывода. Каждый регистр восьмиразрядный и занимает память в один байт. Всего МК может иметь 64 регистра ввода/вывода – максимальное число (за редким исключением). В тоже время, если МК простенький и в нем мало устройств, и он использует меньшее число РВВ, в области памяти данных все равно резервируется 64 адреса. Некоторым, слишком навороченным МК, стандартного значения в 64 РВВ недостаточно. В таких МК в адресном пространстве памяти данных выделяется еще дополнительно 160 ячеек памяти для дополнительных регистров ввода/вывода.

Каждый регистр ввода/вывода имеет свой номер – от 0 до 63 (или в шестнадцатиричном виде – от $00 до $3F), который соответствует его адресу в адресном пространстве ввода/вывода. Адрес РВВ в адресном пространстве ввода/вывода и адреса соответствующих им ячеек в ОЗУ (памяти данных) не совпадают. Если регистр имеет номер 0, то в адресном пространстве ОЗУ он будет занимать 32 ячейку памяти (ведь сначала идут 32 регистра общего назначения, а за ними уже – РВВ). Для определения адреса РВВ в области памяти данных необходимо прибавить к его номеру 32. Помимо номера, каждый регистр имеет свое имя (буквенную аббревиатуру) – для удобства программистов. Для разных МК имена регистров, имеющих одинаковое назначение, обычно совпадают, а вот номер регистра может отличаться. Но это не так и важно, потому, что при программирование оперируют в основном именами регистров. К примеру, для управления портом ввода-вывода имеется три РВВ – один служит для задания направления работы порта (на вывод или ввод информации), второй для … рассмотрим в следующей статье. И так каждое устройство.

Кусочек из таблицы регистров ввода/вывода (первое число – номер регистра, и оно же его адрес в адресном пространстве РВВ, в скобках указывается адрес регистра в адресном пространстве памяти данных, т.е. номер +32):

Стек, или указатель стека – это специальный регистр, который предназначен для организации стековой памяти. По мере заполнения ячеек памяти первые данные уходят в глубь стека, и добраться до них можно, только вытащив сначала последние введенные данные. Допустим, если мы запишем в стек последовательно три числа: 10,20 и 30, то для того, чтобы затем извлечь из стековой памяти число 10, предварительно придется извлечь числа 30 и 20. Т.е., значение записанное последним всегда будет прочитано первым. Для нас тоже очень удобно сохранять в стеке данные при входе в подпрограмму, а затем извлекать их по возвращению из подпрограммы. Также удобно в стеке сохранять промежуточные результаты вычислений, а затем, по мере необходимости, извлекать их.

В микроконтроллерах, в которых отсутствует память данных (ОЗУ), а это часть МК семейства Tiny, стек организуется аппаратно. В таких МК стек располагается в собственной памяти, а глубина его равна трем уровням. Аппаратный стек не доступен программисту, его в своих целях использует только сам МК, сохраняя в нем адреса команд при переходе к подпрограммам. Так как возможности аппаратного стека мы использовать в своих целях не можем, то на этом и закончим его изучение. Во всех остальных МК, которые имеют память данных, стек организуется программно. Стек в этом случае не имеет собственной памяти, а использует память данных (ОЗУ). Такой стек доступен для наших целей и рассмотрим его подробней. Для организации процесса записи данных в стек и их чтения предназначен указатель стека. В качестве указателя стека используются один или два регистра ввода/вывода: – если память данных небольшая (до 256 байт), используется один восьмиразрядный РВВ – SPL; – если память данных большая (более 256 байт), к первому РВВ – SPL, добавляется второй – SPH, и вместе они составляют один шестнадцатиразрядный указатель стека. В указателе стека содержится адрес ячейки памяти, в которую будут записаны или считаны данные.

Для программиста в системе команд МК есть две специальные команды: - PUSH – команда записи в стек – POP – команда чтения из стека Запись данных в стек: Для того, чтобы записать данные в стек, их предварительно необходимо загрузить в любой РОН. По команде PUSH МК записывает данные из указанного нами РОН в память данных по адресу, на который указывает указатель стека, а затем уменьшает содержимое стека на 1 (если указатель восьмиразрядный) или на 2 (если указатель шестнадцатиразрядный). Новая команда PUSH запишет данные в следующую ячейку ОЗУ и опять уменьшит содержимое указателя стека. И так, далее.

Чтение данных из стека: По команде POP, МК сначала увеличивает содержимое указателя стека на 1 или 2, а затем считывает данные с ячейки ОЗУ, на которую указывает указатель стека. И так, далее.

После сброса МК содержимое указателя стека равно нулю. Т.е. получается, что после сброса, указатель стека указывает на нулевую ячейку памяти ОЗУ. А теперь смотрите, что получится, если мы попробуем что-то записать в стек: – первая команда PUSH – содержимое РОН будет записано в нулевую ячейку памяти,а затем МК попытается уменьшить содержимое стека на 1 или 2 – и ни чего не получится – адрес ячейки памяти не может быть меньше нуля! Поэтому, прежде чем пользоваться стековой памятью, необходимо записать в указатель стека значение его вершины – адрес конечной ячейки памяти с которой начнется запись в стек. Обычно вершиной стека указывают адрес последней ячейки памяти данных. Если у вашего МК ОЗУ составляет 128 байт, то указывают адрес 128 ячейки памяти. Под стек можно использовать всю память, если только вы не будете хранить в ней свои переменные. Если в качестве вершины стека вы укажите конечную ячейку памяти, то следить за стековой памятью в большинстве случаев не обязательно. А если вершиной стека указать ячейку памяти поближе к началу, и при этом использовать ОЗУ для хранения своих данных, то следить за размерностью стека придется – он может залезть на ячейки в которых вы будете хранить свои данные.

Счетчик команд

Счетчик команд – представляет собой регистр, в котором содержится адрес следующей исполняемой команды.

Размер счетчика команд может быть от нескольких разрядов до шестнадцати (двухбайтовый). Размерность счетчика зависит от объема памяти программ. Счетчик команд не доступен для программиста, в него мы не можем ничего записать и не можем из него ничего считать. Работой счетчика команд руководит единолично МК.

При включении питания устройства или сброса микроконтроллера, счетчик команд устанавливается в ноль, т.е. указывает на нулевой адрес памяти программ. Поэтому адресу начинается наша программа (если мы не используем прерывания) или адрес, по которому начинается наша программа (если мы используем прерывания).  При нормальном ходе программы содержимое счетчика команд автоматически увеличивается на 1 или 2 (в зависимости от выполняемой команды) в каждом машинном цикле. Т.е., после выполнения команды, счетчик будет указывать адрес следующей команды. Этот порядок будет нарушен, если на пути МК при выполнении программы встретится команда перехода, команда вызова подпрограммы (или возврата из подпрограммы), а также при возникновении прерывания. В этом случае содержимое счетчика – адрес следующей команды, записывается в стек, а в счетчик записывается адрес, по которому надо перейти по команде перехода или по прерыванию. По команде возвращения из подпрограммы, в счетчик записывается адрес команды, сохраненный в стеке, программа продолжается дальше. Все это проделывается автоматически, без нашего участия.

На входе стоит небольшая защита из диодов, которая призвана защитить вход микроконтроллера от превышения напряжения питания. Если напряжение на входе будет выше напряжения питания, то верхний диод откроется, и это напряжение уйдёт на шину питания, где с ним будет бороться источник питания и его фильтры. Если на вход попадет отрицательное напряжение (ниже уровня земли), то оно будет нейтрализовано через нижний диод и уйдет на землю. Диоды эти достаточно маломощные (ввиду их небольших размеров), поэтому защитить они могут только от небольших скачков напряжения. Отклонение от стандартного напряжения питания (в нашем случае это +5 вольт) может составлять плюс-минус 10 %, т.е. 5,5 В порт может выдержать, но если подать на вывод больше 6 В, то диоды не помогут, порт может закоротить, и микроконтроллер выйдет из строя частично или целиком, как повезёт. Поэтому крайне нежелательно экспериментировать с напряжением питания контроллера. Конденсатором обозначена паразитная емкость, которая присутствует на каждом выводе контроллера. Особой роли она не играет, но помнить о ней тем не менее стоит.

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

Немного об условных обозначениях. Все регистры микроконтроллера и относящиеся к ним отдельные биты в технической документации обозначаются в едином стиле: Pxn, где х представляет собой буквенное обозначение порта (PORTAPORTB и т. д.), а n – номер конкретного бита (PORTA1PORTB7). За каждый порт ввода-вывода отвечают три восьмибитных регистра.

PINх  этот регистр доступен только для чтения. В регистре PINx содержится информация о текущем логическом уровне на каждом из выводов порта, независимо от его настроек. Поэтому если мы хотим узнать, что у нас на входе – читаем соответствующий бит регистра PINx. Причем существует две границы: граница гарантированного нуля и граница гарантированной единицы – пороги, за которыми мы можем однозначно определить текущий логический уровень. Для напряжения питания в 5 В это 1,4 В и 1,8 В соответственно. При снижении напряжения от максимума до минимума бит в регистре PIN переключится с 1 на 0 только при снижении напряжения ниже 1,4 В, и наоборот, переключение бита с 0 на 1 осуществится только по достижении напряжения в 1,8 В, т.е. возникает гистерезис переключения с нуля на единицу, что исключает хаотичные переключения под действием помех и наводок, а также ошибочное считывание логического уровня между порогами переключения. При снижении напряжения питания эти пороги также снижаются, график зависимости порогов переключения от питающего напряжения можно найти в соответствующей документации на микроконтроллер.

DDRx  этот регистр определяет направление работы порта. Порт в каждый конкретный момент времени может быть либо входом, либо выходом (но для состояния битов регистра PIN это значения не имеет, оттуда можно всегда считать реальное текущее значение вывода). Если DDRxn=0 – вывод работает как ВХОДDDRxn=1 – вывод работает как ВЫХОД.

PORTx – данный регистр управляет состоянием вывода. Когда мы настраиваем его на вход, то от PORTx зависит тип входа (Hi-Z или PullUp, об этом чуть ниже). Когда вывод микроконтроллера настроен как выход, то значение соответствующего бита в регистре PORTx определяет состояние вывода. Если PORTxn=1, то на выходе логическая единица, если PORTxn=0, то на выходе логический ноль. Когда вывод настроен на вход и PORTxn=0, то он находится в режиме Hi-Z. Если PORTxn=1, то вывод переходит в режим PullUp (подтягивается к шине питания через резистор с сопротивление примерно 40 кОм).

После подачи питания все выводы микроконтроллера находятся по умолчанию в дефолтном состоянии – во всех регистрах записаны нули, соответственно порты ввода-вывода работают на вход и без подтяжки к питанию (так называемое третье состояние, Hi-Z). 

Рассмотрим каждый из режимов работы портов ввода-вывода поподробнее.

Режимы работы

Режим выходаЗдесь всё предельно просто – если нам необходимо выдать в порт логическую единицу, мы переводим порт в режим работы на выход (DDRxn=1) и записываем в регистр PORTxn единицу – при этом замыкается верхний переключатель, и на нужном выводе появляется напряжение питания, соответствующее логической единице. И наоборот, если нам на выходе нужен логический ноль, записываем в PORTxn ноль (всё предельно логично), и получаем на соответствующем выводе напряжение земли, что соответствует уровню логического нуля.

Для справки: состояние высокого импеданса или Z-состояние (в англоязычной литературе Hi-Z) – это такое состояние контакта логической схемы, при котором сопротивление между этим контактом и остальной схемой очень велико (принимается близким к бесконечности, т. е. эквивалентно обрыву цепи, в реальности сотни МОм). Физически данный режим реализуется закрытым полевым транзистором, работающим в ключевом режиме.

Вывод, переведённый в состояние Hi-Z, ведёт себя как оторванный от схемы. Внешние устройства, подключенные к этому выводу, могут изменять напряжение на нём по своему усмотрению (в некоторых пределах), не влияя на работу остальной схемы. Этот режим в большинстве микроконтроллерных устройств включен по умолчанию, после сброса по питанию. Все переключатели разомкнуты, а сопротивление порта очень велико (в идеале – стремится к бесконечности), т. е. электрически вывод как будто вообще никуда не подключен и ни на что не влияет. Но при этом он постоянно считывает свое состояние в регистр PINxn, и мы всегда можем узнать, что находится на входе – логическая единица или ноль.

PullUp – вход с подтяжкой к питанию. При DDRxn=0 и PORTxn=1 замыкается переключатель подтягивающего резистора, и к линии питания подключается резистор с сопротивлением примерно 40 кОм, что моментально переводит линию в состояние логической единицы. Цель подтяжки к питанию проста и понятна – она гарантирует независимость состояния нашего вывода от различных помех и наводок извне, без неё наш микроконтроллер может вести себя абсолютно непредсказуемо (особенно если устройство работает в промышленном цеху или неподалёку от телевизионной башни), хаотически переключаясь с одного логического уровня к другому. При этом, если на входе появляется логический ноль, резистор с достаточно большим сопротивлением не может удержать линию под напряжением питания и тут же отпускает её на землю, на входе при этом появляется уровень логического ноля, что можно с легкостью зафиксировать. Это обстоятельство можно использовать при подключении к микроконтроллеру различных кнопок и переключателей.

Соседние файлы в папке КР1