Работа с кнопкой в мк авр. Примеры на Си для микроконтроллеров Atmel AVR. Так что, на вскидку, считаем

Казалось бы простая тема, а однако в комментах меня завалили вопросами как подключить микроконтроллер. Как подключить к нему светодиод, кнопку, питание. Что делать с AGND или AREF . Зачем нужен AVCC и все в таком духе. Итак, раз есть вопросы, значит тема не понятна и надо дать по возможности исчерпывающий ответ. Все описываю для контроллеров AVR, но для каких нибудь PIC все очень и очень похоже. Т.к. принципы тут едины.

Питание
Для работы микроконтроллеру нужна энергия — электричество. Для этого на него естественно нужно завести питалово. Напряжение питание у МК Atmel AVR разнится от 1.8 до 5 вольт, в зависимости от серии и модели. Все AVR могут работать от 5 вольт (если есть чисто низковольтные серии, то просьба уточнить в комментах, т.к. я таких не встречал). Так что будем считать что напряжение питания контроллера у нас всегда 5 вольт или около того. Плюс напряжения питания обычно обозначается как Vcc . Нулевой вывод (а также Земля, Корпус, да как только его не называют) обозначают GND . Если взять за пример комповый блок питания. То черный провод это GND (кстати, земляной провод традиционно окрашивают в черный цвет), а красный это +5, будет нашим Vcc . Если ты собираешься запитать микроконтроллер от батареек, то минус батареек примем за GND , а плюс за Vcc (главное чтобы напряжение питания с батарей было в заданных пределах для данного МК, позырь в даташите. Параметр обычно написан на первой странице в общем описании фич:

Operating Voltages
–1.8 — 5.5V (ATtiny2313V)
–2.7 — 5.5V (ATtiny2313)
Speed Grades
–ATtiny2313V: 0 — 4 MHz @ 1.8 — 5.5V, 0 — 10 MHz @ 2.7 — 5.5V
–ATtiny2313: 0 — 10 MHz @ 2.7 — 5.5V, 0 — 20 MHz @ 4.5 — 5.5V

Обрати внимание, что есть особые низковольтные серии (например 2313V низковльтная) у которых нижня граница напряжения питания сильно меньше. Также стоит обратить внимание на следующий пункт, про частоты. Тут показана зависимость максимальной частоты от напряжения питания. Видно, что на низком напряжении предельные частоты ниже. А низковольтные серии раза в два медленней своих высоковольтных коллег. Впрочем, разгону все процессоры покорны;)))))

Для работы контроллерам серии AVR достаточно только питания. На все входы Vcc надо подать наши 5 (или сколько там у тебя) вольт, а все входы GND надо посадить на землю. У микроконтроллера может быть много входов Vcc и много входов GND (особенно если он в квадратном TQFP корпусе. У которого питалово со всех сторон торчит). Много выводов сделано не для удобства монтажа, а с целью равномерной запитки кристалла со всех сторон, чтобы внутренние цепи питания не перегружались. А то представь, что подключил ты питалово только с одной стороны, а с другой стороны чипа навесил на каждую линию порта по светодиоду, да разом их зажег. Внутренняя тонкопленочная шина питания, офигев от такой токовой нагрузки, испарилась и проц взял ВНЕЗАПНО и без видимых, казалось бы, причин отбросил копыта. Так что ПОДКЛЮЧАТЬ НАДО ВСЕ ВЫВОДЫ Vcc и GND . Соединить их соответственно и запитать.

Отдельные вопросы вызвают AGND и AVCC — это аналоговая земля и питание для Аналого-Цифрового Преобразователя. АЦП это очень точный измеритель напряжения, поэтому его желательно запитать через дополнительные фильтры, чтобы помехи, которые не редки в обычной питающей цепи, не влияли на качество измерения. С этой целью в точных схемах проводят разделение земли на цифровую и аналоговую (они соединены должны быть только в одной точке), а на AVCC подается напряжение через фильтрующий дроссель. Если ты не планируешь использовать АЦП или не собираешься делать точные измерения, то вполне допустимо на AVCC подать те же 5 вольт, что и на Vcc , а AGND посадить на ту же землю что и все. Но подключать их надо обязательно!!! ЕМНИП от AVCC питается также порт А.

Warning!!!

В чипе Mega8 похоже есть ошибка на уровне топологии чипа — Vcc и AVcc связаны между собой внутри кристалла. Между ними сопротивление около (!!!) 5Ом Для сравнения, в ATmega16 и ATmega168 между Vcc и AVcc сопротивление в десятки МЕГА ом! В даташите на этот счет никаких указаний нет до сих пор, но в одном из топиков за 2004 год на AVRFreaks сказано, что люди бодались с цифровым шумом АЦП, потом написали в поддержку Atmel мол WTF??? А те, дескать, да в чипе есть бага и Vcc и AVcc соединены внутри кристалла. В свете этой инфы, думаю что ставить дроссель на AVcc для Mega8 практически бесполезно. Но AVcc запитывать надо в любом случае — кто знает насколько мощная эта внутренняя связь?

Простейшая схема подключения Микроконтроллера AVR приведена ниже:

Как видишь, добавился дроссель в цепь питания AVCC , а также конденсаторы. Хорошим тоном является ставить керамический конденсатор на сотню нанофарад между Vcc и GND у каждой микросхемы (а если у микрухи много вход питания и земель, то между каждым питанием и каждой землей) как можно ближе к выводам питания — он сгладит краткие импульсные помехи в шине питания вызыванные работой цифровых схем. Конденсатор на 47мКФ в цепи питания сгладит более глубокие броски напряжения. Кондесатор между AVcc и GND дополнительно успокоит питание на АЦП .

Вход AREF это вход опорного напряжения АЦП . Туда вообще можно подать напряжение относительно которого будет считать АЦП , но обычно используется либо внутренний источник опорного напряжения на 2.56 вольта, либо напряжение на AVCC , поэтому на AREF рекомендуется вешать конденсатор, что немного улучшит качество опорного напряжения АЦП (а от качества опоры зависит адекватность показаний на выходе АЦП ).

