RadioRadar - Радиоэлектроника, даташиты, схемы

https://www.radioradar.net/radiofan/radiofan_technology/introduction_arduino_part2.html

Операции цифрового ввода—вывода. Знакомство с Arduino. (часть 2)

Загрузив среду разработки Arduino IDE, можно увидеть, что в выведенной в открывшемся окне заготовке будущей программы присутствуют две функции: setup() и loop(). С функции setup() начинается работа любой программы. Она выполняет её один раз сразу после подачи питания на плату, а также каждый раз после нажатия на имеющуюся на плате кнопку RESET, устанавливающую микроконтроллер в исходное состояние. Внутри этой функции задают режимы работы портов, инициализируют последовательный интерфейс и другие периферийные устройства, как находящиеся внутри микроконтроллера, так и подключённые к нему внешние. Эта функция, даже если она пуста, должна обязательно присутствовать в программе.

Функция loop() содержит бесконечный цикл, который микроконтроллер многократно выполняет, вплоть до выключения питания. В нём опрашивают внешние датчики, подают команды исполнительным устройствам, производят вычисления и прочие операции. В качестве примера приведём простейшую программу, которая с периодом в одну секунду зажигает, а затем гасит встроенный в плату Arduino светодиод, обозначенный на ней буквой L и подключённый к цифровому выводу D13.

Эта программа входит в число стандартных примеров, прилагаемых к среде разработки Arduino IDE. В табл. 1 приведён её текст в том виде, в котором он приложен. Учтите, что на принятом в среде поклонников Arduino жаргоне исходный текст программы называют "скетчем" - эскизом.

Таблица 1

Фрагменты программы, относящиеся к одному блоку, ограничивают фигурными скобками { и }. Далее будем называть их операторными скобками. Текст программы может содержать комментарий, поясняющий её суть и нюансы работы. Многострочный комментарий ограничивают сочетаниями символов /* (в начале) и */ (в конце). С символов // начинают комментарий, заканчивающийся в конце той же строки. При трансляции (преобразовании текста программы на понятном человеку языке программирования в исполняемый микроконтроллером машинный код) эта часть текста полностью игнорируется.

Единственная исполняемая строка тела функции setup() 

pinMode(13, OUTPUT);

переводит вывод D13 платы Arduino в режим выхода.

Функция loop() начинается со строки

digitalwrite(13, HIGH);

Она устанавливает на выводе D13 высокий логический уровень. В Arduino UNO он равен напряжению питания (+5 В) относительно общего провода. Этим будет включён светодиод.

За ней следует строка

delay(1000);

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

digitalwrite(13, LOW);

Далее программа ещё раз выдерживает паузу длительностью 1 с, после чего повторяет с начала всю последовательность операций, описанных в теле функции loop(). Так продолжается до выключения микроконтроллера.

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

Следует помнить, что максимальный ток, отдаваемый выводом Arduino, работающим как выход, равен 40 мА, при этом общий ток всех выходов не должен превышать 300 мА. Для питания обычных светодиодов этого достаточно, можно также напрямую подключить к выходу низковольтное герконовое реле или маломощный вибромотор от сотового телефона. Что-нибудь более мощное подключить без усилителя не получится, да это и опасно - можно испортить микроконтроллер.

Аналоговые входы A0-A5 при необходимости можно использовать наряду с D0-D13 как цифровые входы и выходы, обращаясь к ним соответственно по номерам с 14 по 19.

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

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

Далее изменим программу так, чтобы мигал светодиод, подключённый не к выводу D13, а к выводу D12 Arduino. Поскольку на плате светодиода, подключённого к D12, нет, потребуется внешний светодиод с добавочным резистором. Подключить его нужно по схеме, показанной на рис. 1. Добавочный резистор подбирают так, чтобы токче-рез светодиод находился в пределах 5...10 мА. Это обеспечит достаточно яркое свечение большинства светодиодов. Плата Arduino UNO с подключённым внешним светодиодом изображена на рис. 2.

Схема подключения светодиода

Рис. 1. Схема подключения светодиода

 

Плата Arduino UNO с подключённым внешним светодиодом

Рис. 2. Плата Arduino UNO с подключённым внешним светодиодом

 

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

