AVR. Учебный курс. Устройство и работа портов ввода-вывода

С внешним миром микроконтроллер общается через порты ввода вывода. Схема порта ввода вывода указана в даташите:

Но новичку там разобраться довольно сложно. Поэтому я ее несколько упростил:

Итак, что же представляет собой один вывод микроконтроллера. Вначале на входе стоит небольшая защита из диодов, она призвана защитить ввод микроконтроллера от превышения напряжения. Если напряжение будет выше питания, то верхний диод откроется и это напряжение будет стравлено на шину питания, где с ним будет уже бороться источник питания и его фильтры. Если на ввод попадет отрицательное (ниже нулевого уровня) напряжение, то оно будет нейтрализовано через нижний диод и погасится на землю. Впрочем, диоды там хилые и защита эта помогает только от микроскопических импульсов от помех. Если же ты по ошибке вкачаешь в ножку микроконтроллера вольт 6-7 при 5 вольтах питания, то никакой диод его не спасет.

Конденсатор, нарисованный пунктиром, это паразитная емкость вывода. Хоть она и крошечная, но присутствует. Обычно ее не учитывают, но она есть. Не забивай голову, просто знай это, как нибудь я тебе даже покажу как её можно применить ;)

Дальше идут ключи управления. Это я их нарисовал рубильниками, на самом деле там стоят полевые транзисторы, но особой сути это не меняет. А рубильники наглядней.
Каждый рубильник подчинен логическому условию которое я подписал на рисунке. Когда условие выполняется — ключ замыкается. PIN, PORT, DDR это регистры конфигурации порта.

Есть в каждом контроллере AVRPIC есть тоже подобные регистры, только звать их по другому).

Например, смотри в даташите на цоколевку микросхемы:

Видишь у каждой почти ножки есть обозначение Pxx. Например, PB4 где буква «B» означает имя порта, а цифра — номер бита в порту. За порт «B» отвечают три восьмиразрядных регистра PORTB, PINB, DDRB, а каждый бит в этом регистре отвечает за соответствующую ножку порта. За порт «А» таким же образом отвечают PORTA, DDRA, PINA.

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

При снижении напряжения питания разумеется эти пороги также снижаются, график зависимости порогов переключения от питающего напряжения можно найти в даташите.

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. Честно говоря, я даже не знаю нафиг он нужен — ни разу не доводилось его применять и даже не представляю себе ситуацию когда бы мне надо было запретить использование подтяжки сразу для всех портов. Ну да ладно, инженерам Atmel видней, просто знай что такой бит есть. Мало ли, вдруг будешь чужую прошивку ковырять и увидишь что у тебя подтяжка не работает, а вроде как должна. Тогда слазаешь и проверишь этот бит, вдруг автор прошивки заранее где то его сбросил.

Общая картина работы порта показана на рисунке:

Теперь кратко о режимах:

  • Режим выхода
    Ну тут, думаю, все понятно — если нам надо выдать в порт 1 мы включаем порт на выход (DDRxy=1) и записываем в PORTxy единицу — при этом замыкается верхний ключ и на выводе появляется напряжение близкое к питанию. А если надо ноль, то в PORTxy записываем 0 и открывается уже нижний вентиль, что дает на выводе около нуля вольт.
  • Вход Hi-Z — режим высокоимпендансного входа.
    Этот режим включен по умолчанию. Все вентили разомкнуты, а сопротивление порта очень велико. В принципе, по сравнению с другими режимами, можно его считать бесконечностью. То есть электрически вывод как бы вообще никуда не подключен и ни на что не влияет. Но! При этом он постоянно считывает свое состояние в регистр PIN и мы всегда можем узнать что у нас на входе — единица или ноль. Этот режим хорош для прослушивания какой либо шины данных, т.к. он не оказывает на шину никакого влияния. А что будет если вход висит в воздухе? А в этом случае напряжение будет на нем скакать в зависимости от внешних наводок, электромагнитных помех и вообще от фазы луны и погоды на Марсе (идеальный способ нарубить случайных чисел!). Очень часто на порту в этом случае нестабильный синус 50Гц — наводка от сети 220В, а в регистре PIN будет меняться 0 и 1 с частотой около 50Гц
  • Вход PullUp — вход с подтяжкой.
    При DDRxy=0 и PORTxy=1 замыкается ключ подтяжки и к линии подключается резистор в 100кОм, что моментально приводит неподключенную никуда линию в состояние лог1. Цель подтяжки очевидна — недопустить хаотичного изменения состояния на входе под действием наводок. Но если на входе появится логический ноль (замыкание линии на землю кнопкой или другим микроконтроллером/микросхемой), то слабый 100кОмный резистор не сможет удерживать напряжение на линии на уровне лог1 и на входе будет нуль.

Также почти каждая ножка имеет дополнительные функции. На распиновке они подписаны в скобках. Это могут быть выводы приемопередатчиков, разные последовательные интерфейсы, аналоговые входы, выходы ШИМ генераторов. Да чего там только нет. По умолчанию все эти функции отключены, а вывод управляется исключительно парой DDR и PORT, но если включить какую-либо дополнительную функцию, то тут уже управление может полностью или частично перейти под контроль периферийного устройства и тогда хоть запишись в DDR/PORT — ничего не изменится. До тех пор пока не выключишь периферию занимающую эти выводы.
Например, приемник 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 у нас планка прибита гвоздями к земле или прижата домкратом к питанию. Внешняя сила может ее пересилить только сломав домкрат или сломается сама. Тупая внешняя сила просто разрушает наш домкрат или вырывает гвозди из пола с мясом. В любом случае — девайс в помойку.