Схема сброса
Резистор на RESET . Вообще в AVR есть своя внутренняя схема сброса, а сигнал RESET изнутри уже подтянут резистором в 100кОм к Vcc . НО! Подтяжка это настолько дохлая, что микроконтроллер ловит сброс от каждого чиха. Например, от касания пальцем ножки RST , а то и просто от задевания пальцем за плату. Поэтому крайне рекомендуется RST подтянуть до питания резистором в 10к. Меньше не стоит, т.к. тогда есть вероятность, что внутрисхемный программатор не сможет эту подтяжку пересилить и прошить МК внутри схемы не удасться. 10к в самый раз.

Есть еще вот такая схема сброса:

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

Задержка будет примерно T=R*C для данного примера — около секунды. Зачем эта задержка? Да хотя бы для того, чтобы МК не стартовал раньше чем все девайсы платы запитаются и выйдут на установившийся режим. В старых МК (АТ89С51 , например) без такой цепочки, обеспечивающей начальный сброс, МК мог вообще не стартануть.

В принципе, в AVR задержку старта, если нужно, можно сделать программно — потупить с пол секунды прежде чем приступать к активным действиям. Так что кондер можно выкинуть нафиг. А кнопку… как хочешь. Нужен тебе внешний RESET ? Тогда оставь. Я обычно оставляю.

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

Импульсы задаются тактовым генератором встроенным в микроконтроллер. Впрочем может быть и внешний генератор, все очень гибко конфигурируется! Скорость с которой тикает внутренний генератор зависит от настроек микроконтроллера и обвязки.


Генератор может быть:

  • Внутренним с внутренней задающей RC цепочкой.
    В таком случае никакой обвязки не требуется вообще! А выводы XTAL1 и XTAL2 можно не подключать вовсе, либо использовать их как обычные порты ввода вывода (если МК это позволяет). Обычно можно выбрать одно из 4х значений внутренней частоты. Этот режим установлен по дефолту .
  • Внутренним с внешней задающей RC цепочкой.
    Тут потребуется подключить снаружи микроконтроллера конденсатор и резистор. Позволяет менять на ходу тактовую частоту, просто подстраивая значение резистора.
  • Внутренним с внешним задающим кварцем.
    Снаружи ставится кварцевый резонатор и пара конденсаторов. Если кварц взят низкочастотный (до 1МГц) то конденсаторы не ставят.
  • Внешним.
    С какого либо другого устройства идет прямоугольный сигнал на вход МК, который и задает такты. Полезен этот режим, например, если надо чтобы у нас несколько микроконтроллеров работали в жестком синхронизме от одного генератора.

У разных схем есть разные достоинства:
В случае внутренней RC цепи мы экономим место на плате, нам не нужно дополнительных деталек, но мы не можем развить максимальную частоту и частота немного зависит от температуры, может плавать.

У внешнего кварца отличные показатели точности, но он стоит лишних 15 рублей и требует дополнительных деталей и, что самое обидное, часто съедает пару ног I/O. Также на внешнем же кварце можно добиться максимальной производительности от МК. Частота МК определяется частотой на которую заточен выбранный кварц. Внешная RC цепь позволяет тикать генератору МК быстрей чем от внутренней, стоит дешевле кварца, но имеет те же проблемы со стабильностью частоты, что и внутренняя RC цепь.

Способы тактования МК описаны в даташите в разделе System Clock and Clock Options и всецело определяются конфигурацией Fuse Bit’s . Пока же я настоятельно рекомендую НЕ ТРОГАТЬ FUSE пока ты не будешь твердо знать что ты делаешь и зачем. Т.к. выставив что нибудь не то, можно очень быстро превратить МК в кусок бесполезного кремния, вернуть к жизни который будет уже очень непросто (но возможно!)

Подключение к микроконтроллеру светодиода и кнопки
Сам по себе, без взаимодействия с внешним миром, микроконтроллер не интересен — кому интересно что он там внутри себя тикает? А вот если можно как то это отобразить или на это повлиять…

Итак, кнопка и светодиод подключаются следующим образом:


Для кнопки надо выбраную ножку I/O подключить через кнопку на землю. Сам же вывод надо сконфигурировать как вход с подтяжкой (DDRxy=0 PORTxy=1). Тогда, когда кнопка не нажата, через подтягивающий резистор, на входе будет высокий уровень напряжения, а из бит PINху будет при чтении отдавать 1. Если кнопку нажать, то вход будет положен на землю, а напряжение на нем упадет до нуля, а значит из PINxy будет читаться 0. По нулям в битах регистра PINх мы узнаем что кнопки нажаты.

Пунктиром показан дополнительный подтягивающий резистор. Несмотря на то, что внутри AVR на порт можно подключить подтяжку, она слабоватая — 100кОм. А значит ее легко придавить к земле помехой или наводкой, что вызовет ложное срабатывание. А еще эти внутренние подтягивающие резисторы очень любят гореть от наводок. У меня уже с десяток микроконтроллеров с убитыми PullUp резисторами. Все работает, но только нет подтяжки — сгорела. Вешаешь снаружи резистор и работает как ни в чем ни бывало. Поэтому, для ответственных схем я настоятельно рекомендую добавить внешнюю подтяжку на 10кОм — даже если внутреннюю накроет, внешняя послужит. В процессе обучения на это можно забить.

Светодиод подключается на порт двумя способами. По схеме Порт-земля или Порт-Питание . В первом случае для зажигания диода надо выдать в порт лог1 — высокий уровень (примерно равен Vcc). Во втором случае для зажжения диода требуется выдать в порт лог0 — низкий уровень (около нуля). Для AVR разницы вроде бы нет, а вот многие старые серии микроконтроллеров вниз тянули куда лучше чем вверх, так что схема Порт-Питание распространена чаще. Я применяю и ту и другую схему исходя из удобства разводки печатной платы. Ну, а на программном уровне разницы особой нет.
Вывод порта для работы со светодиодом надо сконфигурировать на выход (DDRxy=1) и тогда в зависимости от значения в PORTxy на ножке будет либо высокий либо низкий уровень напряжения.