Чтобы управлять светодиодом, подключённым не к D13, а к D12, в рассматриваемом случае было бы достаточно исправить в тексте программы все числа 13 на 12. Не считая комментариев, число 13 фигурирует в тексте программы всего три раза, так что изменить его нетрудно. Однако с увеличением объёма программы ситуация принципиально меняется. Одно дело заменить три числа и совсем другое несколько десятков одинаковых чисел в разных местах длинной программы. К тому же может оказаться, что где-нибудь это число обозначает что-то совсем другое и менять его не нужно.

Чтобы подобные изменения проводить было проще, объявим в начале программы переменную и присвоим ей значение, соответствующее номеру нужного вывода:

int LEDPIN = 12;

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

Модифицированная программа приведена в табл. 2. Её необходимо загрузить в память микроконтроллера платы Arduino. Для этого следует выбрать в главном меню IDE пункт "Файл→Загрузить". Если набранная в окне редактирования программа не была сохранена в файл заранее, IDE попросит указать имя файла, в котором и сохранит её. Спустя некоторое время, необходимое IDE Arduino на трансляцию программы в понятные микроконтроллеру машинные коды, на плате начнут мигать светодиоды "Rx" и "Tx", сигнализируя о приёме и передаче сообщений через последовательный интерфейс микроконтроллера.

Таблица 2

Если всё было сделано правильно, в нижней части окна программы появится отчёт о корректной загрузке. В нём будет выведена информация о том, какую часть из имеющихся 32 Кбайт программной памяти микроконтроллера заняла загруженная программа и сколько оперативной памяти требуется для размещения переменных. Светодиод, подключённый к выводу D12, начнёт мигать с периодом 2 с.

Если подключить к выводам D8-D12 Arduino линейку из пяти светодиодов (рис. 3) и загрузить в микроконтроллер программу, приведённую в табл. 3, она будет поочерёдно включать на 500 мс каждый из этих светодиодов и подключённый к D13 светодиод, установленный на плате. Эта программа могла оказаться значительно длиннее, если бы задача решалась "в лоб", простым повторением нужное число раз в функции setup() строки с разными числовыми номерами выводов, настраивающей их на выход, а в функции loop() - последовательности строк, включающих очередной светодиод, выдерживающих паузу и выключающих его. Сократить программу помогли операторы цикла for.

Линейка из пяти светодиодов

Рис. 3. Линейка из пяти светодиодов

 

Таблица 3

В круглых скобках после ключевого слова for указаны начальное значение переменной цикла - LEDPIN=8, условие выполнения тела цикла - LEDPIN<14 и операцию, выполняемую с переменной цикла после каждого исполнения его тела - LEDPIN++, что означает увеличение значения переменной на единицу. При необходимости параметры цикла for могут быть легко изменены.

Тело цикла в операторных скобках следует за условием. В первом случае (в функции setup()) оно состоит из одной строки, которая будет выполнена шесть раз со значениями LEDPIN от 8 до 13. Во втором случае (в функции loop()) оператор цикла задаёт шестикратное выполнение последовательности из трёх строк с такими же значениями переменной.

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

При напряжении питания 5 В, а в Arduino UNO оно именно такое, цифровые входы микроконтроллера гарантированно воспринимают как логически высокое (соответствующее логической единице) напряжение более +3 В, а как логически низкое (соответствующее логическому нулю) - напряжение менее +1,5 В. Промежуточные значения (в том числе, когда вход никуда не подключён) дают непредсказуемый, зависящий от экземпляра микроконтроллера, напряжения его питания, температуры и других факторов, хаотически изменяющийся результат. Поэтому желательно, чтобы на цифровой вход всегда было подано напряжение заведомо высокого или низкого логического уровня.

Простейший датчик - обычная кнопка без фиксации, подключённая по показанной на рис. 4 схеме к одному из внешних выводов платы Arduino, в данном случае к D7. При отпущенной кнопке SB1 уровень напряжения на входе микроконтроллера будет низким (его обеспечит резистор R1), при нажатой - высоким. Если поменять кнопку и резистор местами (рис. 5), то поменяются местами и уровни. Теперь резистор R1 обеспечит высокий уровень при отпущенной кнопке, а нажатие на неё установит низкий уровень.

Схема подключения датчика

Рис. 4. Схема подключения датчика

 

Схема подключения датчика

Рис. 5. Схема подключения датчика

 

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

Учтите, что для выполнения функции резистора R1 в микроконтроллере имеются внутренние резисторы. По умолчанию они отключены. Однако, чтобы подключить, скажем, к входу D2 внутренний резистор, достаточно внести в функцию setup() строку