151 thoughts on “AVR. Учебный курс. Устройство и работа портов ввода-вывода”

  1. я понимаю что с каждым годом молодежь все деградирует. но чтоб да таких пояснений в виде картинок :)) юморно, может в универах это за программу обучения возьмут :)

      1. А что? Здорово, и самое главное — наглядно!
        Детишек обучать удобно…

        Ваши бы рисунки собрать в кучу — был бы неплохой весёлый конспект по АВР…
        помнится где-то я уже видел подобные комиксы на одном из сидюков посвящённых радиолюбителям… Весёлый конспект назывался…

    1. > У некоторый AVR запись “1″ в PIXx.n меняет уровень на выходе.

      Подтверждаю. Если DDRx.n сконфигурирован как «выход», то запись «1» в PINx.n даёт инверсию выхода!
      Очень удобно иногда. Типа моргать светиком и т.п.
      Не работает это на древних камнях, типа 90S…, а на Мегах вроде на всех проходит такой фокус.

    1. А это смотря что искать. У меня в «избранном» ссылок по PIC куда больше, чем по ATMEL.
      А вообще — то я использую и те, и те. Смотря что удобней, или при повторении чего-то готового, когда переделывать лень. Атмеловские контроллеры мне понятны, потому что я в 80-90х годах много работал с MCS 48, предком MCS 51, и по 51 тоже читал в то время много, ну а PIC сами по себе хороши… Другое дело — по конкретным сайтам, там обычно уклон в ту или иную сторону. Этот, например, начинался как учебник по AVR, что и определило соответственно посетителей. Но есть немало и работающих с PIC.
      А если смотреть, например, по моему архиву сохраненных страничек лет за 10, конструкций на PIC гораздо больше. Как и программаторов для них.

  2. Привет, DI! Немного оффтоп, но тоже про IO: задумал я сделать примитивный софтовый уарт чтоб юзать его на милипиздрических тиньках типа 10й или 12й. Однакопришла мысль, что таким макаром можно сделать только полудуплекс, ибо одновременно на вход и выход порт работать не может. Или все-таки с ноги порта, настроенного на выход, можно читать уровни, подаваемые извне?

  3. На Пиках значение регистра направления порта (TRIS) проще запомнить:
    выход = 0 (лог. ноль) от англ. «O»utput
    вход = 1 (лог. единица) от англ. «I»nput
    Вот это я понимаю, красиво и логично, а на AVR вывернули наизнанку

    1. Кроме того, в PIC удобнее работать с портами. Не надо разделять порт на регистры для чтения и записи. В любой команде просто указываем, например, Port B. Мало того, с портом можно делать любые логические и арифметические операции, как и с любым другим регистром. У PIC вообще нет отдельно выделенных регистров «Общего назначения» как таковых, которых вечно нехватает, да еще при прерываниях или вызове подпрограмм их надо сохранять в стеке, потом не забыть извлекать обратно, причем в порядке, обратном загрузке. Все регистры равноправны — ОЗУ, порты, регистры управления… Можно с любым делать любые операции, как байтовые, так и битовые, а также проверять любой бит с последующим условным переходом. Потому у PIC и команд всего 33 — 35 — 37 от младшего семейства до старшего, что все команды работают одинаково со всеми регистрами. А у AVR десятки одинаковых команд, но работающих только с определенными группами регистров или с каким-нибудь видом адресации. После дизассемблирования листинг хрен поймешь, с каждой командой отдельно приходится разбираться. А еще говорят, что AVR проще программировать…

      1. Коллега, я вас поддерживаю на все 100! Развяжем холиварчик PIC vs AVR? :)
        В ранних пиках была проблемка при записи в порт — там выполнялась процедура чтение-модификация-запись. Т.е. записали в порт 1, потом с портом провели какой AND или XOR, и если порт сильно нагружен (т.е. «просажен» нагрузкой), то прочитается не 1, а 0. В пиках поновее поэтому уже имеем 3 регистра работы с портами: TRISx (регистр направления порта), PORTx (регистр порта) и LATx (защёлка порта).

        1. «“просажен” нагрузкой» — В нормально спроектированной схеме такого быть не должно по определению. Что же это за нагрузка, которая просаживает порт так, что его логический уровень меняется на противоположный? И почему тогда не поставили какой — нибудь буферный усилитель? Для того в даташитах и указывают нагрузочные параметры, чтобы их соблюдать… Другое дело, что если записали в порт какое — то значение, затем часть линий перевели на ввод, выполнили логическую операцию с портом, в ходе которой как раз и произойдет чтение — модификация — запись, потом опять захотели использовать записанное ранее в защелке — так и коту понятно, что оно изменилось, что вытекает из логики работы порта в таких случаях. Так за этим должен следить сам программист, и сохранять где — нибудь значение, которое было записано в порт, если оно будет нужно в дальнейшем. Опять же, фирменные мануалы постоянно об этом напоминают.

          1. Если схема достаточно сложна, то нормально её спроектировать нет никакой возможности — на это есть и формальная теорема, что достаточно сложные системы отладить на 100% никак нельзя (название забыл). Поэтому и приходится делать макетки, а не заказывать сразу печатки, а также ловить баги и прочее. Да и от импульсных помех и иголок по питанию мало кто застрахован. Вот потом сидишь и чешешь репу — это аппаратный прикол, или опять где в коде накосячил? Ну а в общем с Вами согласен.

            1. Поэтому и надо как можно больше обдумать заранее, предусмотреть, что может и не может быть, и по возможности не залазить в заведомо недопустимые режимы. Затраченное время на этапе проектирования и анализа техзадания вернется сторицей при отладке. Лучше перестраховаться, чем потом доводить готовое устройство. Лучше несколько лишних емкостей на питании, чем головная боль потом, и прочее в том же духе.

        1. «на кой черт там регистр W» — так это типичный регистр — аккумулятор. Есть в любом процессоре (например, в 8080 — регистр A). Как же без него? куда грузить переменные, константы, временно хранить результаты операций, к чему привязать флаги статуса? Он и у AVR есть. Ты наверное имеешь в виду, что в PIC нельзя, например, сложить сразу регистр с регистром? Так это и в AVR можно далеко не со всяким регистром. А расширенная функциональность небольшой группы «регистров общего назначения» в AVR сводится на нет убогостью операций со всеми остальными. В PIC же я могу одной командой, например, установить или сбросить бит ЛЮБОГО регистра (и ячейки ОЗУ, и порта, и регистров управления), причем без предварительной пересылки в аккумулятор и обратно, сделать условный переход по состоянию любого бита ЛЮБОГО регистра (то есть, и всего ОЗУ!). Команды, использующие аккумулятор (регистр W), имеют возможность сохранить результат или в аккумуляторе, или в другом регистре, участвующем в операции. Это очень удобно, сокращает количество пересылок, а также позволяет делать операции (например, используя маски), не меняя содержимого самого регистра, или наоборот, сохраняя в нем сразу результат. Я много работал с ассемблером I8080 и I8048, немного изучал также Мотороловский M6800 с двумя аккумуляторами (A и B), а также использовавшийся в Эплах 6502, работаю немного и с AVR, и в сравнении с ними PIC мне представляются довольно удобными. Просто с ними надо поработать, чтобы оценить их гениальную простоту. А на языке высокого уровня, например, МикроПаскале, так и вовсе нет особой разницы, для PIC или AVR писать. Хотя одна и та же программа на PIC получается короче, и 8к байт программной памяти Меги 8 и 8 к слов PIC16F876 — большая разница в пользу PIC (у него все команды = 1 слово), хотя по набору периферии они примерно одинаковы. (Но предделитель таймера PIC при подаче сигнала с внешнего вывода способен работать до 70-80МГц, независимо от частоты самого PIC!).
          Обычно считают, что на AVR 1 команда = 1 такт. Хрен там! Это только за счет конвейера, и только на линейной последовательности команд. А попробуйте сделать переход, вызов подпрограммы, или вход в обработчик прерывания… Плюс еще неизбежная возня со стеком для сохранения некоторых регистров «общего назначения», которых вечно для всех задач не хватает. В PIC кроме аккумулятора и слова состояния сохранять ничего не надо, у него регистры — все ОЗУ, просто каждая подпрограмма использует свои, не перекрываясь. Кроме того, стек не отьедает кусок ОЗУ, и не проедется по нему при малейшем сбое, сметая все содержимое… Да много еще чего…

          1. В AVR нет аккумулятора. В этом то и вся фишка. И регистр с регистром сложить можно любой. Младшие регистры ограничены на запись в них констант, на работу с портами и ОЗУ ну и на ряд других операций, что делает их очень неудобными при ручных операциях.

            1. «В AVR нет аккумулятора». В исходном MCS 51 (I8051), акумулятор был в явном виде (регистр ACC). В AVR его функции раскидали на блок из 32 универсальных регистров, причем неравномерно. Можно считать, что там 32 аккумулятора, но это не совсем так, из за их разной функциональности. Поэтому я и продолжаю считать, что там тоже есть аккумулятор (просто так мне привычнее, для совместимости с другими контроллерами).
              Кроме того, есть деление на регистровый файл, служебные регистры или «память ввода-вывода», статическое ОЗУ (SRAM). В PIC же практически все это — просто регистры, без явного разграничения с точки зрения выполняемых с ними операций в командах. В любой команде, работающей с регистрами, может использоваться любой из 7F регистров текущего банка, независимо, регистр ли это порта, управляющий регистр, или область ОЗУ.

              1. AVR ближе к Load/Store архитектуре. Его 32 регистра это именно как аккумулятор. Зато ОЗУ может быть слоноподобного размера и обращение задается к ней через индексы. Не нужно переключать банки, работает с ОЗУ как с куском.

                Переход по битам из IO тоже возможен (кроме последних АВР, где число периферии уже таково, что адресация IO не влазит в разрядность команды и приходится через Load/Store орудовать, впрочем, команды проверки бита в регистре (любом) это нивелируют.

                Я так обычно 32 регистра юзаю как сплошной Temp, а при входе и выходе делаю массивный прогруз из ОЗУ. Либо через стек.

                1. Все дело в привычке. Но в каждой архитектуре есть свои плюсы и минусы. И если PIC и AVR идут в моих предпочтениях довольно близко друг к другу, то с другими фирмами — отрыв огромный. Сколько ни просматривал их контроллеры — отрыв большой, никогда не возникало желания их использовать. Уж больно они все какие — однобокие, заточенные под свои, узкие, сферы применения. В свое время Микрочиз нашел удачное сочетание свойств, периферии, достаточно полное, но без избыточности. АТМЕЛ это удачно использовало, но на базе другого популярного ядра. Результат на лице.
                  Работа с ОЗУ огромного размера, в большинстве существующих моделей AVR реально отсутствующего, и соизмеримого по реально установленному обьему с ОЗУ в аналогичных по функциональности PIC, не кажется такой уж необходимой. Работа же с банками тоже не так сложна, как кажется. В простых случаях вообще хватает одного банка, в более сложных обычно используется язык высокого уровня, который берет на себя эту проблему. Кроме того, есть 16 регистров ОЗУ в верху, адресуемых из любого банка. В них удобно сохранять, например, аккумулятр и регистр статуса при входе в обработчик прерывания, и глобальные переменные, доступных из любого места программы.
                  Зато в PIC практически не приходится работать со стеком. Там нечего хранить, кроме адреса возврата, а это делается автоматически, поэтому 8 уровней — за глаза. Тем более компилятор постоянно следит за количеством вложений (по крайней мере те компиляторы, что я использую).

          2. ===»В любой команде, работающей с регистрами, может использоваться любой из 7F регистров текущего банка»===
            Хм… Так 7F байт — это килобайт… килобайт с четвертью. У большинства массовых моделей МК ОЗУ значительно меньше, даже если учесть регистры и порты. Т.е. получается что в большинстве PIC страниц вообще нет? Или там память программ в этом же пространстве? Откуда страницы-то?

    1. Ты чо, бывают такие кадрый которые и это не понимают с первого раза, приходится дополнительно пояснять. Я решил провести эксперимент, останутся ли еще непонявшие после такого мануала в картинках? ;))))

  4. Еще можно уточнить что втекающий ток максимальный выще чем вытекающий (немного), и разные порты имеют разную мощность. Актуально если кто хочет снимать по 20 ма с каждой ноги:

    PDIP Package:

    1] The sum of all IOL, for all ports, should not exceed 400 mA.
    2] The sum of all IOL, for ports C0 — C5 should not exceed 200 mA.
    3] The sum of all IOL, for ports B0 — B7, C6, D0 — D7 and XTAL2, should not exceed 100 mA.

    PDIP Package:
    1] The sum of all IOH, for all ports, should not exceed 400 mA.
    2] The sum of all IOH, for port C0 — C5, should not exceed 100 mA. (меньше! или ошибка?)
    3] The sum of all IOH, for ports B0 — B7, C6, D0 — D7 and XTAL2, should not exceed 100 mA.

  5. Хозяину блога большой респект :) Была попытка освоить микроконтроллеры AVR, но нарвавшись в свое время на avr123… забил на это дело, там хотя и немало инфы, но глаз и моск в процессе ее усвоения на том ресурсе сломать можно запросто! Буду здесь ошиваццо, может просеку чего в деле программирования AVR.

  6. Вы уж извините, но я даже с комиксами не понял. Видимо особо одаренный :(. Скорее всего принина в отсутствии подписей (какая картинка к какому режиму).
    Итак, сложившееся представление:
    1. Порт — группа из восьми ножек.
    2. Порт в единицу времени может быть либо входом, либо выходом (все 8 ножек).
    2.1. Если он выход — то выход и ниипет… можем в него записывать из программы (изнутри), и узнать, чего записали.
    2.2. Если он вход, то тут два варианта:
    2.2.1. Hi-Z:
    2.2.1.1. Если висит в воздухе — считывает экстрасенсорные сигналы головного мозга обезьяны на расстоянии 100м.
    2.2.1.2. Если подключен — тут вопрос: как отличить 0 от 1? Если я правильно понял, то в ход идет любая разница в напряжении между тактами. Если разница >0, то 1, 3.5В — то 1, иначе 0.

    Исходя из описания к п.2.2 для потока данных лучше использовать Hi-Z, для событий и четкого потока (либо 5В, либо 0В) — PullUp

    З.Ы.
    Вы уж извините, но микроконтроллеры всегда были для меня магией. Образования в этом плане (и во всех связанных) НОЛЬ. Всегда была мечта идиота: взял бы какой-нть гуманитарий, да описал все на детском языке. Как мне показалось, нашел ресурс своей мечты.

    Подтвердите или опровергните плз все пункты, включая «З.Ы.».

    1. Ф-ции изменения каментов явно не хватает, поток мыслей с выводом в текстовом виде последнее в кашу превращает…

      2.2.1.2. Если подключен — тут вопрос: как отличить 0 от 1? Если я правильно понял, то в ход идет любая разница в напряжении между тактами. Если разница >0, то 1, иначе 0. (U[i] — U[i-1] > 0, y = 1)
      2.2.2. PullUp — четкая граница мжду 0 и 1. Если напряжение на ноге, скажем >3.5В — 1, иначе 0.

    2. 1 — да
      2 — нет. Порт частью ножек может быть на вход, частью на выход. Каждая нога настраивается индивидуально.

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

      Когда провод в хиз подключен куда либо, то напряжение там определяется другими микросхемами ведущими передачу. МК же просто считывает не вмешиваясь в процесс.

      Пуллап же нужен для считывания данных от пассивных устройств или от устройств которые умеют только коротить на землю (кнопки, контакты, логика с открытым коллектором) в этом случае высокий уровень делает подтягивающий пулап резистор.

      1. Так… Картинка проясняется…
        Т.о. если я к ноге подключаю, к примеру, выход с другой микросхемы, то лучше Hi-z. А если кнопку, или, скажем, анод фотодиода (катод на землю), то тут полюбас PullUp (подтянутый кверху).

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

      2. === Порт частью ножек может быть на вход, частью на выход ===
        Вот, а можно тут помедленнее? :) Можно ли один порт сделать входом-выходом одновременно? Пример: берём не сильно ногастую тиню. Имеем 8-разрядный порт. Но под задачу надо, к примеру, 3 линии на вход и 5 на выход. Можно ли это решить одним портом, а не двумя разными? Т.е. прочитал порт, наложил маску и получил своё 3-битное число. И тут же выдал в этот же порт 5-битное (ессно, биты д.б. в нужных местах). Это так делается или нужно ещё какие-то процедуры проводить по подготовке порта перед каждым чтением/записью?
        И ещё момент: если 2 линии из трёх входных назначить на внешние прерывания (т.е. на собственно данные оставить 1 линию) — такая развесистая конфигурация порта возможна: 2 входа на прерывания, 1 вход данных, 5 выходов данных?

        1. Порт конфигурируется побитно. Т.е. как выводы запрогал так и будет. Главное их побитно и обрабатывать потом. Иначе у тебя по неосторожности может выход входом стать или еще какая гадость случится. Масками это и делается, а вообще в AVR полно команд позволяющих работать с отдельными битами порта. Т.е. можно сразу же выставить/сбросить/проверить конкретный бит порта. На Memory Mapped регистрах это не работает, но на младших вполне.

          Никаких процедур делать не надо. Сконфигурировал биты и просто знаешь, что у тебя где лежит.

          Конечно возможна, надо следить только чтобы они друг другу не мешали.

  7. «Как запомнить режимы, чтобы не лазать каждый раз в справочник: » (на грани больной фантазии) можно сравнить регистр DDR с половыми органами: 0 принимает , 1 отдает. запомнить проще. по крайней мере мне.

  8. Знач так, мож я туплю, а мож иза той литры пива что вдудлил… у Вас в статье: нога МК имеет три режима:
    1) Выход — читаем(DDR = 1, PORT — делает погоду)
    2) Вход — читает МК(DDR = 0, PORT = 1)
    3) Вход HI-Z «Этот режим хорош для прослушивания какой либо шины данных, т.к. он не оказывает на шину никакого влияния», тоесть мы читаем — парадокс:(.

    Потом: книга «Юрий Ревич — Практическое программирование Микроконтроллеров AVR на языке ассемблера» скачана из Вашего сайта, страница 16 строка 10: «все выводы в них могут пребывать в трех состояниях (вход — отключен — выход)».

    Проблема: мой мозг незнает кому поверить, можно я буду считать что HI-Z ето не вход а отключен?

    1. Вот смотри.

      В режиме DDR=0 PORT=1 МК собственоручно подтягивает порт в 1, а значение смотрит из PIN.

      Вход DDR=0 PORT=0 это тоже чтение, но уже пассивное. Т.е. МК никак не влияет на линию, просто смотрит ее состояние и тихонько записывает в PIN

      И никаких парадоксов.

  9. Ребя!
    Мне вот надо на вход мк с ЛПТ порта единичку подать допустим…
    так как мне вход настраивать?
    как пулап или как хайзет,а?
    с кнопкой и замыканием на землю всё ясно,а с +5в как быть?
    хочу шаговиком порулить (софт-МК-L293-ШД).

      1. Во! Спасибо за быстрый ответ.
        И протэус мне показал что хайзет работает…
        щас накатаю софтинку с одной кнопкой «крути_по часовой»,проверю…
        ещё раз тенькью.

  10. а что предпочтительнее для порта висящего в воздухе(кнопка) в плане энергосбережения (при переключении контролера в режиме POWER SAVE) — оставить входом с подтяжкой, или сделать выходом и записать лог нуль??

      1. так тыж сам пишешь, что пин в Z состоянии, если никуда не подключен, ловит на себе окружающие наводки, от этого входной буфер хаотически и многократно переключается, и следовательно ОЧЕНЬ много жрет.
        Вот я и спрашиваю, как будет меньше потребление, если оставить входом с подтяжкой или выходом с нулём? (при переходе в POWER SAVE. в это время выводы мне не нужны, просыпаюсь от таймера)

          1. отключается?? само? странно. в моей «любимой книге» Евстифеев А.В. — Микроконтроллеры AVR семейства Mega сказано лишь что в новых мегах можно отключать входные буферы. а в старых (я работаю с мега16а) нужно САМОСТОЯТЕЛЬНО сконфигурировать порты на наименьшее потребление. Вот я и решаю, как мне их конфигурировать во время сна…..

            1. Ну тогда кури даташит. Т.к. у меня среднее потребление устройства в 99% около киловатта и милливатты микроконтроллера я даже за потери не считаю и никогда на энергопотребление не заморачивался.

              Но по логике вещей:

              Кнопка на Pull Up обычно работает, но этот режим самый неэкономичный. Т.к. кнопка может нажаться и будет лишняя утечка энергии.

              Надо перевести в экономичный:
              HI-Z если логика порта засыпает и самый безопасный от последствий
              Low или High — в зависимости от схемотехники кнопки. Т.к. перевод в high или Low может убить вывод порта если кнопка будет давить порт в противоположное направлении. Опять же, если есть внешние подтяжки кнопки — будет лишнее потребление. Плюс надо учитывать тот момент когда происходит выход из спячки и делать сразу же переконфигурирование портов. Иначе будут эффекты.

  11. Простите за оффтоп. Я хочу прочитать данные с термодатчика DS18B20, порт настроен на вход. На экране осциллографа напряжение единички на уровне 2,5 вольт. В чём может быть дело?

        1. Ну теперь дело за малым. Понять кто просаживает линию. Отключи вначале МК, потом отключи Даллас. По изменению уровня сразу поймешь где проблема. Возможно порт где то пробит.

  12. Прежде всего. Сайт — класс! Радуюсь, что гугление привело к нему.

    А вопрос по статье следующий — мелькало слово «сила», «пересиливать». Я понимаю, что это для упрощения :). Просто не понимаю, что является «мерой» этой силы. Ведь цифровой выход — это низкий импеданс (только сопротивление канала МОП), ключ на 1 или на 0. Соответственно, если нога-вход AVR подтянута, а на выходе ставится 0, то на резисторе подтяжки падает всё напряжение, соответственно, PIN=0.

    И ещё не понял. Почему от «грубой силы» (закорот от упавшей отвёртки), лучше защищает режим Pull-Up, чем Hi-Z?

    Заранее спасибо :).

    1. Под силой тут можно принято ток. Т.е. какой ток может обеспечить источник и какой ток может просадить выход без разрушения. Грубо говоря у кого меньше сопротивление (внутреннее сопротивление источника против сопротивления выхода) тот и победит.

      Разницы никакой. Но Pull-UP дает подтяжку и обеспечивает определенный уровень. не позволяя выводу болтаться под действием помех как на HiZ

  13. «В режиме OUT у нас планка прибита гвоздями к земле или прижата домкратом к питанию. Внешняя сила может ее пересилить только сломав домкрат или сломается сама. Тупая внешняя сила просто разрушает наш домкрат или вырывает гвозди из пола с мясом. В любом случае — девайс в помойку.»

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

  14. так должно быть теоретически, но практически очень часто с контроллером ничего не случается. Особенно если только один вывод закорочен. Ток КЗ ограничивается до какого нибудь большого, но «вменяемого» уровня, и контроллер остается жив. При этом иногда даже не нарушается работа остальных узлов.

  15. А как думаете, можно ли параллелить порты для увеличения выходного тока. ну например запараллелил два порта, и вешай нагрузку в 80ма??? Естественно позаботившись о строго синхронном переключении.

    1. В даташите всё описанно :) Удвоить нельзя помоему, может 20% даст улучшения, и снижение сопротивления источника тока. Там есть суммарное ограничение тока порта и контроллера, оно намного ниже чем допустимая мощность каждой ноги. Несинхронное переключение даст массу глюков, проще транзистор прилепить помоему и будет хоть 80 мА, хоть 80 ампер :)

  16. ———
    Есть еще бит PUD (PullUp Disable) в регистре SFIOR он запрещает включение подтяжки сразу для всех портов. По дефолту он равен 0. Честно говоря, я даже не знаю нафиг он нужен — ни разу не доводилось его применять и даже не представляю себе ситуацию когда бы мне надо было запретить использование подтяжки сразу для всех портов. Ну да ладно, инженерам Atmel видней
    ———
    У меня в datasheete сказано, что при уходе в сон для меньших затрат электроэнергии лучше отключать все Pull-up резисторы. Видимо здесь и будет полезен этот бит

  17. Добрый день,DI!
    Начал ковырять микроконтроллеры по Вашим материалам и с помощью вашей платы!
    Возник такой вопрос!
    Зажигаю я диодик при включении МК, при этом значение в регистре PORTB ни на что не влияет! Хоть он 0, хоть 1, если вывод работает на выход (DDRB=1) диод горит!
    Как именно PORTB (1 или 0) в режиме работы на выход влияет на работу МК?

    ldi temp, 0b00000001
    ; ldi temp1, 0
    out DDRB, temp
    ; out PORTB, temp1 ; диод горит и при 1, и при 0

  18. Вопрос снят в силу своей глупости и корявости рук))!
    Осталось только непонимание вот в чём:
    диод зажигается от двух команд:
    ldi temp, 0b00000001
    out DDRB, temp
    То есть явно прописывать PORTB=0 (у меня схема порт-питание) почему-то не нужно!
    Если я принудительно пропишу PORTB=1, естественно ничего не горит, но в тоже время диод загорается и без явного указания PORTB=0. Это и непонятно.

  19. Подскажите пожалуйста, написал так:
    #include
    int main()
    {
    DDRB = 1<<2;
    PORTB = 1<<2;
    return 0;
    }
    подключен светодиод. atmega128. частота 8 МГц. Питание стабильное.
    шью. А он сволочь МИГАЕТ!! Один раз из 30 он всё таки горел но потом опять та же картина.. Пробовал менять фьюз BODLEVEL результат 0. Не могу понять что такое..

  20. DI, выражаю тебе огромную благодарность за созданный ресурс! столько грамотного, и доступно написанного (что наверно самое главное) материала наверное нигде не найти.
    особенно радует стиль написания статей))))
    долго смеялся ,когда читал в «основах на пальцах» про аналогию с «дермищем»)))супер!
    так держать!

  21. Привет, начал осваивать твои уроки (отдельная за них благодарность, нигде более ясной подачи материала не встречал), и столкнулся с проблемой (заранее извиняюсь, если вопрос глупый). В качестве теста решил написать программу, которая будет инвертировать линии 2х портов В и С с задержкой (задержка на декременте 2х регистров). Когда писал программу прямиком, тоесть, без использования подпрограммы для задержки — все мигало, стрелка дергалась. Как только закинул всю конструкцию, включая директивы, инициализацию стека (без нее вроде бы подпрограмма хз куда возвращалась) — начали глючить именно 6 и 3 линии портов С и В. В чем проблема?
    http://easyelectronics.ru/repository.php?act=view&id=86

  22. Спасибо, сегодня попробую! Хотя я в отладчике прогонял, только в регистры 2 записывал, чтоб меньше времени занимало — и все работало исправно, т.е. регистры значений порта тов нулях, то в FF.

    1. Привет, делал с адресами векторов, без адресов, без прерываний, чего, вобщем, только не делал. Даже без подпрограммый сейчас некоторые ноги в «1», причем, некоторые на 20 мВ меньше напряжения питания. И, может, это галюны от постоянного переписывания / программирования, но как буд-то каждый раз другие ножки замыкает.
      Появилась идейка, я когда подключаю мультиметр, показывает ток в районе 80 мА, может, я, просто, порты сжег? Хочу завтра купить парочку новых МК, хотелось бы к защите рабочее устройство собрать. Может, стоит напряжение мерять через резистор тестером?

  23. Привет, не совсем в тему статьи, но буду очень признателен, если поможешь.

    Выбираю себе контроллер для одного простого устройства. За ATMega16 у меня запросили 220 руб. Наверно это нормальная цена, но все же дороговасто. А в ней есть много того, что мне для моего устройства не нужно.

    Нужно вот что:
    1. UART
    2. SPI (но это, как я понимаю, само собой разумеется)
    3. Еще два пина просто I/O порта

    Это все что нужно, остальное все равно не будет использоваться.

    Какой бы мне выбрать контроллер?

  24. А чем чревато, если я Hi-Z вход подсоединю напрямую на землю или питание? Я так уже на практике делал, выходило просто значение 0 и 1 но мб так делать нельзя?). И кто мне объяснит как он эти значения считывает, там же ток никакой вообще не течет, что напрямую подрубаю, что слушаю чего-либо.

      1. Щас вот еще вопросы у меня появились.
        1. Если я слушаю какую-либо линию, то у меня выходит будит следующие результаты:
        а) С одной стороны линии 1, с другой 1. Я прочту 1?
        б) С одной стороны линии 1, с другой 0. Я прочту 0?
        в) С одной стороны линии 0, с другой 0. Я прочту 0?
        2. Тут упоминается про подтягивающие резисторы, которые могут быть перетянуты помехами. Это как?
        3. Дальше в статьях говорится, о том как подвесить на одну ногу несколько задач. К примеру нога для программатора и еще там чего-нибуть повесить. Что бы все это работало, опять нужно использовать подтягивающие резисторы. Но такие, чтоб их можно было перетянуть. Я этого тоже не понимаю(

        1. 1) Да.
          2) Резисторы к земле или питанию, смотря куда подтяжка. ИХ цель обеспечивать уровень напряжения на ноге, там же просто затвор полевого транзистора, по сути конденсатор. Вот чтобы его обкладка его не ловила из воздуха заряд и не болталась куда ей вздумается, надо резистором задать начальное положение.
          3) Напряжение определяется током. Через выскоомный резистор идет маленький ток и он не способен противостоять низкоомному сопротивлению которое задаст уровень.

          1. Из всего выше сказанного выходит, если я подтяну ногу напрямую к земле\питанию, то хрен два её чего потом перетянет. Что помехи, что программатор, причем последний небось может еще и выйти из строя, в зависимости от схемы. Я прав?

            1. Смотря чем подтянешь. Положишь на землю напрямую (сопротивление подтяжки = 0) да, скорей сгорит что-нибудь. Если через резистор в 100кОм, то оторвать его от земли может вывод с сопротивлением к питанию в районе 10кОм. Там же образуется простейший делитель напряжения между землей и питанием из двух резисторов. Где нижний резистор подтяг в землю, верхний резистор то, что пытается ногу от земли оторвать. Порассчитывай его и поймешь все.

  25. Всем привет.
    Поспорил с коллегой, что за 1 такт AVR не сможет установить ножку любого порта на нужный уровень, например PORTX |= (1<<PXY); (кроме прямой записи PORTX=Y). То есть в отличии вот STM (знаю только про 32f4) у АВР нету регистра аля BSRR, который просто накладывает маску на регистр порта у STM.
    Подскажите кто прав?
    Спасибо!

    1. Проспорил, можешь проставляться :) У AVR может и нет регистра масок, но есть выделенные команды (SBI,CBI) которые ставят/снимают бит в регистрах IO за один такт (не во всех регистрах, но в портах ВВ и его контрольных регистрах точно . Так что по факту «Чтение-модификация-запись» превращается в «Запись» если компилятор не лохи писали и оптимизатор клювом не щелкает. А обычно все компиляторы понимают запись правильно.

  26. День Добрый!

    1. Есть ли более подробная информация о возможностях хилых защитных диодов?
    К примеру, на вход настроенном на АЦП стоит резистор 100к, попадает напряжение 100в, ток 1мА — диод выдержит ?

    2. И если есть защитные диоды — то почему в документации указано масимальное допустимое напряжение =6в ?

  27. Привет.
    Сорри за нубский вопрос…
    А если я хочу на порт повесить кнопку, но управляющий сигнал — Лог 1. То есть кнопка нажата, если на порте Лог 1.
    Как тогда нужно инициализировать порт?
    Ведь в режиме Пулл-ап подтяжка до питания, а мне нужно наоборот, до земли.
    В таком случае нужно делать режим Хай-з и физически подтягивать ножку до земли? Или как?

    Заранее спасибо за ответ.

    1. А твоя кнопка что делает? Подтаскивает к питанию? Тогда делай вход в HiZ, а ногу снаружи принудитель через резистор в 10кОм на землю. В результате кнопка будет тягать ее вверх, а МК это отследит.

  28. Push-pull = {лог1 = напряжение питания; лог0 = земля;} ? Так?
    Если так, то да, будет push-pull.
    А что значит «делает само»?

    Будет прерывание INT0, которое будет отлавливать плюсовый импульс.

    Блиин… Только что прочитал даташит, там написано, что INT прерывание гернерится все время, пока напряжение выше порога. А мне нужено только одно прерывание для импульса.. Длину его нет возможности выяснить :(
    Наверное, нужно строить логику с INT настроенным на прерывание при смене логического уровня…

  29. В можно такой вопрос (связан с предыдущим): если входной сигнал будет 12в. Ножка выдерживает 5.5в. Можно ли сделать так: ножку порта на вход с подтяжкой до питания (ддр 0, порт 1) и поставить между сигналом и ножкой диод (катодом к сигналу). Тогда если с сигнала 0 — диод открыт и на ножке лог0, а если подать сигнал (12в), то диод закрывается и на ножке лог1. То есть не надо следить даже, чтоб не превышалось напряжение на вход.
    Я правильно рассуждаю? Или чушь полная?..

  30. Здравствуйте.
    вот написал код на кнопку, но не могу функцией PORTB подключить подтягивающий резистор, так как питание подается на PORTB.0

    вот код

    #include

    void main(void)
    {

    #pragma optsize-
    CLKPR=0x80;
    CLKPR=0x00;
    #ifdef _OPTIMIZE_SIZE_
    #pragma optsize+
    #endif

    PORTB=0b00000000;
    DDRB=0b00000010;

    while (1)
    {
    if (PINB.0==0) PORTB.1=0;
    else PORTB.1=1;

    }
    }

  31. подскажите еще, почему при написание вот так

    DDRB=0b00000000 // назначил порты как входы
    PORTB=0b00000001 // при подключение на вход подтягивающего резистора, в протеусе показывает что со входа выходит питание +5 вольт

    это как понимать?

  32. Опечатка закралась:
    PINх
    … граница гарантированного нуля и граница гарантированной единицы …. Для пятивольтового питания это 1.4 и 1.8 вольт соответственно.

    Должно быть 1.4 и 4.8.

  33. Скажи пожалуйста. А на ноге можно выставить некое значение напряжения? В идеале сделать то, что делает функция analogWrite() в ардуине, но без ШИМа.
    Цель — надо одним проводом с одного МК на другой передать значение от 0 до 1024(грубо говоря 0-5 вольт) и обработать его на другой стороне (там это делает тинька 13. Во время работы с АЦП тини, значения по понятным причинам сильно скачут.)

    1. Конечно же нет. Оно же цифровое и дискретное все. Только ШИМом и то если есть вывод шима на этом, в противном случае придется вручную дергать все это. У некоторых контроллеров (STM32 например) есть ЦАП и там можно на вывод ЦАПа это сделать. Но, опять же, это одна-две ноги, не больше.

      А в твоем случае куда выгодней гнать цифрой. Вариантов дофига. Например передавать импульс длительностью от N до 1024N и принимая его на той стороне замерять его длительность. Или сделать подобие уарта или 1-wire.

Добавить комментарий