Светодиод надо подключать через резистор . Дело в том, что прямое сопротивление светодиода очень мало. И если не ограничивать ток через него, то он просто напросто может сгореть нафиг. Либо, что вероятней, пожечь вывод микроконтроллера, который, к слову, может тянуть что то около 20-30мА. А для нормального свечения обычному светодиоду (всякие мы не рассматриваем сейчас, эти монстры могут и ампер сожрать) надо около 3…15мА.

Так что, на вскидку, считаем:

  • Напряжение на выходе ноги МК около 5 вольт, падение напряжени на светодиоде обычно около 2.5 вольт (выше нельзя, иначе диод сожрет тока больше чем надо и подавится, испустив красивый дым)
  • Таким образом, напряжение которое должен взять на себя ограничительный резистор будет 5-2.5 = 2.5В.
  • Ток нам нужен 5мА — нефига светодиод зря кормить, нам индикация нужна, а не освещение:)
  • R=U/I= 2.5/5E-3 = 500Ом. Ближайший по ряду это 510 Ом. Вот его и возьмем. В принципе, можно ставить от 220 Ом до 680 Ом что под руку попадется — гореть будет нормально.

Если надо подключить много светодиодов, то на каждый мы вешаем по собственному резистору. Конечно, можно пожадничать и поставить на всех один резистор. Но тут будет западло — резистор то один, а диодов много! Соответственно чем больше диодов мы запалим тем меньше тока получит каждый — ток от одного резистора разделится между четырьмя. А поставить резистор поменьше нельзя — т.к. при зажигании одного диода он получит порцию тока на четверых и склеит ласты (либо пожгет порт).

Немного схемотехнических извратов или пара слов о экономии выводов

То что не удается запаять приходится программировать. (С) народная мудрость.

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

Во главу угла такой экономии обычно ставится принцип динамического разделения назначения выводов во времени. То есть, например, вывод может работать на какую-либо шину, а когда шина не активна, то через этот же вывод можно проверить состояние кнопки, или что нибудь передать по другой шине. Быстро (десятки или даже тысячи раз в секунду) переключаясь между двумя разными назначениями можно добиться эффекта «одновременной работы».

Главное, тут следовать двум правилам:

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

Приведу пример:

  • У есть у нас вывод на который повешан выход с некого датчика и кнопка. Выход с датчика может быть 0, 1 в активном режиме и Hi-Z когда на датчик не приходит сигнал Enable.
  • Кнопка же дает на линию жесткий 0, путем короткого замыкания.

Как это должно работать:
Скажем, основную часть времени у нас ввод микроконтроллера настроен на вход Hi-Z и мы снимаем показания с датчика на который подан еще и сигнал Enable. Когда нам надо опросить кнопку, то мы отбираем у датчика Enable и его выходы становятся в режим Hi-Z и нам не мешают. Вывод микроконтроллера мы переводим в режим Pull-Up и проверяем нет ли на входе нуля — сигнал нажатой кнопки. Проверили? Переводим вход МК в Hi-Z вход и подаем Enable на датчик снова. И так много раз в секунду.

Тут у нас возникает два противоречия:

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

    Просто будем знать, что нажатие кнопки искажает показания датчика, а значит когда датчик работает — мы кнопку жать не будем. А чтобы показания датчика не принять за нажатие кнопки мы, в тот момент когда ждем данные с датчика, просто не опрашиваем кнопку. От тупых действий, конечно, это не защитит. Но для упрощения примера защиту от дурака я сейчас во внимания не беру.

  • Электрическое противоречие
    Если датчик выставит 1, а мы нажмем кнопку, то очевидно, что GND с Vcc в одном проводе не уживутся и кто нибудь умрет. В данном случае умрет выход датчика, как более слабый — куда там хилому транзистору тягаться с медной кнопкой.

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

    Если у нас, например, вывод датчика может дать не более 10мА, то резистор нужен такой, чтобы ток через него от Vcc до GND не превышал этой величины. При питании 5 вольт это будет 510Ом. Теперь, даже если на линии со стороны датчика будет лог1, высокий уровень, то нажатие на кнопку не вызовет даже искажения логического уровня т.к. резистор рассчитан с учетом максимальной нагрузки порта

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

Ну и несколько примеров нескольких функций на одной ноге:
Во-первых, ISP разьем . Я уже давным давно забыл что такое тыкать микроконтроллер вначале в колодку программатора, потом в плату, потом обратно и так по многу раз, пока прогу не отладишь. У меня на плате торчат 6 выводов ISP разьема и при отладке программатор вечно воткнут в плату, а программу я перешиваю порой по нескольку раз в 10 минут. Прошил — проверил. Не работает? Подправил, перепрошил еще раз… И так до тех пор пока не заработает. Ресурс у МК на перепрошивку исчисляется тысячами раз. Но ISP разьем сжирает выводы. Целых 3 штуки — MOSI, MISO, SCK.

В принципе, на эти выводы можно еще повесить и кнопки. В таком случае никто никому мешать не будет, главное во время прошивки не жать на эти кнопки. Также можно повесить и светодиоды (правда в этом случае простейший может дать сбой, а вот молодцом!) тогда при прошивке они будут очень жизнерадостно мерцать:)))

На линии под ISP можно повесить и что нибудь другое, главное, чтобы при прошивке это ЧТОТО не начало ВНЕЗАПНО чудить . Например, управление стокилограммовым манипулятором висит на линии ISP и во время прошивки на него пошла куча бредовых данных — так он может свихнуться и кому нибудь бошку разнести. Думать надо, в общем. А вот с каким нибудь , который работает по шинному интерфейсу прокатит такая схема:

