AVR. Учебный курс. Устройство и работа портов ввода-вывода
С внешним миром микроконтроллер общается через порты ввода вывода. Схема порта ввода вывода указана в даташите:
Но новичку там разобраться довольно сложно. Поэтому я ее несколько упростил:
Итак, что же представляет собой один вывод микроконтроллера. Вначале на входе стоит небольшая защита из диодов, она призвана защитить ввод микроконтроллера от превышения напряжения. Если напряжение будет выше питания, то верхний диод откроется и это напряжение будет стравлено на шину питания, где с ним будет уже бороться источник питания и его фильтры. Если на ввод попадет отрицательное (ниже нулевого уровня) напряжение, то оно будет нейтрализовано через нижний диод и погасится на землю. Впрочем, диоды там хилые и защита эта помогает только от микроскопических импульсов от помех. Если же ты по ошибке вкачаешь в ножку микроконтроллера вольт 6-7 при 5 вольтах питания, то никакой диод его не спасет.
Конденсатор, нарисованный пунктиром, это паразитная емкость вывода. Хоть она и крошечная, но присутствует. Обычно ее не учитывают, но она есть. Не забивай голову, просто знай это, как нибудь я тебе даже покажу как её можно применить 😉
Дальше идут ключи управления. Это я их нарисовал рубильниками, на самом деле там стоят полевые транзисторы, но особой сути это не меняет. А рубильники наглядней.
Каждый рубильник подчинен логическому условию которое я подписал на рисунке. Когда условие выполняется — ключ замыкается. PIN, PORT, DDR это регистры конфигурации порта.
Есть в каждом контроллере
Например, смотри в даташите на цоколевку микросхемы:
Видишь у каждой почти ножки есть обозначение Pxx. Например, PB4 где буква «B» означает имя порта, а цифра — номер бита в порту. За порт «B» отвечают три восьмиразрядных регистра PORTB, PINB, DDRB, а каждый бит в этом регистре отвечает за соответствующую ножку порта. За порт «А» таким же образом отвечают PORTA, DDRA, PINA.
PINх
Это регистр чтения. Из него можно только читать. В регистре PINx содержится информация о
При снижении напряжения питания разумеется эти пороги также снижаются, график зависимости порогов переключения от питающего напряжения можно найти в даташите.
DDRx
Это регистр направления порта. Порт в конкретный момент времени может быть либо входом либо выходом (но для состояния битов PIN это значения не имеет. Читать из PIN реальное значение можно всегда).
- DDRxy=0 — вывод работает как ВХОД.
- DDRxy=1 вывод работает на ВЫХОД.
PORTx
Режим управления состоянием вывода. Когда мы настраиваем вывод на вход, то от PORT зависит тип входа (Hi-Z или PullUp, об этом чуть ниже).
Когда ножка настроена на выход, то значение соответствующего бита в регистре PORTx определяет состояние вывода. Если PORTxy=1 то на выводе лог1, если PORTxy=0 то на выводе лог0.
Когда ножка настроена на вход, то если PORTxy=0, то вывод в режиме Hi-Z. Если PORTxy=1 то вывод в режиме PullUp с подтяжкой резистором в 100к до питания.
Есть еще бит PUD (PullUp Disable) в регистре SFIOR он запрещает включение подтяжки сразу для всех портов. По дефолту он равен 0. Честно говоря, я даже не знаю нафиг он нужен — ни разу не доводилось его применять и даже не представляю себе ситуацию когда бы мне надо было запретить использование подтяжки сразу для всех портов. Ну да ладно, инженерам
Общая картина работы порта показана на рисунке:
Теперь кратко о режимах:
- Режим выхода
Ну тут, думаю, все понятно — если нам надо выдать в порт 1 мы включаем порт на выход ( DDRxy=1) и записываем в PORTxy единицу — при этом замыкается верхний ключ и на выводе появляется напряжение близкое к питанию. А если надо ноль, то в PORTxy записываем 0 и открывается уже нижний вентиль, что дает на выводе около нуля вольт. - Вход Hi-Z — режим высокоимпендансного входа.
Этот режим включен по умолчанию. Все вентили разомкнуты, а сопротивление порта очень велико. В принципе, по сравнению с другими режимами, можно его считать бесконечностью. То есть электрически вывод как бы вообще никуда не подключен и ни на что не влияет. Но! При этом он постоянно считывает свое состояние в регистр PIN и мы всегда можем узнать что у нас на входе — единица или ноль. Этот режим хорош для прослушивания какой либо шины данных, т.к. он не оказывает на шину никакого влияния. А что будет если вход висит в воздухе? А в этом случае напряжение будет на нем скакать в зависимости от внешних наводок, электромагнитных помех и вообще от фазы луны и погоды на Марсе (идеальный способ нарубить случайных чисел!). Очень часто на порту в этом случае нестабильный синус 50Гц — наводка от сети 220В, а в регистре PIN будет меняться 0 и 1 с частотой около 50Гц - Вход PullUp — вход с подтяжкой.
При DDRxy=0 и PORTxy=1 замыкается ключ подтяжки и к линии подключается резистор в 100кОм, что моментально приводит неподключенную никуда линию в состояние лог1. Цель подтяжки очевидна — недопустить хаотичного изменения состояния на входе под действием наводок. Но если на входе появится логический ноль (замыкание линии на землю кнопкой или другим микроконтроллером/микросхемой), то слабый 100кОмный резистор не сможет удерживать напряжение на линии на уровне лог1 и на входе будет нуль.
Также почти каждая ножка имеет дополнительные функции. На распиновке они подписаны в скобках. Это могут быть выводы приемопередатчиков, разные последовательные интерфейсы, аналоговые входы, выходы ШИМ генераторов. Да чего там только нет. По умолчанию все эти функции отключены, а вывод управляется исключительно парой
Например, приемник USART. Стоит только выставить бит разрешения приема RXEN как вывод RxD, как бы он ни был настроен до этого, переходит в режим входа.
Совет:
С целью снижения энергопотребления и повышения надежности рекомендуется все неиспользованные пины включить в режим PullUp тогда их не будет дергать туда сюда помехой, а если на порт свалится грубая сила (например, монтажник отвертку уронит и коротнет на землю) то линия не выгорит.
Как запомнить режимы, чтобы не лазать каждый раз в справочник:
Чем зазубривать или писать напоминалки, лучше понять логику разработчиков, проектировавших эти настройки, и тогда все запомнится само.
Итак:
- Самый безопасный для МК и схемы, ни на что не влияющий режим это Hi-Z.
- Очевидно что этот режим и должен быть по дефолту.
- Значения большинства портов I/O при включении питания/сбросе = 0х00, PORT и DDR не исключение.
- Соответственно когда DDR=0 и PORT=0 это High-Z — самый безопасный режим, оптимальный при старте.
- Hi-Z это вход, значит при DDR=0 нога настроена на вход. Запомнили.
- Однако, если DDR=0 — вход, то что будет если PORT переключить в 1?
- Очевидно, что будет другой режим входа. Какой? Pullup, другого не дано! Логично? Логично. Запомнили.
- Раз дефолтный режим был входом и одновременно в регистрах нуль, то для того, чтобы настроить вывод на выход надо в DDR записать 1.
- Ну, а состояние выхода уже соответствует регистру PORT — высокий это 1, низкий это 0.
- Читаем же из регистра PIN.
Есть еще один способ, мнемонический:
1 похожа на стрелку. Стрелка выходящая из МК — выход. Значит DDR=1 это выход! 0 похож на гнездо, дырку — вход! Резистор подтяжки дает в висящем порту единичку, значит PORT в режиме Pullup должен быть в единичке!
Все просто! 🙂
Для детей в картинках и комиксах 🙂
Для большей ясности с режимами приведу образный пример:
Уровень напряжения на выводе словно планка, которая может двигаться вертикально вверх или вниз. В режиме Hi-Z мы можем на эту планку только смотреть, а двигать или как то на нее воздействовать мы не можем. Поэтому любая помеха может ее дрыгать как угодно, но зато если мы ее куда прицепим, то ее уровень будет зависеть только от другой цепи и ей мы не помешаем.
В режиме PullUp эту планку мы пружиной подтянули кверху. Слабые помехи не смогут больше ее дрыгать как угодно. С другой стороны шине она может помешать, но не факт что заблокирует ее работу. От шины зависит и ее силы. А еще мы можем отслеживать тупую внешнюю силу, вроде кнопки, которая может взять и придавить ее к земле. Тогда мы узнаем что кнопка нажата.
В режиме OUT у нас планка прибита гвоздями к земле или прижата домкратом к питанию. Внешняя сила может ее пересилить только сломав домкрат или сломается сама. Тупая внешняя сила просто разрушает наш домкрат или вырывает гвозди из пола с мясом. В любом случае — девайс в помойку.
Устройство и работа портов ввода-вывода микроконтроллеров AVR. Часть 1 / Habr
Работа портов ввода/выводаИзучив данный материал, в котором все очень детально и подробно описано с большим количеством примеров, вы сможете легко овладеть и программировать порты ввода/вывода микроконтроллеров AVR.
Пример будем рассматривать на микроконтроллере ATMega8.
Программу писать будем в Atmel Studio 6.0.
Эмулировать схему будем в Proteus 7 Professional.
С внешним миром микроконтроллер общается через порты ввода вывода. Схема порта ввода вывода указана в даташите:
Но новичку разобраться довольно со схемой довольно сложно. Поэтому схему упростим:
Pxn – имя ножки порта микроконтроллера, где x буква порта (A, B, C или D), n номер разряда порта (7… 0).
Cpin — паразитная емкость порта.
VCC — напряжение питания.
Rpu — отключаемый нагрузочный верхний резистор (pull-up).
PORTxn — бит n регистра PORTx.
PINxn — бит n регистра PINx.
DDRxn — бит n регистра DDRx.
Рассмотрим, что же представляет собой вывод микроконтроллера. На входе микроконтроллера стоит небольшая защита из двух диодов (см.1), она предназначенная для защиты ввода микроконтроллера от кратковременных импульсов напряжения, превышающих напряжение питания. Если напряжение будет выше питания, то верхний диод откроется и это напряжение будет стравлено на шину питания, где с ним будет уже бороться источник питания и его фильтры. Если на ввод попадет отрицательное (ниже нулевого уровня) напряжение, то оно будет нейтрализовано через нижний диод и погасится на землю. Впрочем, диоды там хилые и защита эта помогает только от микроскопических импульсов и помех. Если же на ножку микроконтроллера подать вольт 6-7 при 5 вольтах питания, то внутренние диоды его не спасут.
Конденсатор (см.2) — это паразитная емкость вывода. Хоть она и крошечная, но присутствует. Обычно ее не учитывают, но она есть. Не забивай голову, просто знай это.
Дальше идут ключи управления (см.3,4). Каждый ключ подчинен логическому условию, которые нарисованы на рисунке. Когда условие выполняется — ключ замыкается.
Каждый порт микроконтроллера AVR (обычно имеют имена A, B и иногда C или даже D) имеет 8 разрядов, каждый из которых привязан к определенной ножке корпуса. Каждый порт имеет три специальных регистра DDRx, PORTx и PINx (где x соответствует букве порта A, B, C или D). Назначение регистров:
DDRx – Настройка разрядов порта x на вход или выход.
PORTx – Управление состоянием выходов порта x (если соответствующий разряд настроен как выход), или подключением внутреннего pull-up резистора (если соответствующий разряд настроен как вход).
PINx –Чтение логических уровней разрядов порта x.
PINхn – это регистр чтения. Из него можно только читать. В регистре PINxn содержится информация о реальном текущем логическом уровне на выводах порта. Вне зависимости от настроек порта. Так что если хотим узнать что у нас на входе — читаем соответствующий бит регистра PINxn. Причем существует две границы: граница гарантированного нуля и граница гарантированной единицы — пороги за которыми мы можем однозначно четко определить текущий логический уровень. Для пятивольтового питания это 1.4 и 1.8 вольт соответственно. То есть при снижении напряжения от максимума до минимума бит в регистре PINx переключится с 1 на 0 только при снижении напряжение ниже 1.4 вольт, а вот когда напряжение нарастает от минимума до максимума переключение бита с 0 на 1 будет только по достижении напряжения в 1.8 вольта. То есть возникает гистерезис переключения с 0 на 1, что исключает хаотичные переключения под действием помех и наводок, а также исключает ошибочное считывание логического уровня между порогами переключения.
При снижении напряжения питания разумеется эти пороги также снижаются.
DDRxn – это регистр направления порта. Порт в конкретный момент времени может быть либо входом либо выходом (но для состояния битов PINxn это значения не имеет. Читать из PINxn реальное значение можно всегда).
DDRxy = 0 – вывод работает как ВХОД.
DDRxy = 1 – вывод работает на ВЫХОД.
PORTxn – режим управления состоянием вывода. Когда мы настраиваем вывод на вход, то от PORTх зависит тип входа (Hi-Z или PullUp, об этом чуть ниже).
Когда ножка настроена на выход, то значение соответствующего бита в регистре PORTx определяет состояние вывода. Если PORTxn=1 то на выводе лог.1, если PORTxn=0 то на выводе лог.0.
Когда ножка настроена на вход, то если PORTxn=0, то вывод в режиме Hi-Z. Если PORTxn=1 то вывод в режиме PullUpс подтяжкой резистором в 100к до питания.
Таблица. Конфигурация выводов портов.
DDRxn PORTxn I/O Comment
0 0 I (Input) Вход Высокоимпендансный вход. (Не рекомендую использовать, так как могут наводится наводки от питания)
0 1 I (Input) Вход Подтянуто внутренне сопротивление.
1 0 O (Output) Выход На выходе низкий уровень.
1 1 O (Output) Выход На выходе высокий уровень.
Общая картина работы порта показана на рисунках:
Рис. DDRxn=0 PORTxn=0 – Режим: HI-Z – высоко импендансный вход.
Рис. DDRxn=0 PORTxn=1 – Режим: PullUp – вход с подтяжкой до лог.1.
Рис. DDRxn=1 PORTxn=0 – Режим: Выход – на выходе лог.0. (почти GND)
Рис. DDRxn=1 PORTxn=1 – Режим: Выход – на выходе лог.1. (почти VCC)
Вход Hi-Z — режим высокоимпендансного входа.
Этот режим включен по умолчанию. Все ключи разомкнуты, а сопротивление порта очень велико. В принципе, по сравнению с другими режимами, можно его считать бесконечностью. То есть электрически вывод как бы вообще никуда не подключен и ни на что не влияет. Но! При этом он постоянно считывает свое состояние в регистр PINn и мы всегда можем узнать что у нас на входе — единица или ноль. Этот режим хорош для прослушивания какой либо шины данных, т.к. он не оказывает на шину никакого влияния. А что будет если вход висит в воздухе? А в этом случае напряжение будет на нем скакать в зависимости от внешних наводок, электромагнитных помех и вообще от фазы луны и погоды на Марсе (идеальный способ нарубить случайных чисел!). Очень часто на порту в этом случае нестабильный синус 50Гц — наводка от сети 220В, а в регистре PINn будет меняться 0 и 1 с частотой около 50Гц
Вход PullUp — вход с подтяжкой.
При DDRxn=0 и PORTxn=1 замыкается ключ подтяжки и к линии подключается резистор в 100кОм, что моментально приводит не подключенную никуда линию в состояние лог.1. Цель подтяжки очевидна — не допустить хаотичного изменения состояния на входе под действием наводок. Но если на входе появится логический ноль (замыкание линии на землю кнопкой или другим микроконтроллером/микросхемой), то слабый 100кОмный резистор не сможет удерживать напряжение на линии на уровне лог.1 и на входе будет лог.0.
Режим выхода.
Тут, думаю, все понятно — если нам надо выдать в порт лог.1, мы включаем порт на выход (DDRxn=1) и выдаем лог.1 (PORTxn=1) — при этом замыкается верхний ключ и на выводе появляется напряжение, близкое к питанию. А если надо лог.0, то включаем порт на выход (DDRxn=1) и выдаем лог.0 (PORTxn=1) — при этом открывается уже нижний вентиль, что дает на выводе около нуля вольт.
ATMega8 / Порты ввода/вывода
С внешним миром микроконтроллер общается через порты ввода вывода.
От превышенных и отрицательных значений напряжения порт защищён внутренними диодами. Эти диоды слабы и защищают порт от перепадов всего в 1-2 В. Большие перепады способны привести к выгоранию порта.
Нв рисунке показана цоколёвка микроконтроллера Atmega8 в корпусе PDIP28. Порты ввода/вывода обозначены следующим образом PXY, где X — имя порта, а Y — номер бита в этом порту (например вывод PD0 является нулевым битом порта D).
За настройку каждого порта отвечают 3 регистра конфигурации порта: PORTx, DDRx, PINx (x-имя порта)
DDRx — регистр направления порта.
Если DDRx.y=0, то бит y порта x работает в режиме входа, если DDRx.y=1, то бит y порта x работает в режиме выхода.
PORTx — управления состоянием вывода.
Когда ножка настроена на выход (DDRx.y=1) , то значение соответствующего бита y в регистре PORTx определяет состояние вывода. Если PORTx.y=1, то на выводе установится напряжение, соответсвующее логической единице; если PORTx.y=0, то на выводе установится логический ноль.
Когда ножка настроена на вход (DDRx.y=0), то в зависимости от значения PORTx.y возможны 2 варианта состояния:
1) Вход PullUp — вход с подтяжкой (PORTx.y=1)
При такой конфигурации замыкается ключ подтяжки и к линии подключается резистор 100кОм, что приводит к возникновению на выводе логической 1. Если этот вывод замкнуть на землю (например кнопкой), то слабый резистор не сможет удерживать напряжение на линии на уровне логической единицы и на выводе установится ноль.
Для неиспользуемых выводов рекоммендуется устанавливать этот режим (это приведёт к снижению энергопотребления и повышению надёжности)
P.S. бит PUD в регистре SFIOR запрещает включение подтяжки сразу для всех портов. По умолчанию он равен 0, то есть подтяжка разрешена
2) Вход Hi-Z — режим высокоимпендансного входа (PORTx.y=0).
При такой конфигурации вывод вообще никуда не подключен и ни на что не влияет. Но при этом он постоянно отправляет свое состояние в регистр PIN и всегда можно узнать что на этом входе — единица или ноль. Этот режим подходит для прослушивания какой либо шины данных, так как он не оказывает на неё никакого влияния. Если вывод никуда не подключён и просто «висит в воздухе», то напряжение на нём будет колебаться от действия внешних наводок, электромагнитных полей и т.д
Этот режим стоит по умолчанию.
PINх
Это регистр чтения. Из него можно только читать. В регистре PINx содержится информация о реальном текущем логическом уровне на выводах порта. Вне зависимости от настроек порта
Если у вас есть какие-то замечания по этому документу или что-то осталось непонятно, то вы можете оставить свой отзыв или вопрос
РадиоКот :: Настройка портов ввода-вывода
РадиоКот >Обучалка >Микроконтроллеры и ПЛИС >Микроконтроллеры AVR — пишем, компилируем, прошиваем… >Настройка портов ввода-вывода
У нас уже есть папка tutorial c файлом code.asm и двумя настроенными батниками. Можете писать прямо в этот файл, а можете скопировать папочку tutorial под другим именем. Нопример, у меня папка с этим проектом называется runfire (типа, бегущий огонек :)).
Структура новой папки должна остаться такой же, как у tutorial.
Открываем code.asm (правой кнопкой по файлу -> Правка).
Стираем все кроме самой первой строки, в которой написано .include тра-ля-ля…
Я помнится уже где-то говорил, что любая программа начинается с настройки портов. Я от своих слов не отказываюсь. Кстати говоря, кроме этого, в начале программы обычно прописывается указатель СТЕКа. Но мы еще не знаем, что такое стек, и он нам сегодня не понадобится — так шо, иди он лесом.
Но самым первым делом, мы должны объявить о начале текста программы и назначить адрес ПЗУ для первой команды. Поэтому мы пишем:
.include «d:avravrasmappnotes2313def.inc»
.cseg
.org 0
CSEG — обозначает начало программного сегмента
ORG — задает начальный адрес. В данном случае он = 0.
Кроме программного сегмента, бывает сегмент данных — DSEG.
В нем инициализируется оперативная память. Но об этом мы будем говорить в дальнейшем.
Итак, как вы помните, в нашем контроллере 16 РОН (регистров общего назначения): R16…R31. С любым из них мы можем сделать все что угодно. Просто полная анархия! Однако, я бы не рекомендовал злоупотреблять количеством используемых регистров. Это очень усложняет процесс написания программы. Я обычно использую 4 или 5 регистров - только самое необходимое в данный момент. Остальное — в оперативке (ОЗУ).
Чтобы не запутаться, я по-своему обзываю эти регистры: Temp, Temp1, …, Temp4. И вам того же желаю. Делается это так: до начала программного сегмента необходимо прописать примерно следующее:
.def Temp=R16
.def Temp1=R17
и т.д.
Напишем:
.include «d:avravrasmappnotes2313def.inc»
.def Temp=R16
.def Temp1=R17
.def Temp2=R18
.def Temp3=R19
.def Temp4=R20
.cseg
.org 0
Все, теперь нам станет проще…
Ах да! Мы ж настраиваем порты… 🙂 Ну ладно — настраиваем!
Я уже говорил, что каждый порт ввода-вывода (ПВВ) состоит из скольки-то каналов.
Каждый канал может быть настроен на вход или на выход.
В нашем контроллере два порта:
PortB — 8 каналов (к нему подключены светодиоды)
PortD — 7 каналов (к нему пока ничего не подключено)
Порты устроены очень хитроумно. Для работы с любым портом используется три регистра:
PortX
PinX
DDRX
(где X — буква порта, например PortB, PinD и т.д.)
PortX содержит информацию, предназначенную для вывода.
PinX содержит вводимую информацию
DDRX содержит информацию о том, какой канал настроен на ввод, какой — на вывод.
То есть, DDRX определяет, грубо говоря, какая ножка микросхемы будет подключена к PinX, какая — к PortX:
0 — ввод
1 — вывод
Соответственно, если, скажем, PX3 настроен на ввод, то бесполезно писать что-либо в 3-й бит PortX,
поскольку оно не будет выведено.
И наоборот, если например, PX5 настроен на вывод, то прочитав 5-й бит PinD, мы всегда обнаружим 0.
Порты — дело тонкое…
По умолчанию, все каналы порта настроены на ввод.
Нам же надо, чтобы порт B был целиком настроен на вывод. Значит, все биты DDRB должны равняться 1. То есть, в DDRB надо записать «11111111».
Мы не можем напрямую записать константу в регистр, не являющийся РОН. Но мы можем вывести значение из РОНа в этот регистр.
Знакомимся с первыми операторами:
out — оператор вывода данных из РОН в другие регистры
Пример:
out DDRB,Temp1
ldi — оператор присвоения константы РОН
Примеры:
ldi Temp1,14 — присвоение десятеричной константы (14)
ldi Temp1,0x0E — присвоение шестнадцатеричной константы (0E)
ldi Temp1,0b00001110 — присвоение двоичной константы (00001110)
Итак, операция настройки портов происходит в два шага:
1.Присваиваем РОН константу
2.Выводим данные из РОН в DDRx
.include «d:avravrasmappnotes2313def.inc»
.def Temp=R16
.def Temp1=R17
.def Temp2=R18
.def Temp3=R19
.def Temp4=R20
.cseg
.org 0
ldi Temp, 0b11111111 ;присвоение константы
out DDRB,Temp ;вывод
Все! Порты настроены, я вас поздравляю.
А шуму то было! :)
Теперь можно вывести какое-нибудь число и проверить как работает наша схема.
Выведем двоичное число 11010010
.cseg
.org 0
ldi Temp, 0b11111111 ;присвоение константы
out DDRB,Temp ;вывод
;выводим число 11010010 на светодиоды
ldi Temp,0b11010010 ;присвоение константы
out PortB,Temp ;вывод на индикацию
Компилируем. Шьем. Смотрим. Должны загореться соответствующие светодиоды.
Загорелись?
Кричим УРА, идем дальше… 🙂
<<—Вспомним пройденное—-Поехали дальше—>>
Как вам эта статья? | Заработало ли это устройство у вас? |
Устройство и работа портов ввода-вывода микроконтроллеров AVR. Часть 2 / Habr
Подключение светодиода к линии порта ввода/выводаИзучив данный материал, в котором все очень детально и подробно описано с большим количеством примеров, вы сможете легко овладеть и программировать порты ввода/вывода микроконтроллеров AVR.
Пример будем рассматривать на микроконтроллере ATMega8.
Программу писать будем в Atmel Studio 6.0.
Эмулировать схему будем в Proteus 7 Professional.
Первым примером в изучении микроконтроллеров является подключение и управление светодиодом, это самый простой и наглядный пример. Этот пример стал классическим при изучении микроконтроллеров, как программа «Hello World!» при изучении прочих языков программирования.
Максимальный ток, который способен пропустить каждый порт ввода/вывода составляет 40 mA.
Максимальный ток, который способна пропускать каждая линия порта ввода/вывода составляет 20 mA.
Прежде чем подключать нагрузку, в том числе и светодиод к линиям порта ввода/вывода нужно знать, что можно спалить микроконтроллер превысив допустимую нагрузку на линию порта ввода/вывода.
Что бы ограничить ток, который протекает через линии порта ввода/вывода микроконтроллера нужно рассчитать и подключить резистор.
Рис: Рапиновка светодиода.
Рис: Подключение светодиода анодом к микроконтроллеру.
Рис: Подключение светодиода катодом к микроконтроллеру.
Сопротивление токоограничивающего резистора подключаемого к линиям портов ввода/вывода при подключении светодиода рассчитывается по формуле:
где:
— Vs — напряжение источника питания;
— Vsp — падение напряжения на линии порта ввода/вывода;
— Vd — прямое падения напряжения на светодиоде;
— Id — прямой ток на светодиоде;
— Кn — коэффициент надежности роботы светодиода;
Пример:
— напряжение источника питания – 5В;
— прямое падения напряжения на светодиоде – 2В (Берётся с datasheet на светодиод);
— прямой ток на светодиоде – 10мА (Берётся с datasheet на светодиод);
— коэффициент надежности роботы светодиода – 75% (Берётся с datasheet на светодиод);
— падение напряжения на линии порта ввода/вывода – 0,5В (Берётся с datasheet на микроконтроллер: Vol(output low voltage) – если ток втекает, и Voh (output high voltage) – если ток вытекает);
Таким образом номинал резистора R = 166,66 Om, подбирается ближайшее большее значение сопротивления.
Если не известно прямое напряжение светодиода, сопротивление можно рассчитать по закону Ома.
где:
— U — напряжение, приложенное к участку цепи;
— I — номинальный ток линии порта ввода/вывода.
Пример:
— напряжение, приложенное к участку цепи – 4,5В;
— номинальный ток линии порта ввода/вывода – 20мА.
Определив номинал резистора R, необходимо рассчитать мощность P, измеряемая в ваттах, которая будет выделяться в резисторе, в виде тепла при протекании тока в цепи.
где:
— U – напряжение, приложенное к участку цепи;
— I — номинальный ток линии порта ввода/вывода.
Пример:
— напряжение, приложенное к участку цепи – 4,5В;
— прямой ток на светодиоде – 20мА.
Рассчитав выделяемую мощность на резисторе, выбираем ближайшее большее значение мощности резистора. Если рассеиваемой мощности резистора будет недостаточной, то он может выйти из строя.
— подключения маломощного светодиода анодом к линии порта ввода/вывода:
// Подключаем внешние библиотеки
#include <AVR/io.h>
#include <stdint.h>
// Основная программа
int main(void)
{
// Настраиваем порты ввода/вывода
DDRC = 0b11111111; //Настраиваем все разрады порта С на режим "Выход"
PORTC = 0b11111111; //Устанавливаем все разряды порта C в лог.«1» (Навыходе порта напряжение равное Vcc)
// Вечный цикл
while (1)
{
}
}
— подключения маломощного светодиода катодом к линии порта ввода/вывода:
// Подключаем внешние библиотеки
#include <AVR/io.h>
#include <stdint.h>
// Основная программа
int main(void)
{
// Настраиваем порты ввода/вывода
DDRC = 0b11111111; //Настраиваем все разряды порта С на режим "Выход"
PORTC = 0b00000000; //Устанавливаем все разряды порта C в лог.«0» (На выходе порта напряжение равное GND)
// Вечный цикл
while (1)
{
}
}
— подключения маломощного светодиода анодом и катодом к линии порта ввода/вывода:
// Подключаем внешние библиотеки
#include <AVR/io.h>
#include <stdint.h>
// Основная программа
int main(void)
{
// Настраиваем порты ввода/вывода
DDRD = 0b11111111; //Настраиваем все разряды порта D на режим "Выход"
PORTD = 0b11111111; //Устанавливаем все разряды порта D в лог.«1» (На выходе порта напряжение равное Vcc)
DDRC = 0b11111111; //Настраиваем все разряды порта C на режим "Выход"
PORTC = 0b00000000; //Устанавливаем все разряды порта C в лог.«0» (На выходе порта напряжение равное GND)
// Вечный цикл
while (1)
{
}
}
Порты ввода/вывода микроконтроллеров AVR, их конфигурирование
В данной статье я хочу подробно остановится на портах ввода-вывода. Как основной элемент для получения информации микроконтроллером всегда используется порт. Порт в микроконтроллере может быть настроен на вход:
DDRB = 0x00; или на выход DDRB = 0xFF;
Синтаксис Си подразумевает конфигурацию портов именно таким, так сказать оператором DDR(я так подозреваю аббревиатура от дословного перевода Data Direction, может и нет :-))
То есть для того чтобы сконфигурировать весь порт B на вход или выход достаточно указать следующий код:
...
int main (void)
{
DDRB = 0x00;
...
}
...
«…» — подразумевает какой-то код.
код 0x00 — соответствует шестнадцатиричному коду «00000000», для тех кто впервые сталкивается с микроконтроллерами будет полезно знать, что есть несколько вариантов записи числа, то есть, несколько вариантов в двоичной системе и в шестнадцатиричной. Для обозначения двоичного ввода числа в синтасисе Си предусмотрено использование опаретора:
0byyyyyyyy — где «y» 8 бит порта микроконтроллера. Для шестнадцатиричного вида записи числа в синтаксисе Си предусмотрен следующий оператор:
0xyy — где «y» 4 тетрады порта микроконтроллера. Для справки: тетрада — 4 бита, то есть «0000» к примеру, байт — 8 бит или соответственно 2 тетрады, то есть «00000000».
Для конвертирования кода из двоичной системы исчисления в шестнадцатиричную, из шестнадцатиричной в десятичную рекомендую использовать программу BinHexDec Converter.
Порт не обязательно настраивать полностью на вход или на выход. Возможны варианты частичного конфигурирования, такие как:
DDRB = 0x0F; (равносильно 0b00001111 — 4 бита на вход, 4 бита на выход)
Для удобства я иногда записываю побитово конфигурацию порта:
DDRD &= ~_BV(PD2);//вход
DDRD &= ~_BV(PD3);//...
DDRD &= ~_BV(PD4);//...
DDRD &= ~_BV(PD5);//вход
DDRB |= _BV(PB0);//выход
DDRB |= _BV(PB1);//...
DDRB |= _BV(PB2);//...
DDRB |= _BV(PB3);//выход
На рисунке пример работы с BinHexDec Converter.
Порты ввода/вывода у каждого контроллера по своему мощны, некоторые могут выдержать нагрузку светодиода напрямую подключенного к выводу порта(например AtTiny2313 а некоторые не могут ATmega8), в результате чего порт просто выгорает. Поэтом, очень советую на, те же светодиоды, ставить ограничивающий ток резистор, номиналом от 750 Ом до 1кОм. И будете уверены что ни порт, ни светодиод не сгорит.
Честно говоря существует еще два вида записи конфигурации порта на ввод/вывод, но они уже устарели, да и они менее наглядные чем пред идущие с использованием операторов «0x..» и «0b……..».
DDRB |= 1<<2; - сконфигурировать бит номер 2 порта B на выход то же самое означает запись:
DDRB |= _BV(PB2);
Порты ввода-вывода микроконтроллера
Устройство микроконтроллера:
– назначение, устройство и программирование портов ввода-вывода микроконтроллера
Доброго дня уважаемые радиолюбители!
Приветствую вас на сайте “Радиолюбитель“
Ну вот, уважаемые радиолюбители, сегодня этой статьей я закончу загрузку ваших (и своих) мозгов чистой теорией. Дальше будет легче и приятней: теорию совместим с практикой.
Ну а сегодня мы рассмотрим очень важный и интересный вопрос – порты ввода/вывода микроконтроллера.
Порты ввода/вывода микроконтроллера AVR
Порты ввода/вывода (далее я буду писать сокращенно – ПВВ) – предназначены для общения микроконтроллера с внешними устройствами. С их помощью мы передаем информацию другим устройствам и принимаем информацию от них. В зависимости от типа, микроконтроллер может иметь на своем борту от одного до семи ПВВ. Каждому порту ввода/вывода присвоено буквенное обозначение – A, B, C, D, E, F, G. Все порты в микроконтроллере равнозначные, восьмиразрядные (содержат восемь линий, они же выводы, они же разряды, они же биты) и двунаправленные – могут как передавать, так и принимать информацию. ПВВ в микроконтроллере обслуживают все его устройства, в том числе и периферийные. Поэтому, в зависимости от того какое устройство будет работать с портом он может принимать и передавать или цифровую информацию, или аналоговую.
Вообще, порты классифицируются по типу сигнала:
– цифровые порты – которые работают с цифровыми сигналами – логическими “нулями” и логическими “единицами”
— аналоговые порты – которые работают с аналоговыми сигналами – использующими плавно весь диапазон входных напряжений от нуля вольт до напряжения питания МК
— смешанные порты – они и используются в наших МК, могут оперативно переключаться с режима “цифровой порт” в режим “аналоговый порт”, и обратно.
В технической литературе и схемам ПВВ обозначаются следующим образом:
– “Р” – первая буква, означающая слово “порт”
– “А” (В, С, D, E, F, G) – вторая буква, обозначающая конкретный порт
– “0” (1, 2, 3, 4, 5, 6, 7) – третий символ – цифра, обозначающая конкретный вывод (регистр, бит) порта.
К примеру: “порт А” – РА, “пятый разряд порта А” – РА5.
Если в МК есть несколько портов, то не обязательно их имена могут идти по порядку – A, B, C. Может быть и так – В, С, D. Поэтому пугаться и судорожно искать где же порт А не надо.
Кроме того, хотя порты восьмиразрядные, выводов у порта не обязательно должно быть 8, может быть и меньше, к примеру 3 – PA0, PA1, PA2. В таком случае порт называют неполным, или урезанным.
Давайте посмотрим на конкретный МК – ATmega8:
Как видите, в этом МК порта с именем “А” нет (отсутствует как класс ;). Порт РВ и порт PD – полные, имеют по восемь выводов. А порт С – неполный (ущемленный, нет места в корпусе МК для его вывода), в нем отсутствует восьмой разряд (реально, внутри корпуса МК, он есть, но работать мы с ним не можем).
Для управления портами в их электрической схеме имеется два переключателя, которыми мы можем “щелкать” программно, используя специальные регистры ввода/вывода. Такие переключатели имеются для каждого вывода, что означает возможность управлять любым выводом порта. К примеру, один вывод порта можно настроить на ввод информации, три разряда этого же порта на вывод, а оставшиеся вообще не настраивать, оставить их в “Z- состоянии” .
Давайте разберемся с этим вопросом конкретней, на примере вот этой схемы:
Обратите внимание на два переключателя – Sin и Sout, и сопротивление Rup.
С помощью Sin осуществляется переключение вывода порта или для работы на вход, или для работы на выход. Управляется этот переключатель с помощью регистра ввода/вывода DDRx. У каждого порта свой регистр. Каждый разряд регистра управляет соответствующим разрядом порта (нулевой – нулевым, первый – первым и т.д.). Символ “x” в названии порта заменяется соответствующим именем порта: для порта А – DDRA, для порта С – DDRC. При записи в разряд регистра DDRx “единицы”, соответствующий ему разряд порта переключается на вывод информации, а при записи “нуля” – на ввод информации. Просмотрите рисунки ниже, и вы поймете как работать с регистром DDRx.
1. Переключение всех выводов порта на вывод информации:
2. Переключение всех выводов порта на ввод информации:
3. Переключение части выводов порта на ввод, и части на вывод информации:
В “классическом” Ассемблере настройка выводов портов на ввод и вывод информации выглядит так (просто пример 3-го рисунка):
Idi R20, 0b01100010 — этой командой мы записываем в РОН R20 двоичное число 01100010, которым определяем – какой вывод порта будет работать на вывод (1), а какой на ввод (0) информации. В данном случаем разряды порта В 1,5,6 – настраиваются на вывод информации, а 0,2,3,4,7 – на ввод информации
Out DDRB, R20 — этой командой мы переносим содержимое РОН R20 в регистр ввода/вывода порта В.
В Algorithm Builder запись немного отличается:
#b01100010 –> DDRB
Дело в том, что Algorithm Builder несколько более смещен к языкам высокого уровня, поэтому мы просто прописываем “свое желание” одной строчкой, но а при компилировании (переводе в машинные коды), программа сама преобразует эту строчку как и в “классической” записи.
Второй переключатель – Sout. Этот переключатель имеет двойное назначение, в зависимости от настройки разрядов порта на вывод или ввод информации.
Если разряд порта настроен на вывод информации, то с его помощью мы устанавливаем на выходе разряда или логическую “1”, или логический “0”.
Если разряд порта настроен на ввод информации, то с его помощью подключается так называемый “подтягивающий резистор” – Rup, или “внутренний нагрузочный резистор”. Благодаря этому резистору упрощается подключение внешних кнопок и переключателей, т.к. обычно контакты требуют внешнего резистора.
Как и переключатель Sin, Sout – это регистр ввода/вывода под названием PORTx, где “х” – буквенное обозначение порта (к примеру для порта D регистр будет иметь вид – PORTD).
В семейств МК Mega имеется дополнительный переключатель – PUD, — 2-й разряд регистра ввода/вывода SFIOR (он называется “Регистр специальных функций”). С помощью этого PUD осуществляется общее управление подтягивающими резисторами:
— при записи в этот разряд “1” – все подтягивающие резисторы для всех портов отключаются;
– при записи в этот разряд “0” – состояние подтягивающих резисторов определяется регистром PORTx.
Зачем нужно общее отключение резисторов, да и этот PUD заодно, мы сегодня рассматривать не будем.
В режиме работы разрядов порта на вывод, задача регистра PORTx очень проста – то, что мы в него запишем, то и будет на выходе. Запишем одни “нули” – на выходах буду логические нули, запишем “единицы” – на выходе буду логические “единицы”.
Например:
Настраиваем порт В на вывод информации:
Idi R20, 0b11111111
Out DDRB, R20
Выводим в разряды 0-3 логический ноль, а в разряды 4-7 логическую единицу:
Idi R20, 0b11110000
Out PORTB, R20
В Algorithm Builder:
#b11111111 –> DDRB
#b11110000 –> PORTB
Надеюсь, что пока все понятно.
Вышеприведенные примеры позволяют настроить весь порт сразу, и вывести нужные значения на все выводы порта за один раз.
Если необходимо настроить только один разряд порта на ввод или вывод, а также вывести “0” или “1” только в один разряд порта, не затрагивая состояние и содержание других разрядов этого порта, существуют следующие команды:
SBI A,b – установить разряд регистра
CBI A,b – сбросить разряд регистра
При этом: “А” – номер регистра, “b” – разряд этого регистра.
Данные команды работают не только с РВВ DDRx и PORTx, но и с теми, которые имеют номера от 0 до 31.
Пример:
— “классический” Ассемблер:
Настраиваем порт В на вывод информации:
Idi R20, 0b11111111
Out DDRB, R20
Нам нужно переключить 1-й разряд порта на ввод информации:
CBI $17, 1 (где $17 – номер РВВ порта В – DDRB, 1 – разряд порта В)
— Algorithm Builder:
#b11111111 –> DDRB
0 –> PORTB.1
У портов ввода/вывода есть еще один регистр: PINx, регистр выводов порта (“х” – буквенное обозначение порта)
Этот регистр предназначен для считывания информации с вывода порта, независимо в какой он конфигурации – на ввод, или на вывод. Записать в этот регистр мы ничего не можем, он предназначен только для считывания.
Состояние выводов портов в зависимости от их конфигурации:
* PUD нет в МК Tiny и в МК модели ATMega161
Подавляющее большинство контактов портов имеют дополнительные функции и используются периферийными устройствами. При этом может быть две ситуации: в одном случае мы должны самостоятельно задавать конфигурацию вывода, а в другом случае – вывод конфигурируется самостоятельно, при включении соответствующего периферийного устройства.
Некоторые рекомендации по использованию портов ввода/вывода:
При сбросе или включении питания микроконтроллера все выводы всех портов (за очень-очень редким случаем) переводятся в высокоимпедансное состояние – “Z- состояние”. Этот момент следует учитывать в реальных схемах. Если нагрузкой выхода служит транзисторный ключ, то для того, чтобы его база (затвор полевого транзистора) не болтались в воздухе, необходимо ставить дополнительные внешние резисторы сопротивлением 10-100 кОм.
Если вы не используете выводы порта, то не следует их оставлять “парящими в воздухе” – из-за этого повышается потребляемый ток МК (почему – не так важно, но это так). Все неиспользуемые выходы в схеме рекомендуется нагружать на сопротивления 10-100 кОм (можно использовать и внутренние подтягивающие резисторы), или переводить выводы в режим цифровых выходов.
При использовании аналогового компаратора, следует следить, чтобы подтягивающие резисторы были отключены – иначе пострадают показания абсолютных уровней сигнала.
Подтягивающие резисторы не совсем “резисторы” – их роль выполняют полевые транзисторы, которые имеют большой технологический разброс – номинал подтягивающего сопротивления может колебаться в пределах 30-100 кОм. При мощных помехах, да и в других “критических случаях” рекомендуется (хотя такой рекомендации и нет в даташитах) подключать дополнительные подтягивающие резисторы номиналом 2-5 кОм. Такие резисторы следует устанавливать на вывод “Reset”, на выводы внешних прерываний, если они не используются. Также следует устанавливать резисторы при работе выводов МК на общую шину (I2C, или просто при подсоединении выхода МК к выходу другого устройства с открытым коллектором, при подключении к двухвыводным кнопкам). Сопротивление встроенного резистора в таких случаях слишком велико, чтобы отсеивать электромагнитные помехи.
Создаем программу «мигалку»
Итак, уважаемые читатели, мы уже ознакомились со структурой микроконтроллера, разобрали простые команды ассемблера. Теперь можно приступить к написанию простой программы.
Для этого нам понадобится среда AVRStudio (о которой упоминалось раньше) и середа для симуляции микроконтроллера – Proteus 7. В сети маса примеров по установке этих программ, так что на этом останавливаться не будем.
Первая наша программа будет состоять из:
Подключения файла директив, инициализации МК;
Настройки портов ввода-вывода МК;
Простейшего цикла переключения портов из логического состояния «0» в «1»;
Подпрограммы простой задержки с использованием регистров общего назначения.
При штатной установке программы AVR Studio, файлы с директивами микроконтроллера располагается по следующему адресу C:\Program Files\Atmel\AVR Tools\AvrAssembler\Appnotes.
В нашем примере будем использовать микроконтроллер Attiny2313. Его inc файл имеет название 2313def.
Для начала откроем программу AVR Studio 4 и создадим проект.
Нажимаем на клавишу создания нового проекта.
Далее откроется окно, где необходимо указать название файла, выбрать язык программирования и указать путь к сохранению проекта.
В последнем окне необходимо выбрать симулятор и тип нашего МК. Далее, нажимаем на клавишу «finish» и можно будет увидеть, как откроется новое окно нашего проекта.
Наш проект уже создан и его можно наполнять программным кодом. Как говорилось раньше, первым делом нужно подключить файл директив данного микроконтроллера. Если возникнет необходимость проводить симуляцию проекта в среде AVR Studio 4, то желательно указать еще и имя нашего МК. Для этого нужно прописать следующую строку «.device ATtiny2313».
Для подключения inc файла, нужно прописать .include “tn2313def.inc”. Тем самым мы разрешим компилятору использовать файл директив данного МК.
Этот файл существенно упрощает задачу программирования, так как мы можем придерживаться определенных стандартов и обращаться к разным адресам МК словами, а не цифрами.
К примеру, на следующем рисунке обозначена строка значения ОЗУ нашего МК. В программе мы пишем «spl», хотя можно написать и« $3d».
Правильно будет в обоих случаях, и компилятор не выдаст вам ошибок и предупреждений. Но так сложнее зрительно воспринимать команды.
Так как в разных микроконтроллерах эти адреса имеют свои значения, открыв новый проект, не совсем понятно будет, что там написано. А когда мы используем директивы, то все эти адреса заменяем понятными для нас словами. При желании в файле директив можно поменять все названия на свои. Но тут есть подвох, вы не сможете открыть и скомпилировать какой-то проект с интернета, точно так и ваш проект никто не сможет скомпилировать и проверить на ошибки или внести изменения. Для этого необходимо будет переделывать файлы директив.
Итак, на Листинге 1 приведу пример нашей простой программы.
Листинг 1.
.device Attiny2313 ; указываем тип устройства
.include “tn2313def.inc” ; подключаем файл директив МК ATtiny2313
.def temp = r16 ; задаем имя нашему регистру общего назначения
.org 0x0000 ; начало программы с 0 адреса
ldi temp,ramend ; грузим значение ramend в регистр temp
out $3d, temp ;
ser temp ; настраиваем все выводы порта В на выход
out DDRB, temp ;
main:
sbi portb,5; устанавливаем логическую «1» в PORTB5
rjmp main
Итак, разберем все по строкам, что мы сделали.
Первым делом, на всякий случай указали тип устройства .device Attiny2313.
Подключили файл директив .include “tn2313def.inc”.
Для простоты написания программы задали регистру R16 имя .def temp = r16. Такая операция хорошо будет упрощать написание программы в дальнейшем. Ведь словесное название регистра нам проще запомнить, нежели просто писать R16. Таким образом, можно присвоить имя любому регистру начинаю от R0 и заканчивая R31.
Командой ser temp мы грузим в регистр temp значение 255 и выгружаем его в out DDRB. Тем самым конфигурируем порт на выход. В дальнейшем, при симуляции программы в Proteus 7, мы увидем как данные порты приймут состояние логического нуля.
Устанавливаем на порте вывода PB5 логическую единицу с помощью команды sbi portb,5.
В самом конце необходимо организовать какой-то цикл, чтобы микроконтроллер не завис.
После того как наша программа написана, можно компилировать проект. Для этого нажимаем клавишу F7. Если программа написана без ошибок, то появится диалоговое окно внизу проекта с зеленым кружочком и отчетом об использовании памяти и ошибок.
Открываем среду моделирования Proteus 7 и смотрим результат.
Теперь немного усложним задачу и заставим порт вывода переключаться с логического нуля в единицу. Для этого нам необходимо немного доработать нашу программу, Листинг 2. Все изменения происходит только в цикле «main», так что весь код не будем повторять.
main:
sbi portb,5; устанавливаем логическую “1” в PORTB5
cbi portb,5; устанавливаем логический “0” в PORTB5
rjmp main
Смотрим результат моделирования в среде Proteus 7, пподключив к выводу PB5 осциллограф.
Как видно, сигнал на выходе порта появился. Однако частота переключения близка к частоте микроконтроллера.
Чтобы понизить скорость переключения, нам необходимо воспользоваться простой задержкой. На Листинге 3 показан простой пример реализации задержки.
Листинг 3.
main:
sbi portb,5; устанавливаем логическую “1” в PORTB5
rcall delay ;вызываем подпрограмму задержки
cbi portb,5; устанавливаем логический “0” в PORTB5
rcall delay
rjmp main
delay:
clr r20; очистить регистры
clr r21
d_1:
inc r20; добавить 1
cpi r20,200 ; сравниваем, R20 = 200 ?
brne d_1; если не равно, то переходим по метке d_1, иначе пропускаем
d_2:
inc r21
cpi r21,50 ;
brne d_1
ret
После выполнения данной программы скорость переключения порта снизилась до 100мс. Задавая значения сравнения в регистры R20 и R21 можно регулировать этот интервал. На следующем рисунке видим результат работы программы.
На этом закончим. В следующей части мы разберем примеры программы с подключением кнопок, напишем цикл бегущей строки.
Предыдущие статьи:
♦ Микроконтроллер и как его победить
♦ Микроконтроллер и системы счисления
♦ Микроконтроллер и логические операции
♦ Общее устройство микроконтроллера
♦ Арифметико-логическое устройство и организация памяти – память программ, память данных, энергонезависимая память
♦ Регистры общего назначения, регистры ввода/вывода, стек, счетчик команд
♦ Регистр состояния SREG