pinMode(2, INPUT_PULLUP);

Рассмотрим цифровой ввод на примере приведённой в табл. 4 программы, гасящей подключённый к выводу 13 светодиод при нажатии на кнопку, подключённую к выводу D7. Она основана на условном операторе

if (условие)

{

/*Действия в случае, если условие выполнено*/

}

else

{

/*Действия в случае, если условие не выполнено*/

}

 

Таблица 4

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

Собственно проверку состояния кнопки выполняет логический оператор 

digitalRead(BUT) = HIGH

В рассматриваемом случае он сравнивает значение, возвращаемое функцией чтения состояния вывода BUT, к которому подключена кнопка, с логической константой HIGH, и, если они равны, принимает значение TRUE (истина), а в противном случае - FALSE (ложь). Обратите внимание, что операция проверки равенства обозначена двумя подряд знаками равенства. А одним знаком равенства обозначают операцию присваивания значения переменной. Не путайте их, это приводит к трудно обнаруживаемым ошибкам.

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

Очень полезна способность Arduino взаимодействовать с персональным компьютером через последовательный интерфейс. Её можно использовать не только для загрузки программы в микроконтроллер, но и для двухстороннего обмена информацией в процессе её выполнения. По этому интерфейсу Arduino может передавать в компьютер собранную информацию для сложной обработки или хранения и получать от него команды и исходные данные. Так могут взаимодействовать и два микроконт-роллерных устройства. Последовательный порт микроконтроллера использует цифровые выводы платы D0 и D1, поэтому при организации и использовании связи через последовательный порт их нельзя применять ни для чего другого.

Для примера рассмотрим программу, приведённую в табл. 5, которая передаёт в компьютер информацию о состоянии вывода D12. Если уровень на нём высокий, программа посылает в компьютер код символа H, а если он низкий - код символа L. Принимать эту информацию может любая программа, способная работать с COM-портом компьютера. Среда разработки Arduino IDE имеет встроенный монитор последовательного порта, с помощью которого компьютер может выводить на экран полученные от платы Arduino текстовые сообщения и передавать ему сообщения, набранные пользователем на клавиатуре компьютера.

Таблица 5

Строка Serial.begin(9600) в функции setup() инициализирует последовательный порт микроконтроллера и устанавливает скорость передачи и приёма 9600 Бод. Можно задавать и другие стандартные значения скорости: 1200, 2400, 4800, 9600, 19200, 38400, 57600 или 115200 Бод. При этом скорость, установленная в микроконтроллере, должна совпадать со скоростью, на которую настроен COM-порт компьютера или другого устройства, с которым должен идти обмен информацией. Допустимая скорость, при которой обеспечивается надёжный приём информации, зависит от длины кабеля, соединяющего Arduino c компьютером. Например, по стандартному USB- кабелю

длиной 1,8 м компьютер примет от Arduino информа-циюдаже со скоростью 115200 Бод. А если добавить к этому кабелю пятиметровый удлинитель, допустимая скорость упадёт до 4800 Бод.

Отправляет информацию в последовательный порт функция Seri-al.print(), где в круглых скобках указывают имя переменной, значение Отправить которой нужно передать, или строку символов, подлежащих передаче. Для отличия от имени переменной строку символов заключают в кавычки. Существует модификация этой функции Serial.println(). Она отличается тем, что, передав информацию, заключённую в скобках (если она есть), дополняет её символами возврата каретки и перевода строки. Начинает новую строку и комбинация символов в передаваемой строке.

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

Далее рассмотрим программу (табл. 6), которая зажигает и гасит имеющийся на плате светодиод в соответствии с командами, поступающими от компьютера через последовательный порт. Следует иметь в виду, что информацию по последовательному порту передают байтами. Приёмник последовательного порта, работая независимо от процессора микроконтроллера, принимает эти байты и сохраняет их в своём буфере объёмом 64 байта.

Таблица 6.

Для того чтобы программа могла определить, есть ли в буфере принятые байты, имеется функция Serial.available(), возвращающая их число. Если они есть, программа с помощью функции Serial. read() читает байт из буфера и присваивает его значение (код принятого символа) переменной C типа char. Далее условные операторы сравнивают код с образцами и при совпадении включают или выключают светодиод.

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

Окно программы

Рис. 6. Окно программы

 

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

Упомянутые в статье программы для Arduino можно скачать здесь.

Продолжение следует

Автор: Д. Лекомцев, г. Орёл