Переключаем выход с 0 на 1 и зажигаем то верхний то нижний диод. Если надо зажечь оба, то мы просто переводим вывод микроконтроллера в режим Hi-Z и словно нет его, а диоды будут гореть сквозным током. Либо быстро быстро переключать диоды между собой, в этом случае на глаз они будут оба гореть. Недостаток схемы очевиден — диоды нельзя погасить. Но если по задумке хотя бы один должен гореть, то почему бы и нет? UPD: Тут подумал, а ведь можно подобрать светодиоды и резисторы так, чтобы их суммарное падение напряжения было на уровне напряжения питания, а суммарные резисторы в таком случае загонят ток в такой мизер, что когда нога в Hi-Z то диоды вообще гореть не будут. По крайней мере на глаз это будет не заметно совсем. Разве что в кромешной тьме.

Следующий вариант он не дает экономию ножек, зато позволяет упростить разводку печатной платы, не таща к двум диодам еще и шину питания или земли:

А применив сходную тактику к кнопкам можно либо упростить разводку, либо по трем ножкам развести 6 кнопок.
Тут тоже все просто — одна нога дает подтяг, вторая косит под землю. Нажатие кнопки дает просадку напряжения на подтягивающей ножке. Это чует программа, поочередно опрашивающая каждую кнопку. Потом роли ножек меняются и опрашивается следующая кнопка.

В шестикнопочном режиме ситуация схожая — одна ножка дает подтяг, другая землю, а третья прикидывается ветошью Hi-Z и не отсвечивает. Но тут есть один побочный эффект. Например, опрашиваем мы кнопку «В». Для этого у нас верхняя линия встает на вход с подтяжкой (PORTxy=1, DDRxy=0), средня дает низкий уровень на выходе (PORTxy=0, DDRxy=1), нижняя не участвует в процессе ибо стоит в Hi-Z (PORTxy=0, DDRxy=0). Если мы нажмем кнопку «В» то верхняя линия в этот момент просядет и программа поймет что нажата кнопка «В», но если мы не будем жать «В», а нажмем одновременно «Е» и «Б» то верхняя линия также просядет, а программа подумает что нажата «В», хотя она там и рядом не валялась. Минусы такой схемы — возможна неправильная обработка нажатий. Так что если девайсом будут пользоваться быдло-операторы, жмущие на все подряд без разбора, то от такой схемы лучше отказаться.

Ну и, напоследок, схема показывающая как можно обьединить кнопку и светодиод:


Работает тоже исключительно в динамике. То есть все время мы отображаем состояние светодиода — то есть выдаем в порт либо 0 (диод горит) либо Hi-Z (диод не горит). А когда надо опросить кнопку, то мы временно (на считанные микросекунды) переводим вывод в режим вход с подтягом (DDRxy=0 PORTxy=1) и слушаем кнопку. Режим когда на выводе сильный высокий уровень (DDRxy=1 PORTxy=1) включать ни в коем случае нельзя, т.к. при нажатии на кнопку можно пожечь порт.

Минусы — при нажатии на кнопку зажигается светодиод как ни крути. Впрочем, это может быть не багой, а фичей:)

Вот такие пироги. А теперь представьте себе прогу в которой реализованы все эти динамические фичи + куча своего алгоритма. Выходит либо бесконечная череда опросов, либо легион всяких флагов. В таких случаях простейшая диспетчеризация или кооперативная это то что доктор прописал — каждый опрос гонишь по циклу своей задачи и не паришься. Зато юзаешь везде какую-нибудь ATTiny2313 и ехидно глядишь на тех кто в ту же задачу пихает Mega8 или что пожирней:)

Я ничего не знаю и боюсь что либо сжечь, что мне делать???

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

А потом, на примере того как сделана демоплата, попробовать сделать что то свое. Сама же демоплата представляет собой микроконтроллер + немного стартовой периферии, которой хватит на ряд несложных опытов и которая может облегчить подключение и исследование других устройств. Демоплаты есть разные, например фирменные комплексы вроде STK500 или AVR Butterfly или моя которая была спроектированна исходя из моего опыта и на которой будет строится весь дальнейший учебный курс.

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

Способ первый - традиционный

рис1а рис1б

Если кнопок немного и дефицита выводов мк не наблюдается, используем традиционный способ подключения.

Когда кнопка отпущена – вывод мк через резистор соединен с “плюсом” питания (рис. 1а). Когда кнопка нажата – вывод мк соединен с землей. Подтягивающий резистор R1 ограничивает силу тока в цепи переключателя. Если бы его не было, то при нажатии кнопки мы бы просто закоротили наш источник питания.

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

Что произойдет, если вывод микроконтроллера окажется в режиме выхода? Это будет зависеть от состояния этого вывода. Если на выводе “логический ноль” – ничего страшного не случиться, потому что - в первом случае (рис1а) величина втекающего тока ограничена резистором R1, а во втором случае (рис1б) никакой ток вообще не потечет. При нажатии кнопки тоже ничего не случиться, поскольку разность потенциалов между выводом и “землей” в этом случае будет равна нулю.

Если же на выводе будет ”логическая единица” и кнопка окажется нажатой, то через вывод микроконтроллера на землю потечет ток величиной в несколько десятков миллиампер и вывод порта может “погореть”. Предельно допустимый ток для вывода микроконтролера AVR согласно документации равен 40 мА. Поэтому иногда нелишним бывает поставить между выводом мк и кнопкой резистор номиналом в несколько сотен ом, например 330 (рис 1с). Так, например, подключены кнопки на отладочной плате STK500. Это сделано для подстраховки, чтобы пользователь нечаянно не спалил микроконтроллер в ходе своих эксперементов.

Для своих макетов впрочем можно обойтись и без этого резистора.

Второй способ - с использованием диодов

Используется когда кнопок больше двух, а выводы мк хочется сэкономить. Каждой кнопке в данном случае соответствует свой цифровой код, а количество кнопок, которые можно таким способом повесить на N выводов мк = 2 N - 1. То есть на три вывода можно повесить 7 кнопок, на четыре – 15 и так далее... но я бы больше 7-ми вешать не стал. Увеличивается количество дополнительных внешних компонентов, усложняется схема и программа мк. Кроме того, для большого количества кнопок есть и другие схемы включения. Подтягивающие резисторы на схеме не показаны, подразумевается, что используются внутренние.

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

Данная схема актуальна не для всех микроконтроллеров AVR, потому что в некоторых моделях микроконтроллеров внешнее прерывание может возникать по любому изменению на любом выводе. (например в ATmega164P)

Третий способ – для матричной клавиатуры

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

Выводы PС0, PС1, PС2, PC3 – это строки матрицы, выводы PB0, PB1, PB2 – это столбцы матрицы. Кнопки можно опрашивать либо по строкам, либо по столбцам. Допустим, мы опрашиваем их по столбцам. Процедура опроса будет выглядеть следующим образом. Начальное состояние всех выводов – вход с включенным подтягивающим резистором. Устанавливаем вывод PB0 в режим выхода и выставляем ноль. Теперь нажатие кнопок S1, S2, S3, S4 будет замыкать выводы PС0, PС1, PС2, PC3 на 0 питания. Опрашиваем эти выводы и определям нажата ли какая-нибудь кнопка в данный момент. Устанавливаем вывод PB0 в режим выхода и включаем подтягивающий резистор. Устанавливаем вывод PB1 в режим выхода и выставляем ноль. Снова опрашиваем выводы PС0, PС1, PС2, PC3. Теперь нажатие кнопок S5, S6, S7, S8 будет замыкать выводы PС0, PС1, PС2, PC3. Последний столбец кнопок опрашиваем аналогично.

Строки матрицы можно завести через диоды на вывод внещнего прерывания. Тогда логику программы можно было бы построить так. Если клавиатура не используется в течении нескольких минут, микроконтроллер переходит в режим пониженного энергопотребления. При этом выводы PB0, PB1, PB2 – конфигурируются как выходы с нулевым логическим уровнем. Когда одна из кнопок нажимается, вывод прерывания через диод замыкается на ноль. Это вызывает внешнее прерывание, микроконтроллер просыпается и запускает таймер по сигналам которого происходит сканирование клавиатуры. Параллельно запускается счетчик времени, который сбрасывается при нажатии любой из кнопок. Как только он переполняется, микроконтроллер опять переходит в режим пониженного энергопотребления.

Сегодня мы расширим свой кругозор по изучению работы портов микроконтроллера и изучим второе назначение порта — работу на вход. И для изучения работы на вход мы применим обычную тактовую кнопку.

Как всегда, создадим проект в Atmel Studio, выберем Atmega8A, назовем проект Test04 и код также в main.c, как обычно, скопируем с проекта предыдущего урока.

В качестве подопытного порта давайте возьмём порт B. Можно с успехом использовать любой порт. И в качестве ножки возьмем нулевую ножку. Итак у нас ножка B0.

Также опять мы соберём проект, скопируем и переименуем файл протеуса, откроем его и в свойствах контроллера покажем путь к новому проекту. Запустим на выполнение и убедимся, что всё работает.

Добавим кнопку в протеусе, для этого в поиске компонентов найдём Button

Затем подключим нашу кнопку вот таким вот образом к ножке B0 контроллера

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

Для этого мы, во-первых настроим порт B. Мы можем объявить все ножки порта B на вход, так как нам не важны настройки остальных ножке, ибо мы их не используем

DDRD = 0xFF;

DDRB = 0x00;

В случае, когда мы работали с портом D на выход, биты регистра PORTD отвечали за уровень на соответствующих ножках. А в случае, когда порт инициализирован на вход, как наш порт B, то биты регистра PORTB будут уже отвечать за подтягивание к соответствующим ножкам порта резисторов на шину питания. Если будет логическая единица, то регистр будет подтягиваться, а если логический ноль — то не будет. Поэтому мы в 0 бите регистра установим 1

PORTD = 0b00000001;

PORTB = 0b00000001;

Соберём код и запустим его в протеусе. Мы видим, что на ножке B0 у нас установилась логическая 1 , а если мы нажмём кнопку, то увидим, что на ней будет логический 0 , о чём свидетельствует синий цвет квадратиков на ножке и на кнопке.

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

// for(i=0;i<=7;i++)

// {

// PORTD = (1<

// _delay_ms(500);

// }

В данном цикле мы и будем отслеживать состояние ножки PB0. Делается это с помощью определения состояния соответствующего бита в регистре PINB, который собственно за это и отвечает.

Чтобы нам следить за каким-либо действием или состоянием, нам необходимо будет обработать условие.

Условие в языке C добавляется с помощью команды if .

И в качестве условия мы возьмём состояние ножки 0 порта B или состояние бита 0 регистра PINB .

Как же можно получить состояние одного бита, ведь в языке C в отличие от ассемблера нет битовых операций?

Можно пойти на хитрость и применить вот такую конструкцию PINB &0b00000001 .

Данная конструкция нам и проверит нулевой бит. То есть если в регистре PINB также будет 1 в нулевом его бите, то независимо от состояния остальных битов в данном регистре мы получим ненулевой результат, что также является истиной. То есть если ни с чем не сравнивать в условии результат, то условие эквивалентно сравниванием с нулём, только наоборот. Для истинности результат должен быть ненулевым — (результат!=0 ).

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

while (1)

if (!( PINB &0b00000001))

{

}

else

{

}

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

DDRB =0x00;

PORTD =0b00000000 ;

PORTB =0b00000001;

while (1)

If (!(PINB &0b00000001))

PORTD =0b00000001;

Else

PORTD =0b00000000;

Теперь давайте пересоберём проект и пойдём в протеус смотреть, удалось ли нам что-то.

Чтобы у нас при сборке не было даже предупреждений, уберём объявление переменной i, так как она в коде не используется

int main ( void )

// unsigned char i;

Unsigned char butcount =0;

Запустим проект в протеусе и увидим, что при нажатии на кнопку у нас начинает светиться самый верхний светодиод

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

И чтобы это как-то отследить и определить, что это было именно нажатие, а не дребезг, то мы будим отслеживать нажатие некоторое время, ну или некоторое количество тактов или циклов. Для этого в начале функции main() до бесконечного цикла мы добавим другую переменную (i нам ещё пригодится и мы её портить не будем). Назовём мы переменную butcount , так как имя переменной должно как-то само за себя говорить и тем самым достигается ещё большая читабельность кода

// unsigned char i;

unsigned char butcount =0;

И чтобы воспользоваться данной переменной, мы применим ещё одно условие. И у нас будет условие в условии. Это всё допустимо и очень широко используется. И в зависимости от этого условия мы данную переменную будем наращивать (инкрементировать). Условием будет у нас достижение данной переменной определённой величины. То есть попробуем сделать так, чтобы значение переменной не достигало 5

if (!( PINB &0b00000001))

if ( butcount < 5)

{

butcount ++;

}

А когда значение данной переменной достигнет значения 5, то мы уже в данный цикл не попадём, а попадём мы в тело оператора else , который мы сейчас и добавим и в его теле напишем следующий код

Butcount ++;

else

PORTD =0b00000001;

То есть мы как раз после достижения пятёрки и будем обрабатывать нажатие кнопки и включать на нулевой ножке порта D высокое состояние.

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

else

if (butcount >0)

{

butcount —;

}

else

{

PORTD =0b00000000;

}

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

Давайте теперь соберём проект и проверим его работу сначала в протеусе, а затем и на практике. Выглядит это приблизительно так. Интереснее конечно это смотреть в видеоуроке

Смотреть ВИДЕОУРОК

Post Views: 13 084

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

Схема

Схема подключения кнопок традиционная – один контакт кнопки соединен с выводом микроконтроллера, другой с нулем питания. Подтягивающие резисторы внутренние. Кнопки подключены к обычным выводам микроконтроллера, не обладающим функциями внешних прерываний.

Заголовочный файл

Заголовочный файл содержит символические имена регистров и выводов порта, к которому подключены кнопки. Коды которые будут записываться в буфер при нажатии кнопок. Прототипы функций.


void BUT_Init(void);

Настраивает выводы к которым подключены кнопки на вход и включает подтягивающие резисторы. Вызывается обычно в начале main`a.


void BUT_Debrief(void);

Выполняет однократный опрос кнопок. В функции реализована антидребезговая защита – чтобы номер кнопки был занесен в буфер, она должна быть нажата в течении заданного числа циклов опроса. Номер кнопки заносится в буфер однократно, даже если кнопка долго удерживается. Эта функция вызывается в прерывании таймера.


unsigned char BUT_GetKey(void);

Возвращает содержимое кнопочного буфера, при этом буфер очищается. Вызывается из основного цикла программы.

void BUT_SetKey(unsigned char key);

Записывает в буфер значение. Используется для имитации нажатия кнопки.

Сишный файл

Си файл содержит реализацию всех функций. Из них интерес, пожалуй, представляет только функция опроса.


Обычная тактовая кнопка может находиться в двух состояния – нажатом и отпущенном (третье состояние – сломанное, нас сейчас не интересует). В идеальном случае кнопка переходит из одного состояния в другое мгновенно, однако в реальности при нажатии ее контакты многократно замыкаются и размыкаются, прежде чем окажутся в установившемся замкнутом состоянии. Это явление называется дребезгом контактов, и оно присуще всем механическим переключателям.

Микроконтроллер работает на частоте в несколько мегагерц, и пока происходит дребезг, может опросить кнопку несколько раз. Если не принять каких-то мер, то дребезг контактов будет “воспринят” микроконтроллером как многократное нажатие. Для некоторых приложений это не критично, ну, например, если кнопка управляет включением лампы фонаря. Но в большинстве случаев это явление нежелательное.

Дребезг контактов можно устранить аппаратно, с помощью пассивного НЧ фильтра. Но гораздо проще побороть дребезг, сделав программу микроконтроллера невосприимчивой к нему. Вариант подобного алгоритма как раз и реализован в функции BUT_Debrief()

#define THRESHOLD 20

volatile unsigned char pressedKey = 0;
unsigned char comp = 0;

void BUT_Debrief(void )
{
unsigned char key;

//последовательный опрос выводов мк

if (BitIsClear(PIN_BUTTON, ENTER))
key = KEY_ENTER;
else if (BitIsClear(PIN_BUTTON, CANCEL))
key = KEY_CANCEL;
else if (BitIsClear(PIN_BUTTON, UP))
key = KEY_UP;
else if (BitIsClear(PIN_BUTTON, DOWN))
key = KEY_DOWN;
else {
key = KEY_NULL;
}

//если во временной переменной что-то есть
if (key) {

//и если кнопка удерживается долго
//записать ее номер в буфер
if (comp == THRESHOLD) {
comp = THRESHOLD + 10;
pressedKey = key;
return ;
}
else if (comp < (THRESHOLD+5)) comp++;

}
else comp=0;
}

В начале функции выполняется последовательный опрос выводов. Если какая-нибудь из кнопка нажата, во временную переменную key заносится соответствующий код. Затем проверяется значение переменной comp, чтобы выяснить как долго кнопка находится в нажатом состоянии. Пока comp не достигнет некоего порогового значения THRESHOLD, она будет инкрементироваться. Как только значение comp станет равным THRESHOLD, временная переменная key будет скопирована в кнопочный буфер pressedKey. При этом comp получит новое значение, чтобы при следующих опросах кнопок микроконтроллер не инкрементировал эту переменную, и не производил запись в буфер. Таким образом, как бы долго пользователь не удерживал кнопку, микроконтроллер зафиксирует факт ее нажатия только один раз.

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

#define THRESHOLD2 300
...
if (key)
{
if (comp > THRESHOLD2)
{
comp = THRESHOLD2 - 40;
pressedKey = key;
return ;
}
else comp++;

if (comp == THRESHOLD)
{
pressedKey = key;
return ;
}
}
else comp=0;
...

Теперь, если кнопка удерживается больше 300 циклов опроса, микроконтроллер снова и снова будет записывать в буфер ее код.

На этом пока все, но тема кнопок не закрыта. Продолжение следует...

Описан простой эксперимент с подключением кнопки к AVR микроконтроллеру, разобрана не сложная программа на языке Си для обработки нажатий кнопки. Разберемся с особенностями подключения кнопки к портам МК, а также с методами считывания состояний кнопки на языке Си.

В предыдущих статьях были рассмотрены эксперименты со светодиодами, которые подключались к портам микроконтроллера, сконфигурированных на вывод (Output).

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

Принципиальная схема эксперимента

Для того чтобы можно было хоть как-то наблюдать и управлять чем-то с помощью кнопки мы подключим к микроконтроллеру еще два светодиода. Схемка очень простая, вот она:

Рис. 1. Принципиальная схема эксперимента с микроконтроллером ATtiny2313 и кнопкой.

Как видим, к двум портам PB0 и PB1 через ограничивающие резисторы подключены два светодиода, а к порту PD2 - кнопка и она также с ограничивающим резистором. Для подключения программатора к МК используется разъем Conn 1 (AVR-ISP), а для подключения схемы к отдельному источнику питания +5В предназначены два контакта - P1 и P2.

Рис. 2. Собранная на беспаечной макетной панели схема эксперимента с микроконтроллером и кнопкой.

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

Структура портов ввода-вывода в AVR микроконтроллерах

Пины микроконтроллера являются универсальными GPIO (General Purpose Input Output), к ним можно подключать как исполнительные устройства (индикаторы, силовые ключи), так и разнообразные цифровые датчики (кнопки, переключатели).

Несколько пинов в МК могут быть подключены к АЦП/ЦАП (Аналогово-Цифровой-Преобразователь и наоборот), с их помощью можно выполнять анализ и генерацию аналоговых сигналов. Обычные GPIO не умеют работать с аналоговыми сигналами, у них на входе/выходе может быть только 0 (0В) или 1 (+5В).

К каждому пину GPIO внутри микроконтроллера подключены несколько блоков и электронных компонентов, о которых полезно знать:

  • Между пином порта и каждой из шин питания (GND и VCC) подключено по диоду . Они используются для "гашения" кратковременных помех, скачков напряжения относительно пина и каждой из шин питания;
  • Также между пином и GND включен конденсатор . Точно не знаю зачем он нужен, возможно для защиты от помех, для предотвращения дребезга контактов при использовании кнопок и переключателей подключенных к пину, или еще для чего-то;
  • К каждому пину подключен электронный ключ с резистором - это подтяжка пина к напряжению источника питания (Pull-UP) . Данный электронный ключ включается программно и служит для установки по умолчанию высокого логического уровня 1 (+5В) при работе с пином в режиме ввода (Input);
  • Между пином и каждой из шин питания (GND и VCC) включены еще два электронных ключа (без резисторов), они нужны для установки на пине высокого (+5В) или низкого (0В) логического уровня при работе пина в режиме вывода (Output).

Для программного управления и конфигурирования каждого из портов применяются три специальных регистра, к примеру для порта "B":

  • DDRB - регистр (8 бит) для установки режимов работы пинов - на ввод или вывод. Осуществляется установкой соответствующих бит в регистре;
  • PORTB - регистр для управление состоянием пинов порта в режиме вывода - высокий или низкий уровень. Также используется в режиме ввода, применяется для включения подтягивающих резисторов (Pull-UP) и установки высокого уровня на входе по умолчанию;
  • PINB - регистр, который содержит логические состояния пинов в порте, используется для чтения значений портов, которые сконфигурированы в режиме ввода.

Более детально узнать об устройстве портов для конкретной модели микроконтроллера можно из его даташита, в разделе "I/O-Ports", также там могут быть приведены примеры кода на Си и Ассемблере для работы с портами.

Пин RESET в качестве порта ввода-вывода

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

Это может быть полезно если у микросхемы не хватает пинов для вашей конструкции. Например при сборке какого-то устройства на чипе ATtiny13 (8 выводов, 2шт - питание, 5шт - порты ввода-вывода, 1шт -для RESET) у вас оказалось что не хватает одного пина для светодиода. Здесь может быть несколько вариантов решения проблемы:

  1. Перепрограммирование пина с RESET под порт ввода-вывода;
  2. Подключение светодиода к одному из соседних уже использованных пинов, применив некоторые хистросты в схемном решении и с учетом возможности его общего использования;
  3. Использование другого МК у которого больше пинов, например ATtiny2313.

Что из этих вариантов проще и дешевле по финансам/времени - судите по своему случаю.

Для превращения пина "RESET" в порт ввода-вывода придется изменить специальный фьюз - RSTDISBL (Reset Disable). Но прежде чем это сделать нужно помнить что после данной операции перепрограммировать микроконтроллер станет возможным только с применением высоковольтного программатора (на 12В), обычный USB ISP или другой программатор с питанием от 5В сделать свою работу уже не сможет.

Программа на Си

Итак, у нас есть одна кнопка и два светодиода которые подключены к микроконтроллеру, что же с ними можно сделать? - а сделаем мы вот что (алгоритм):

  1. После включения питания светодиоды будут мигать попеременно и с задержкой в 300 миллисекунд;
  2. При нажатии и удержании кнопки будет светиться только синий светодиод;
  3. После отжатия кнопки синий светодиод мигнет 3 раза с задержкой 500 миллисекунд, после чего светодиоды снова будут мигать поочередно и с задержкой 300 миллисекунд.

Пример реализации такого алгоритма на языке Си под AVR приведен ниже. Создадим новый файл для нашей программы и откроем его для редактирования:

Nano /tmp/avr-switch-test.c

Поместим следующий код в тело файла:

/* Эксперимент с кнопкой на ATtiny2313 * https://сайт */ #define F_CPU 1000000UL // Частота ядра = 1 МГц #include #include // -- Макросы для управления светодиодами -- #define LED_BLUE_ON PORTB |= (1 << PB0) // Засвечиваем синий диод #define LED_BLUE_OFF PORTB &= ~(1 << PB0) // Гасим синий диод #define LED_RED_ON PORTB |= (1 << PB1) // Засвечиваем красный диод #define LED_RED_OFF PORTB &= ~(1 << PB1) // Гасим красный диод // Основная программа void main(void) { DDRD |= (0 << PD2); // Пин 6 - на вход PORTD |= (1 << PD2); // Включаем подтягивающий (Pull-UP) резистор для пина 6 DDRB |= (1 << PB0); // Пин 12 - на вывод DDRB |= (1 << PB1); // пин 13 - на вывод // -- Бесконечный цикл -- while(1) { _delay_ms(300); // Задержка 300 мс LED_BLUE_ON; // Включаем синий диод LED_RED_OFF; // Гасим красный диод _delay_ms(300); LED_RED_ON; // Включаем красный диод LED_BLUE_OFF; // Гасим синий диод if(!(PIND & (1 << PD2))) { // Проверяем нажата ли кнопка _delay_ms(50); // Задержка 50 мс (дребезг контактов) LED_RED_OFF; LED_BLUE_ON; while(!(PIND & (1 << PD2))); // Ждем пока кнопка не будет отпущена _delay_ms(500); // Дальше мигаем синим диодом LED_BLUE_OFF; _delay_ms(500); LED_BLUE_ON; _delay_ms(500); LED_BLUE_OFF; _delay_ms(500); LED_BLUE_ON; _delay_ms(500); LED_BLUE_OFF; _delay_ms(200); } // Конец блока работы с кнопкой } // Конец блока с вечным циклом }

Первым делом мы задаем константу F_CPU , которая укажет компилятору рабочую частоту ядра микроконтроллера, это нужно чтобы некоторые подпрограммы и функции работали корректно. В нашем примере используется функция задержки по времени - "_delay_ms" из библиотеки "util/delay.h", которая просчитывает время затраченное на холостые такты, опираясь на значение в константе F_CPU.

Посмотреть код библиотеки "delay" для организации задержки по времени и в котором используется константа F_CPU, можно в GNU Linux при помощи любого текстового редактора, к примеру можно выполнить вот такую команду:

Nano /usr/lib/avr/include/util/delay.h

Заводская установленная частота внутреннего RC генератора в микроконтроллере ATtiny2313 равняется 8000000Гц (8МГц), также по умолчанию установлен фьюз деления частоты - CKDIV8 (Clock Divide by 8), поэтому реальная рабочая частота кристалла = 8000000Гц / 8 = 1000000Гц = 1МГц.

Посмотреть какие фьюзы установлены в микроконтроллере можно при помощи avrdude или же графической оболочке к нему под названием AVR8 Burn-O-Mat .

Дальше в программе определены макросы для управления состоянием портов к которым подключены светодиоды: LED_BLUE_ON, LED_BLUE_OFF, LED_RED_ON, LED_RED_OFF. Вызвав подобный макрос в любом месте программы мы очень просто можем зажечь или погасить каждый из светодиодов, не придется повторять его код, что в свою очередь упростит программу и сделает ее более наглядной.

В основной программе "void main(void)" мы начинаем работу с конфигурации портов:

  • DDRD |= (0 << PD2) - установка разряда PD2 регистра DDRD на ввод, к нему подключена кнопка (пин 6);
  • PORTD |= (1 << PD2) - включение подтягивающего резистора для пина к которому привязан разряд PD2 регистра PORTD (пин 6);
  • DDRB |= (1 << PB0) - установка разряда PB0 в регистре DDRB на вывод, к нему подключен СИНИЙ светодиод (пин 12);
  • DDRB |= (1 << PB1) - установка разряда PB1 в регистре DDRB на вывод, к нему подключен КРАСНЫЙ светодиод (пин 13).

Дальше, используя макросы, мы гасим красный светодиод и зажигаем синий. Теперь при помощи еще одного вечного цикла но у же с условием мы выполним ожидание до того момента, пока кнопка не будет отжата: "while(!(PIND & (1 << PD2)));".

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

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

Очень простая программа, но тем не менее, она является хорошим примером и почвой для дальнейших экспериментов.

Настройка Geany под ATtiny2313

В предыдущих публикациях я проводил эксперименты с микроконтроллером ATMega8, здесь же используется менее "нафаршированный" МК - ATTiny2313.

Для компиляции программы и прошивки ее в МК следует немножко перенастроить команды для сборки в интегрированной среде программирования Geany.

Идем в меню Build - Set Build Commands. В команде для компиляции (C commands) нужно изменить модель применяемого чипа: "-mmcu=attiny2313". В команде для прошивки МК нужно изменить тип чипа для avrdude: "-p t2313".

Рис. 3. Перенастройка Geany для работы с микроконтроллером ATTiny2313.

Все команды приведены для ОС GNU Linux, если у вас Windows то возможно придется прописать полные пути к бинарным файлам "avr-gcc.exe", "avr-objcopy.exe", "avrdude.exe".

Более подробно о том как настроить Geany в GNU Linux я рассматривал в одной из предыдущих статей цикла.

Компиляция и прошивка программы в МК

Компиляцию, сборку и прошивку программы можно выполнить нажав в среде Geany поочередно три кнопки: "Compile", "Build" и "Run". Также все эти операции можно выполнить из консоли, вот команды для данных действий (выполнять последовательно):

Avr-gcc -mmcu=attiny2313 -Os /tmp/avr-switch-test.c -o /tmp/avr-switch-test.o avr-objcopy -j .text -j .data -O ihex /tmp/avr-switch-test.o /tmp/avr-switch-test.hex avrdude -c usbasp -p t2313 -P usb -U flash:w:/tmp/avr-switch-test.hex

Все команды почти полностью (за исключением подстановок имен файлов) идентичны тем, которые мы исправляли в настройках Geany.

Заключение

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

  • Сергей Савенков

    какой то “куцый” обзор… как будто спешили куда то