Текст
                    Белов А.В.
ПРОГРАММИРОВАНИЕ
ARDUINO
СОЗДАЕМ ПРАКТИЧЕСКИЕ УСТРОЙСТВА
+ ВИРТУАЛЬНЫЙ ДИСК
Наука и Техника, Санкт-Петербург


ББК 32.812 УДК 621.314:621.311.6 Белов А.В. Программирование ARDUINO. Создаем практические устройства + виртуальный диск.- СПб.: Наука и Техника, 2018. - 272 с, илл. ISBN 978-5-94387-882-4 Книга посвящена созданию практических устройств с использованием модуля Ардуино. Этот модуль в настоящее время стал очень популярным. Он оказался настолько удачной разработкой и получил настолько широкое распространение в мире, что сегодня признан идеальной основой для изучения премудростей микроконтроллерной техники. Для данной книги автор специально разработал ряд практических схем и устройств, на основе которых читатель постепенно, от простого к сложному, сможет научиться писать программы и разрабатывать свои устройства на основе модуля Ардуино. Книга содержит подробное описание каждой включенной в нее программы. Вы узнаете как создается алгоритм, как разрабатывается схема и как пишется программа. Параллель- но, на тех же примерах, идет изучение языка программирования Ардуино. Все функции, операторы и другие элементы этого языка подробно описываются перед тем, как они будут использованы в очередной конкретной программе. Сотни тысяч плат Ардуино используются каждый день, стимулируя людей во всем мире создавать что-то новое и интересное. Книга предназначена для широкого круга радиолю- бителей и для всех, кто изучает языки программирования и учится создавать электронные устройства. Виртуальный диск содержит тексты всех программных примеров из книги, инсталляцион- ный пакет среды разработки IDE, архивы используемых в книге программных библиотек, видеоролики, набор вспомогательной справочной информации и многое другое. 9 |785943»878824 ISBN 978-5-94387-882-4 Автор и издательство не несут ответственности за возможный ущерб, причиненный в ходе использования материалов данной книги Контактный телефон издательства (812) 412-70-26 Официальный сайт: www.nit.com.ru © Белов А.В. © Наука и Техника (оригинал-макет) 000 «Наука и Техника». Лицензия № 000350 от 23 декабря 1999 года. 198097, г. Санкт-Петербург, ул. Маршала Говорова, д. 29. Подписано в печать. Формат 70х 100 1/16. Бумага офсетная. Печать офсетная. Объем 17 п. л. Тираж 1300 экз. Заказ № 1596. Отпечатано с готовых файлов заказчика в АО «Первая Образцовая типография» филиал «УЛЬЯНОВСКИЙ ДОМ ПЕЧАТИ» 432980, г. Ульяновск, ул. Гончарова, 14
СОДЕРЖАНИЕ Глава 1. Прежде чем начать читать книгу 6 Здравствуй, Ардуино! 6 Почему стал популярным модуль Ардуино? 7 Как будем осваивать язык Ардуино 8 Кто и зачем создал модуль Ардуино? 10 Глава 2. Ардуино - конструктор для домохозяек 10 Как же удалось достичь такой популярности? 11 Первые варианты Ардуино 12 Знакомимся с модулем Arduino UNO 15 Полезные упрощения в модуле 17 Группа аналоговых входов 18 Команда аналогового вывода 19 Контакты питания «Power» 21 Поддерживаемые языки программирования 21 Схема распиновки модуля Arduino UNO 23 Глава 3. Среда разработки IDE 24 Для чего нужно специальное приложение «Среда разработки Arduino IDE»? 24 Команды и функции языка Ардуино 25 Внутренние библиотеки 27 Скачиваем программный пакет с сайта разработчика 29 Варианты установочных пакетов для Windows 29 Запуск программы 31 Основное окно среды разработки 31 Панель инструментов 33 Выбор номера СОМ порта в настройках программы 34 Выбор типа используемой платы Ардуино 35 Скетч: открытие, сохранение, загрузка 36 Организация обмена информацией между программой на Ардуино и компьютером 38 Глава 4. Простейшая программа «Hello, world!» 41 Постановка задачи 41 Схема 42 Алгоритм 45 Первый вариант программы 47 Второй вариант программы 55
Глава 5. Переключаемый светодиод ♦ 64 Постановка задачи 64 Схема 64 Алгоритм 65 Первый вариант программы 65 Второй вариант программы 69 Третий вариант программы 71 Глава б. Боремся с дребезгом контактов 74 Постановка задачи 74 Схема 76 Антидребезг простыми средствами 76 Алгоритм 76 Программа 77 Применение внешней библиотеки Button 79 Метод проверки ожидания стабильного состояния сигнала .. 85 Метод фильтрации сигнала по среднему значению 85 Глава 7. Мигающий светодиод 87 Постановка задачи 87 Схема 87 Алгоритм 88 Программа 88 Глава 8. Бегущие огни 91 Постановка задачи 91 Схема 92 Алгоритм 92 Первый вариант программы 94 Второй вариант- используем один универсальный цикл 100 Глава 9. Альтернативные способы формирования задержки 104 Постановка задачи 104 Схема 106 Алгоритм 106 Первый вариант программы 107 Второй вариант программы 110 Глава 10. Работа с прерываниями по таймеру 117 Постановка задачи 117 Схема 120 Используем внешнюю библиотеку прерываний по таймеру.. 120 Алгоритм 122 Программа 122 Совместное использование таймера 0 126
Глава 11. Формирование звука 135 Постановка задачи 135 Схема 137 Алгоритм 139 Программа 139 Глава 12. Ввод аналоговой информации 144 Постановка задачи 144 Схема 145 Алгоритм 146 Программа 148 Глава 13. Вывод аналоговой информации 151 Широтно-импульсная модуляция 151 Простейший способ аналогового вывода 154 Схема 154 Алгоритм 155 Программа 156 Более сложный пример аналоговой индикации 157 Схема 158 Алгоритм 158 Программа 160 Глава 14. Передача данных из Ардуино на компьютер 166 Постановка задачи 166 Схема 167 Алгоритм 169 Программа 169 Глава 15. Передача данных с компьютера на Ардуино 178 Постановка задачи 178 Схема 179 Алгоритм 180 Программа 181 Глава 16. Музыкальная шкатулка 185 Постановка задачи 185 Схема 191 Алгоритм 191 Программа 192 Глава 17. Кодовый замок 206 Постановка задачи 206 Схема 210 Алгоритм 212 Программа 214
Глава 18. Кодовый замок с музыкальным звонком 233 Постановка задачи 233 Схема 234 Алгоритм 235 Программа 236 Глава 19. Платы Arduino: особенности и возможности 247 Arduino Due 247 Arduino Leonardo 248 Arduino Yun 248 Arduino Micro 249 Arduino UNO 249 Arduino Ethernet 249 Arduino Duemilanove 250 Arduino Diecimila 251 Arduino Nano 251 Arduino Mega 252 Arduino Mega 2560 252 Arduino ADK 253 Arduino LilyPad 253 Arduino Fio 254 Arduino Mini 254 Arduino Pro 255 Arduino Pro Mini 255 USB Serial Light Адаптер 256 Глава 20. Arduino shields или платы расширения 257 Для чего нужны платы расширения? 257 Плата расширения Arduino WiFi 258 Плата расширения Xbee Shield 258 Плата расширения Arduino Motor 259 Плата расширения Ethernet Shield 259 Глава 21. Подводя итоги 260 Приложение 1 .Основные операторы языка Ардуино 261 Главные функции 261 Управляющие операторы 262 Операторы цифрового ввода/вывода 264 Операторы аналогового ввода/вывода 265 Операторы времени 266 Расширенный ввод/вывод 267 Работа с последовательным портом 269 Приложение 2. Типы данных в Arduino IDE 270 Список литературы 271 Список ссылок на ресурсы в интернет 271
ГЛАВА 1 ПРЕЖДЕ ЧЕМ НАЧАТЬ ЧИТАТЬ КНИГУ Здравствуй, Ардуино! Уважаемый читатель! Ты держишь в руках книгу, которую можно использовать как самостоятельный самоучитель по разработке устройств на основе модулей Ардуино и программ для них. Но, в то же время, книга фактически является про- должением хорошо известной в России и странах ближнего зарубежья другой книги авторства Александра Белова, которая называется: «Микроконтроллеры AVR: от азов программирования до создания практических устройств». ПРИМЕЧАНИЕ. Это позиция [1] в списке литературы в конце данной книги. Те читатели, которые уже прочитали книгу [1], знают, что она дает основы знаний по цифровой и микропроцессор-
8 Программирование ARDUINO. Создаем практические устройства ной технике, начиная от азов и заканчивая рядом примеров по разработке конкретных несложных микропроцессорных устройств. Изучение материала в [1] происходит на примере микро- контроллеров AVR фирмы Atmel. При этом уроки программи- рования даются сразу на двух языках: ♦ языке Ассемблера; ♦ языке СИ. Однако, после выхода книги [1] в мире получил широ- кое распространение микроконтроллерный модуль Ардуино. Разработка устройств на основе этого модуля и разработка программ для них — это новое, оригинальное, бурно разви- вающееся направление, ориентированное на начинающих и самодеятельных конструкторов. Это подвигло автора написать НОВУЮ книгу, которую можно считать как бы продолжением к известной книге [1]. ПРИМЕЧАНИЕ. В новой книге на тех же примерах, на которых в [1] читатель постигал секреты конструирова- ния и программирования классическими метода- ми, автор решил рассказать, как это можно сде- лать средствами Ардуино. В то же время, решено было построить НОВУЮ книгу так, чтобы читатель мог пользоваться ей как абсолютно самостоя- тельным самоучителем по Ардуино. I Почему стал популярным модуль Ардуино? Модуль Ардуино — это небольшая плата, собранная на микроконтроллере AVR, которая служит основой для создания
Глава 1. Прежде чем начать читать книгу любых электронных устройств управления, сигнализации и автоматики, которые могут применяться в самых разных обла- стях электронной техники. ПРИМЕЧАНИЕ. Ардуино является отличной альтернативой для начинающих разработчиков благодаря тому, что в модуле использован ряд оригинальных решений упрощающих разработку как схем, так и программ. Модуль не требует отдельного программатора и предостав- ляет разработчику простые средства обмена информацией с компьютером по последовательному USB каналу. Ардуино оказался настолько удачной разработкой и получил настолько широкое распространение во всем мире, что теперь именно он считается самой удачной основой для изучения азов микро- контроллерной техники. Как будем осваивать | язык Ардуино 1 Книга, которую вы держите сейчас в руках, содержит набор специально разработанных примеров, на основе которых чита- тель постепенно, от простого к сложному, учится разрабатывать схемы и создавать программы с использованием модуля Ардуино. ПРИМЕЧАНИЕ. За основу взяты примеры из [1], которые допол- нены целым рядом дополнительных примеров, ил- люстрирующих либо различные способы решения одной и той же задачи, плюс несколько примеров, решающих абсолютно новые задачи.
10 Программирование ARDUINO. Создаем практические устройства Каждый пример снабжен: ♦ подробным описанием решаемой проблемы; ♦ четко сформулированной постановкой задачи; ♦ описанием алгоритма; ♦ подробным описанием схемы и готовой программы. Традиционно, в книге вы увидите очень подробное описа- ние каждой программы. Параллельно идет изучение языка программирования Ардуино. Все функции, операторы и другие элементы языка программирования подробно описываются перед тем, как они будут использованы в очередной конкретной программе. Тем читателям, кто прежде, чем начать изучение данной книги, все-таки решил предварительно изучить книгу [1], сле- дует знать, что вместо книги [1], с таким же успехом, можно использовать одно из предыдущих ее изданий. В списке лите- ратуры, приведенном в конце данной книги, вы найдете все эти издания. А на обратной стороне книги вы увидите обложку книги [1] (издание 2-е). СОВЕТ. Использовать можно позиции [2], [3], [4], [5], [6], [7] или [8] списка. Конечно, лучше всего использо- вать самое последнее издание, то есть книгу [1]. Но если у вас уже есть другая книга из приведен- ного выше списка, то она вполне подойдет в том случае, если вас интересует в конечном итоге ра- бота с Ардуино. При этом можно даже опустить в [1] главу 6.
ГЛАВА 2 АРДУИНО - КОНСТРУКТОР для домохозяек Кто и зачем создал | модуль Ардуино? II Модуль Ардуино придумали пятеро друзей из малень- кого итальянского городка Ивреа, стоящего на реке Дора Балтея. Городок знаменит своими королями-неудачниками. В 1002 году король по имени АРДУИН стал правителем страны, но через два года был свергнут королем Германии Генри И. Сегодня есть бар ди Ре Ардуино, расположенный в истори- ческой части этого городка. Назван бар в честь короля и стоит на том самом месте, где, по преданию, родился король. Бар является пивнушкой Массимо Банци (Massimo Banzi), итальян- ского соучредителя уникального проекта в сфере электроники, который был назван в честь этого места. Появился модуль в 2005 году. Создателей Ардуино звали так: ♦ Дэвид Куар-тилльз (David Cuartielles); ♦ Джанлука Мартино (Gianluka Martino); ♦ Том Иго (Tom Igoe); ♦ Дэвид Мелис (David Mellis); ♦ Массимо Банци (Massimo Banzi). Эта компания из пяти единомышленников разработала учебное пособие для студентов Института проектирования
12 Программирование ARDUINO. Создаем практические устройства взаимодействий города Ивреа. При помощи этого модуля сту- денты изучали микроэлектронику. ПРИМЕЧАНИЕ. ■ Название модуля было придумано позже по име- ни упонянутого ранее бара «Ардуино», в котором любили собираться друзья и обсуждать там все свои идеи. Модуль получился настолько удачным, что моментально стал известен во всем мире! Ардуино — недорогая микрокон- троллерная плата, которая позволяет даже новичку делать по-настоящему удивительные вещи. |Как же удалось достичь такой популярности? Прежде всего, разработчики Ардуино (первоначально он вообще не имел названия, но будем называть его так) решили упростить процесс прошивки программ в программную память микроконтроллера. Традиционно, для прошивки микроконтрол- леров использовались отдельные устройства — программаторы. Программатор подключается к компьютеру, а уже к про- грамматору подключается микроконтроллер. Разработчики решили избавиться от такого вспомогательного устройства, как программатор. Решение напрашивалось само собой. Большинство выпускаемых микроконтроллеров к тому вре- мени уже имело такую полезную функцию, как возможность самопрограммирования. Для этого программная память микроконтроллера раз- бивалась на две области соответствующими настрой- ками (точнее установкой его битов конфигурации FUSE- переключателями):
Глава 2. Ардуино - конструктор для домохозяек 13 ♦ часть памяти выделялась под область загрузчика; ♦ оставшаяся же память использовалась для размещения ту- да основной рабочей программы. ЧТО ЕСТЬ ЧТО, 1 N Область загрузчика - это защищенная область программной памяти. Программный код, записан- ный в эту область, не может быть переписан са- мопрограммированием. Такая конфигурация программной памяти позволяла про- граммисту поместить в область загрузчика программу, кото- рая в нужный момент, используя любые внешние каналы связи микроконтроллера, будет: ♦ получать извне коды нового варианта рабочей программы; ♦ прошивать эти коды в основную часть программной памяти. Подробнее об этом вы можете прочитать в [1]. Программа- загрузчик обычно разрабатывается программистом, и алгоритмы ее работы зависят от его квалификации и фантазии. В настоящее время технология самопрограммирования широко используется в самых разных электронных устройствах. Она позволяет заливать обновленные версии программ, используя самые разные источ- ники данных. Например, Интернет, компьютер и т. п. Первые варианты II Ардуино II Самые первые варианты Ардуино представляли собой плату, на которую был установлен микроконтроллер (исполь- зовались микроконтроллеры AVR) и элементы самой необхо- димой внешней обвязки: ♦ цепи сброса; ♦ внешний кварцевый резонатор;
14 Программирование ARDUINO. Создаем практические устройства ♦ стабилизатор напряжения; ♦ несколько светодиодов для индикации питания и сигналов канала связи. С компьютером первые Ардуино связывались при помощи последовательного порта. В микроконтроллере для этого исполь- зовался встроенный канал UART, а в компьютере — СОМ порт. ПРИМЕЧАНИЕ. Оба этих стандарта основаны на едином прото- коле RS-232. Но UART отличается от СОМ уров- нями сигнала. Последовательный канал в микроконтроллере работает с сигналом, принимающим значения: О В и +5 В. В стандарте СОМ используются уровни плюс 12 В и минус 12 В. Поэтому на плате Ардуино присутствовала схема преоб- разования уровней. На рис. 2.1 изображен один из первых вариантов Ардуино, использующих СОМ порт для связи с компьютером. ПРИМЕЧАНИЕ. Как известно, СОМ порты в настоящее время уже полностью вытеснены таким универсаль- ным стандартом, как USB. Поэтому и Ардуино пришлось приспосабливаться. На платах Ардуино появились микросхемы с адаптерами USB-COM. В одном из вариантов маленький модуль с преобра- зователем USB-COM выполнялся как отдельный модуль, кото- рый после загрузки и отладки программы в основную плату снимался и мог быть использован для отладки и прошивки в других разработках.
Глава 2. Ардуино - конструктор для домохозяек 15 Рис. 2.1. Модуль Ардуино для СОМ порта Рис. 2.2. Модуль Arduino UNO
16 Программирование ARDUINO. Создаем практические устройства В настоящее время все модели Ардуино снабжены USB пор- тами. На сегодняшний день в мире разработано и произво- дится большое количество самых различных моделей Ардуино. На рис. 2.2 изображена плата одного из вариантов модуля, который носит название «Arduino UNO». Это довольно удач- ная модель, которая получила довольно широкое распростра- нение по всему миру, в том числе и в России. Ее мы и будем использовать в качестве примера в нашей книге. I Знакомимся с модулем Arduino UNO Для того чтобы понять, какие преимущества дает начи- нающему разработчику электронных устройств архитектура модуля Ардуино, рассмотрим модель Arduino UNO подробнее. Существует несколько версий Arduino UNO. Они отличаются по: ♦ схеме; ♦ размещению некоторых деталей на плате; ♦ типу корпуса микросхем. Но все они полностью совместимы между собой и взаимо- заменяемы. На рис. 2.2 показан внешний вид одного из вариантов Arduino UNO. В основе этой модели модуля лежит микрокон- троллер ATmega328P. При этом существует две разновидности: ♦ вариант на микросхеме в DIP корпусе (как раз его мы ви- дим на рис. 2.2); ♦ вариант на микросхемах в SMD корпусе. Разные модели также отличаются типом преобразователя USB-COM: ♦ в одних вариантах используется специализированная ми- кросхема CH340G; ♦ в других же (как на рис. 2.2) в качестве преобразователя используется микроконтроллер Atmegal6U2 с соответству- ющей программой, зашитой в ее программную память.
Глава 2. Ардуино — конструктор для домохозяек 17 ПРИМЕЧАНИЕ Но какой бы вариант не использовался, определен- ные параметры в модуле всегда остаются неиз- менными. Во-первых, это установочные и габа- ритные размеры модуля. Во-вторых, расположение и функциональное назначение всех его выводов. Выводы Arduino UNO выполнены в виде разъемов, выстро- енных в две цепочки гнезд, расположенные по краям платы. К таким разъемам удобно подключать внешние элементы. При желании, например, светодиод можно просто вставить в нуж- ные гнезда разъема. На своих строго определенных местах установлены: ♦ разъем внешнего питания; ♦ разъем USB. Чаще всего в модулях Ардуино (в том числе и в Arduino UNO) используются аппаратные USB розетки, такие же, какие используются в современных принтерах. Поэтому для подклю- чения модуля Ардуино к компьютеру можно использовать USB кабель от принтера. Кроме разъемов всегда находятся на стандартных местах следующие элементы: ♦ кнопка сброса (Reset); ♦ светодиоды, предназначенные для индикации. На плате Arduino UNO имеется четыре светодиода. Светодиод индикации питания загорается всегда, когда на шине питания модуля появляется напряжение +5 В. Напряжение может поступать как от порта USB, так и от разъ- ема внешнего питания. Два светодиода подключены параллельно линиям вну- треннего СОМ порта, то есть к линиям, идущим от преобразо- вателя USB-COM к основному микроконтроллеру модуля: ♦ один из этих светодиодов индицирует процесс передачи информации от компьютера в модуль Ардуино; ♦ второй светодиод индицирует процесс передачи информа- ции от микроконтроллера в компьютер.
18 Программирование ARDUINO. Создаем практические устройства Четвертый светодиод подключен к выводу РВ5 порта РВ основного микроконтроллера ATmega323 и предназначен для использования программистом по своему усмотрению. I Полезные упрощения в модуле Как уже говорилось выше, главным преимуществом про- екта Ардуино является его простота в понимании и использо- вании. Для того чтобы работа с Ардуино стала доступнее, авто- рами был придуман целый ряд упрощений. И одно из них — сквозная нумерация линий ввода- вывода. Так, все цифровые контакты объединены в одну общую группу и пронумерованы, как контакт 0, контакт 1 и так далее до 13 (см. рис. 2.2). ПРИМЕЧАНИЕ. На самом деле к контактам 0...7 подключены раз- ряды D0...D7 порта PD микроконтроллера, а к контактам 8...13 - разряды В0...В5 порта РВ. Однако на языке программирования, который использу- ется для создания программ в модуле Ардуино, обращение к любому цифровому контакту производится по его номеру. Например, для чтения уровня сигнала на цифровом кон- такте используется команда digitalRead (НомерКонтакта). Например: а = digitalRead(O); или b = digitalRead(13); Вывод уровней на любой цифровой контакт тоже очень прост. Для перевода контакта в режим вывода информации используется команда: pinMode (НомерКонтакта, OUTPUT);
Глава 2. Ардуино - конструктор для домохозяек 19 Второй параметр этой функции имеет значение OUTPUT. Служебное слово OUTPUT настраивает контакт на вывод информации. Затем, для вывода на контакт нужного цифро- вого уровня используется команда: digitalWrite (НомерКонтакта, Значение); Параметр «Значение» может принимать величину: ♦ или HIGH (высокий логический уровень); ♦ или LOW (низкий логический уровень). ПРИМЕЧАНИЕ. Также можно задавать значение и в цифровом виде: 1 или 0. Группа аналоговых II входов II Кроме группы цифровых контактов («пинов») в Ардуино имеется группа так называемых аналоговых входов. Это шесть контактов, расположенных на противоположном «борту» модуля. В Ардуино аналоговые входы называются АО, А1,... А5 (см. рис. 2.2). ПРИМЕЧАНИЕ. На самом деле аналоговыми эти входы можно назвать условно. В качестве аналоговых входов используются разряды РС0...РС5 порта PC микро- контроллера. То есть, это обычные разряды пор- та, которые могут служить и цифровыми входа- ми, и цифровыми выходами.
20 Программирование ARDUINO. Создаем практические устройства Но альтернативной функцией именно этих шести выводов микроконтроллера ATmega328P является то, что они могут слу- жить входами внутреннего АЦП. Для упрощения эти входы объявлены аналоговыми. И это в Ардуино основной режим их работы. В языке программирования Ардуино для того, чтобы про- читать уровень аналогового сигнала с любого аналогового входа, нужно всего лишь дать одну простую команду: analogRead(); Например, следующая строка программы прочитает уро- вень аналогового сигнала с входа АО: с = analogRead (АО); Очевидно, что любой аналоговый вход всегда можно исполь- зовать как цифровой вход или выход. Это позволяет архитектура микроконтроллера. В языке Ардуино учитывается и эта возмож- ность. На этот случай выводы А0...А5 имеют альтернативную нуме- рацию. В командах цифрового ввода и цифрового вывода к ним можно обращаться как к контактам 14...19. Например, командой d = digitalRead(14); вы можете прочитать цифровой уровень (HIGH или LOW) с «аналогового входа» АО. I Команда аналогового вывода Еще одной интересной находкой, упрощающей програм- мирование на языке Ардуино, является команда аналогового вывода. Выглядит эта команда следующим образом: analogWrite (НомерКонтакта, Значение); Несмотря на то, что команда выводит некий уровень, назы- ваемый аналоговым, выводимый этой командой сигнал не
Глава 2. Ардуино - конструктор для домохозяек 21 совсем аналоговый. И выводить «аналоговый сигнал» можно как раз только на цифровые выводы, и то не на все. ПРИМЕЧАНИЕ. Дело в том, что «аналоговый» сигнал выводит- ся при помощи широтно-импульсной модуляции (ШИМ). На плате Ардуино контакты, при помощи которых можно производить «аналоговый» вывод информации, обозначены значком «~». Это тоже связано с архитектурой микроконтрол- лера ATmega328P. ПРИМЕЧАНИЕ. Подобный «аналоговый» вывод возможен только на те разряды портов ввода/вывода, которые поддер- живают ШИМ. Поэтому параметр НомерКонтакта в модуле Arduino UNO может принимать только следующие значения 3,5,6,9,10,11. В более ранних версиях аналоговый выход был возмо- жен только на контактах 9,10,11. Параметр «Значение» может лежать в диапазоне O...255. Как уже говорилось, команда analogWrite() вызывает на выходе «НомерКонтакта» появление прямоугольных импуль- сов. Частота импульсов приблизительно равна 490 герц. Длительность зависит от параметра «Значение». Чем больше значение, тем больше длительность импульса. Если «Значение» = 0, импульсы прекращаются (импульсы нулевой длины), и на выходе устанавливается логический ноль. Если «Значение» = 255, то ширина импульсов становится равной их периоду. Поэтому импульсов также не будет, и на выходе установится постоянное значение логической единицы.
К) ю 2.1м | Внешнее питание 7...12В О Только для R3 —.рсо —iPC2 --РС4 -—«PCS t Выводы МС ATmega328 Выводы MCATmega328 Рис. 2.3. Схема распиновки модуля Ардуино О ■а ш 2 "О о го QJ Л> 9 I тз ш q
Глава 2. Ардуино - конструктор для домохозяек 23 Контакты питания | «Power» I Кроме вышеописанных контактов, на плате Ардуино есть группа контактов под общим названием «Power» (питание). Сюда выведены: шина питания +5 В; опорное напряжение 3,3 В; контакт общего провода; сигнал сброса. Так как большинство контактов модуля Ардуино напря- мую подключены каждый к своему выводу микроконтроллера ATmega328P, для каждого из контактов модуля дублируются все альтернативные функции, характерные для соответствующего вывода микроконтроллера. Поддерживаемые | языки программирования | Разработчики предусмотрели возможность при необходи- мости использовать все возможности микроконтроллера. Для этого, кроме специализированного языка программирования Ардуино, модуль поддерживает и классический язык C++. Для тех, кто изучил язык СИ, описанный в книге [1], не составит труда перейти на язык C++ Ардуино. Эти языки очень похожи. На рис. 2.3 изображена схема распиновки модуля Arduino UNO, где для каждого контакта указаны все поддержи- ваемые им функции. Это поможет вам разобраться с дополни- тельными функциями каждого контакта.
ГЛАВА 3 СРЕДА РАЗРАБОТКИ IDE |Для чего нужно специальное приложение «Среда разработки Arduino IDE»? Специальное приложение для компьютера, которое назы- вается «Среда разработки Arduino IDE», нужно для таких целей: ♦ составлять программы для Ардуино; ♦ сохранять программы на компьютере; ♦ транслировать и загружать программы в программную па- мять модуля. ПРИМЕЧАНИЕ. Это приложение полностью бесплатно, и его можно свободно скачать с сайта разработчика (https://www.arduinoxc). Существуют версии для всех основных операционных систем (Windows, Mac X, Linux). Среда разработки включает в себя специализированный текстовый редактор, при помощи которого вы можете писать
Глава 3. Среда разработки IDE 25 программы для Ардуино. Для написания программ использу- ется специальная версия языка программирования, кото- рую мы будем называть далее «Язык Ардуино». ЧТО ЕСТЬ ЧТО. Программа, написанная на этом языке в среде IDE, называется Скетч и записывается на жест- кий диск компьютера в виде файла с расширени- ем то. Программа может состоять из нескольких файлов ino, состав- ляющих проект. Один из файлов проекта считается главным. ВНИМАНИЕ! Все файлы проекта (даже если в проекте один файл) должны размещаться в отдельной дирек- тории, имя которой должно совпадать с именем главного файла проекта. Команды и функции | языка Ардуино 1 Мы уже познакомились с несколькими командами и функ- циями языка Ардуино. Простота обучения и использования Ардуино в значительной степени обусловлена эффективно- стью его языка программирования. ПРИМЕЧАНИЕ. Список основных команд и функций языка Ардуино приведен в Приложении 1.
26 Программирование ARDUINO. Создаем практические устройства Язык Ардуино является клоном языка СИ, и его синтак- сис максимально приближен к синтаксису этого популярного языка программирования. СОВЕТ. В том случае, когда при программировании вам не хватает набора команд языка Ардуино, вы мо- жете использовать команды языка C++. При трансляции разработанной вами программы, создан- ной в среде разработки Ардуино, создается машинный код, который, кроме команд заложенных программистом, содержит ряд системных процедур. Одна из таких процедур — внутренние системные часы. При запуске на Ардуино любой вашей программы параллельно запускаются эти часы. Они работают все время, пока работает ваша программа, и постоянно отсчитывают количество милли- секунд, прошедших с момента запуска программы. Для этого используется системный таймер 0 микрокон- троллера и прерывание по переполнению этого таймера. ПРИМЕЧАНИЕ. Подробнее мы опишем этот процесс в главе 10. Этот таймер используется, например, для работы функций millisQ или micros()> возвращающих значение счетчика вре- мени в миллисекундах и в микросекундах, соответственно. Кроме системного таймера, программа на Ардуино органи- зует еще несколько вспомогательных процессов. Например, таймер 1 микроконтроллера используется для команд аналогового вывода на контакты 3, 5, 6, а таймер 2 для аналогового вывода на контакты 9,10,11 и для формиро-
Глава 3. Среда разработки IDE 27 вания звуковых сигналов. Для этого таймеры переводятся в режим ШИМ. Некоторые ресурсы микроконтроллера система програм- мирования самостоятельно использует для своих систем- ных нужд. Поэтому программист, создающий программы на языке Ардуино, уже не может распоряжаться ресурсами микроконтроллера так же свободно, как он может это делать в программах на классическом языке СИ. Зато такое реше- ние облегчает программирование для начинающих про- граммистов. Язык Ардуино позволяет не утруждать себя подробным изу- чением всех сложных режимов работы таймеров, прерываний, выбором нужных режимов, определением того, какие коды и в какие системные регистры записать для того, чтобы правильно выбрать нужный режим. Язык Ардуино все это делает сам. ПРИМЕЧАНИЕ. Главный минус подобного решения состоит в том, что это сужает гибкость готовой про- граммы. Допуская вольное использование ресурсов нужно всегда помнить, что тем самым вы мешаете языку Ардуино выпол- нять те либо иные команды. Например, используя самостоятельно для своих нужд тай- мер 1, вы уже не сможете осуществлять аналоговый вывод для контактов 3,5,6. Внутренние | библиотеки I Набор стандартных команд Ардуино можно существенно расширить применением внешних библиотек. Пакет среды
28 Программирование ARDUINO. Создаем практические устройства разработки Ардуино имеет довольно большой набор внешних библиотек для самых различных задач. ПРИМЕР. Существует Библиотека для работы с внутрен- ней долговременной памятью данных, или библи- отека для работы с последовательным каналом CPI. Есть библиотека для работы с Ethernet, Wi-Fi, GSM, внешним SD модулем памяти и многое другое. СОВЕТ. Если нужная библиотека отсутствует в стан- дартном пакете среды разработки, вы можете использовать библиотеку сторонних разработ- чиков. Огромное количество таких библиотек можно свободно скачать из сети Интернет. Например, существует библиотека, позволяющая использо- вать прерывания по таймеру. Набор внешних библиотек в про- екте Ардуино просто огромен. Совместными усилиями разра- ботчиков и энтузиастов проекта разработаны библиотеки на все случаи жизни. После присоединения к вашей программе нужной вам библиотеки в вашем распоряжении появляется набор новых функций, которые дают вам новые нужные вам возможности. Вы можете самостоятельно разработать свою собственную библиотеку и применять ее в своих программах. Далее, по мере изучения разных примеров в нашей книге, мы будем часто использовать внешние библиотеки.
Глава 3. Среда разработки IDE 29 Скачиваем программный пакет | с сайта разработчика II Для установки среды программирования Ардуино на ваш компьютер нужно сначала скачать программный пакет с сайта разработчика (arduino.cc). Точный адрес ссылки на страницу загрузки вы можете посмотреть в списке ссылок в конце книги. Сайт англоязычный. Его удобно просматривать при помощи браузера с переводчиком. Например, в Яндекс-браузере. Находясь на сайте, выберите в главном меню пункт Software (Программное обеспечение). На открывшейся по этой ссылке странице вы увидите раздел «Download the Arduino IDE» (Скачать Arduino IDE). ПРИМЕЧАНИЕ. В момент написания книги (январь 2018 года) но- мер последней стабильной версии среды разра- ботки -1.8.5. Справа от описания последней версии в синем прямоуголь- нике вы увидите набор ссылок на разные варианты пакета для разных операционных систем. Варианты установочных пакетов | для Windows II Для Windows, например, на сайте имеется три варианта установочных пакетов. Вариант №1. Windows Installer — пакет с инсталлято- ром. Представляет собой исполняемую программу (с расши- рением .ехе). При запуске инсталлятора он сам автоматически устанавливает все компоненты на ваш компьютер. В том числе и драйвер USB интерфейса для платы Ардуино.
30 Программирование ARDUINO. Создаем практические устройства Вариант №2. Windows ZIP file for поп admin install — пакет в виде ZIP архива. В архиве находится папка с именем «arduino-x.x.x», где х.х.х — это номер версии программного пакета. Папка содержит все файлы, составляющие пакет среды раз- работки IDE. Для установки среды вам нужно извлечь папку со всем ее содержимым из ZIP-архива и поместить в любом удоб- ном для вас месте на жестком диске вашего компьютера. После этого вам вручную придется установить драйвер USB интерфейса. Для этого вы просто подключаете модуль Ардуино к USB порту компьютера. Компьютер обнаружит USB устрой- ство и попытается установить драйвер самостоятельно. Ему это не удастся, и он выведет на дисплей стандартное окно, сообща- ющее, что драйвер установить не удалось. В этом окне нужно: ♦ выбрать команду «Установить драйвер с известного устройства»; ♦ выбрать и указать файл драйвера и нажать кнопку «Уста- новить». ПРИМЕЧАНИЕ. Драйвер вы найдете в папке «arduino-x.x.x» в под- папке «drivers». Там вы увидите несколько вариан- тов драйвера. Выберите тот, который подходит для вашей системы. Программа установки «увидит» только тот вариант, кото- рый подходит для вашего случая. Вариант №5. Windows app Get — версия для Windows 10. Эта версия также выполнена в виде ZIP-архива и устанавлива- ется аналогично предыдущей.
Глава 3. Среда разработки IDE 31 Запуск программы После установки драйверов запустите программу. Если вы использовали для установки ZIP-архив, то на рабочем столе у вас будет отсутствовать иконка для запуска программы. В этом случае вы должны знать, что запускать нужно файл arduino.exe в папке arduino-x.x.x. СОВЕТ. Для удобства дальнейшей работы вам реко- мендуется самостоятельно создать иконку для запуска этой программы на рабочем столе. Для этого проще всего сначала щелкнуть правой кнопкой мышки по файлу программы (arduino.exe) и выбрать пункт меню «Копировать». А затем щелкнуть правой кнопкой мыши в любом месте рабочего стола и выбрать пункт «Вставить ярлык». Основное окно среды | разработки II После запуска программы откроется основное окно среды разработки (см. рис. 3.1). Основное окно имеет два встроен- ных окна: ♦ строка меню; ♦ панель инструментов. В центре основного окна программы находится окно редактора для написания текста программ. Текст программы пишется на белом фоне. Редактор имеет подсветку синтаксиса. Поэтому разные элементы текста автоматически раскрашиваются разными
32 Программирование ARDUINO. Создаем практические устройства Рис. 3.1. Основное окно среды разработки IDE цветами. Например, выделяются элементы текста такими цве- тами: ♦ управляющие слова языка программирования — красным; ♦ зарезервированные константы — голубым; ♦ тексты примечаний — серым; ♦ основной текст программы — темно-синим. Как уже говорилось, файл с текстом программы называется «скетч». Сразу под окном редактора программ находится дру- гое узкое окошко, предназначенное для вывода системных сообщений. Сюда выводятся сообщения при трансляции программы и записи ее в модуль Ардуино. Сюда же выводятся все сообщения об ошибках. Сообщения в этом окне выводятся белым цветом на черном фоне. Сообщения об ошибках выделяются красным. В верхней части окна системных сообщений можно видеть поле заголовка.
Глава 3. Среда разработки IDE 33 ПРИМЕЧАНИЕ. В обычном состоянии поле заголовка голубого цвета. В поле заголовка в процессе работы выводится сообщение о текущем режиме работы IDE (проверка, трансляция, загрузка и т. д.). ВНИМАНИЕ. Если процесс трансляции программы закончится ошибкой, то поле заголовка становится желтым. В нем появляется сообщение о том, что произо- шла ошибка, а также появится кнопка, при помо- щи которой вы можете распечатать все содержи- мое из окна системных сообщений на принтере. Содержимое окна будут содержать как сообщения, обычно выводимые при трансляции, так и все сообщения об ошибках. Полный текст сообщений значительно больше, чем то, что вы видите в окне сообщений. Весь текст вы можете просмотреть также, пролистав это окно при помощи полосы прокрутки. В самой верхней части основного окна среды разработки расположено главное меню программы. При помощи этого меню можно производить все операции управления средой IDE. Панель | инструментов II Сразу под строкой основного меню расположена панель инструментов. На этой панели расположено несколько ико- нок, которые дублируют самые важные функции основного меню. При наведении курсора мыши на любую из этих ико-
34 Программирование ARDUINO. Создаем практические устройства нок, справа сразу за последней иконкой появляется подсказка с пояснением функции, на которую указывает мышь. В табл. 3.1 приведено назначение всех элементов панели управления. Таблица 3.1. Назначение элементов панели инструментов Иконка 0 Название Проверить Загрузить Новый Открыть Сохранить Монитор порта Команда меню Скетч/Проверить Скетч/Загрузить Файл/Новый Файл/Открыть Файл/Сохранить Инструменты/ Монитор порта Описание Проверка синтаксиса программы Загрузка программы в память модуля Ардуино Создание нового файла программы (новый скетч) Открыть файл программы (скетч) Записать файл программы (скетч) на диск Открыть окно монитора порта I Выбор номера СОМ порта в настройках программы Прежде чем начать работу в среде разработки, нужно пра- вильно выбрать номер СОМ порта в настройках программы. ПРИМЕЧАНИЕ. Драйвер USB модуля Ардуино работает в режиме эмуляции СОМ порта. При установке драйвера он создает в списке устройств компьютера вирту- альный СОМ порт. Номер создаваемого порта вы- бирается автоматически в процессе установки. В зависимости от конфигурации системы номер может быть разный. По умолчанию среда разработки настроена на порт С0М1.
Глава 3. Среда разработки IDE 35 Для того, чтобы узнать правильный номер порта, нужно открыть диспетчер устройств Windows (Компьютер/Свойства/ Диспетчер устройств). Откроется окно диспетчера (см. рис. 3.2). В списке устройств откройте ветку «Порты СОМ и LPT». Среди СОМ портов должен появиться порт Ардуино (на рис. 3.2 мы обвели эту запись серой линией). ВНИМАНИЕ. Учтите, что порт в этом списке виден только при подключенном к компьютеру модуле Ардуино. При отключении Ардуино порт в окне диспетче- ра исчезает. Запомните номер СОМ порта. Затем зайдите в настройки среды разработки: меню «Инструменты/Порт». При выборе этого пункта меню вы увидите список всех имеющихся в вашем компьютере последовательных портов. Выберите нуж- ный порт из списка. Выбор типа используемой | платы Ардуино II Кроме номера порта, вы должны выбрать тип вашей платы Ардуино. Для этого выберите пункт меню «Инструменты/ Плата». Появится список всех поддерживаемых плат. Выберите из списка вашу плату. Например «Arduino Uno». После того как вы выберите порт и плату, в самой нижней части окна среды разработки (в строке состояния основного окна) вы увидите надпись: «Arduino/Genuino Uno на С0М1». Теперь все готово к работе.
36 Программирование ARDUINO. Создаем практические устройства ,q5 Диспетчер устройств ^\':''-:);у [ Файл Действие Вид Справка гг РйРй i m :"<ф —. t v D'v 10! Б а Вы Д1- Кл Ко Ко Mi '0 и CD- i А ТА/ А гарей десздзп ивовые \атщ-р ■мпьюте :НГрО.ПЛ« эттгсрь ROM дис ТАР! кон^ теры устройст Р iptoi USB КСвОДЫ грсл.дерЬ1 'Ей "' "v Перт принтера (IPTi) r? Последовательный порт (С0М1) D Процессоры »> С i: т е-а ы е ада и т ер ы '* Системные усфойсгвэ *^ У<тр>",йсг8а обработки изображений Рис. 3.2. Порт Ардуино в окне диспетчера устройств ЧТО ЕСТЬ ЧТО. Genuino - это проект, аналогичный Ардуино. Он разработан другим производителем, но эти два проекта совместимы на уровне среды разработ- ки IDE. I Скетч: открытие, сохранение, загрузка При каждом последующем запуске среды разработки в окно редактора программ загружается последний редактиру- емый при предыдущем запуске программы скетч. Если среда разработки запускается впервые, в окне редактора программ создается новый пустой не именованный скетч.
Глава 3. Среда разработки IDE 37 ПРИМЕЧАНИЕ. После написания или редактирования текста программы скетч можно записать на жесткий диск при помощи команды меню «Файл/Сохранить» или «Файл/Сохранить как». Прочитать уже записанный файл можно при по- мощи команды «Файл/Открыть». Когда программа написана, ее можно оттранслировать и загрузить в память модуля Ардуино. Для этого нужно просто выбрать пункт меню «Скетч/Загрузка» или щелкнуть по соот- ветствующей иконке на панели инструментов. При выборе команды «Загрузка» скетч сначала записы- вается на жесткий диск, затем начнется процесс проверки и трансляции программы. По окончании транслирования среда IDE сразу же начинает передавать оттранслированную про- грамму в модуль Ардуино посредством USB интерфейса. ПРИМЕЧАНИЕ. При этом модуль Ардуино должен быть подклю- чен к компьютеру и готов к работе. Процесс трансляции идет достаточно продолжительное время. Если все пройдет нормально, то по окончании транс- ляции и загрузки в заголовке окна системных сообщений вы увидите надпись: «Загрузка окончена». А в самом окне увидите сообщение о результатах трансляции.
38 Программирование ARDUINO. Создаем практические устройства ВНИМАНИЕ! При заливке программ из компьютера в модуль Ардуино используются цепи, связанные с цифро- выми контактами 0 и 1. Поэтому в момент за- грузки на эти контакты нельзя подавать ника- кие сигналы, нельзя замыкать их или как-то по- другому мешать прохождению сигналов. СОВЕТ. Лучше всего, по возможности, вообще не исполь- зовать цифровые контакты 0 и 1. Сразу после загрузки программы в модуль Ардуино она автоматически начинает выполняться. При этом выполнение программы тут же прекращается, как только вы начнете новый процесс заливки программы из компьютера в модуль. Главное не забывать, что цифровые контакты 0 и 1 модуля не должны быть заблокированы. I Организация обмена информацией между программой на Ардуино и компьютером Удобной особенностью Ардуино является простой спо- соб организации обмена информацией между программой на Ардуино и компьютером с использованием USB интер- фейса. Ряд несложных команд, описанных в Приложении 1 (раз- дел «Работа с последовательным портом»), позволяют вашей программе передавать результаты своей работы в компьютер и принимать данные из компьютера. При этом среда разра- ботки IDE имеет специальные средства для того, чтобы при- нимать и отображать информацию, поступающую по каналу
Глава 3. Среда разработки IDE 39 USB от модуля Ардуино, а также, наоборот, передавать дан- ные в Ардуино. Эти средства называются «Монитор порта» и «Плоттер по последовательному соединению». Рассмотрим работу этих двух функций среды IDE по порядку. Монитор порта. Отображает поступающие через USB порт данные в текстовом виде. Запускается монитор из меню «Инструменты/Монитор порта» или при помощи соответству- ющей иконки на панели инструментов. После запуска мони- тора открывается специальное окно. В окне два поля. Верхнее поле — узкое в одну строку. В него вы можете ввести с клавиатуры данные для передачи в модуль Ардуино. В конце этого поля имеется кнопка «Отправить». При нажатии на эту кнопку все набранные данные отправляются по каналу USB. ПРИМЕЧАНИЕ. Разработанная вами программа, работающая в модуле Ардуино, должна принять эти данные и обработать так, как вы задумали. Второе большое поле в окне монитора порта сразу после запуска монитора работает на прием данных. Все поступив- шие от модуля Ардуино данные сразу же отображаются на экране. Монитор порта предполагает, что передаваемые и при- нимаемые данные — это символьные строки. Плоттер по последовательному соединении^ Плоттер вызывается аналогично монитору при помощи команды меню: «Инструменты/ Плоттер по последовательному соединению». Окно плоттера похоже на окно монитора, но в нем имеется только одно большое поле. И это поле отображает поступаю- щие из Ардуино по USB порту данные в графическом виде.
40 Программирование ARDUINO. Создаем практические устройства ПРИМЕЧАНИЕ. Монитор порта и Плоттер по последовательно- му соединению предназначены лишь для отладки алгоритмов передачи и приема данных. Для практического применения вам придется разрабо- тать специализированные программы для компьютера, которые будут работать в паре с программами, загружен- ными в модуль Ардуино. Это позволит, например, создавать на Ардуино систему сбора информации с разных датчиков (один из вариантов — пункт метеонаблюдения), и передавать эту информацию в компьютер. А компьютер сможет обрабатывать и систематизировать полученные данные. ПРИМЕЧАНИЕ. Правда, в среде разработок IDE вы сможете соз- дать только ту часть программного пакета, ко- торая будет работать в самом модуле Ардуино. Приложение для компьютера вам придется создавать при помощи других систем программирования. Рассмотрение этого вопроса не входит в задачу данной книги.
ГЛАВА 4 ПРОСТЕЙШАЯ ПРОГРАММА «HELLO, WORLD!» Постановка задачи Итак, в предыдущих главах мы узнали общие принципы построения модулей Ардуино и среды разработки IDE. ВНИМАНИЕ! Дальнейшая наша цель - научиться програм- мировать, изучить основные тонкости языка Ардуино, узнать приемы составления программ. Лучший способ научиться программировать — изучать материал на конкретных примерах. Именно такой способ обучения языкам Ассемблера и СИ используется в книге [1]. Поэтому и в данной книге изучение материала мы будем про- изводить на конкретных примерах. За основу набора примеров, предложенных в этой книге, взяты примеры, используемые в [1]. Примеры специально при- думаны так, чтобы изучать программирование от простого к сложному. Каждый новый пример раскрывает, какой-либо
42 Программирование ARDUINO. Создаем практические устройства новый аспект или прием программирования. Этот набор про- стейших алгоритмов хорошо зарекомендовал себя на практике. И начнем мы с самого простого, элементарного примера. Задача, поставленная в этом примере, не имеет практического смысла. Но как первый шаг в мир программирования подходит идеально. Первый урок по программированию для компьютера обычно начинается с программы, выводящей на экран надпись «Hello, world!». Считайте, что наша первая задача — это «Hello, world!» для микроконтроллеров. ЗАДАЧА. Создать программу для модуля Ардуино, которая должна управлять свечением светодиода в зави- симости от нажатия кнопки. Светодиод должен быть подключен к одному из цифровых контак- тов модуля, а кнопка - к другому цифровому кон- такту. При нажатии кнопки светодиод должен загораться, а при отпускании - гаснуть. Схема Один из возможных вариантов схемы изображен на рис. 4.1. Для подключения кнопки мы используем цифровой кон- такт 2, а для подключения светодиода — контакт 8. ПРИМЕЧАНИЕ. Выбор именно этих двух контактов не критичен. Для подобной задачи мы могли бы выбрать любые два других цифровых контакта. Не желательно лишь использовать контакты 0 и 1 по причине, о которой говорилось в предыдущей главе книги.
Глава 4. Простейшая программа «Hello, world!» 43 VD1 _J \ Рис. 4.1. Электрическая схема с кнопкой и светодиодом Выбранная нами схема максимально приближена к схеме аналогичного примера из книги [1]. Для подключения кнопки выбрана схема с замыканием на общий провод и использова- нием внутреннего подтягивающего резистора. Такое включение кнопки требует минимума деталей и позволяет использовать специальные возможности вход- ных цепей микроконтроллера. Рис, 4.2 иллюстрирует прин- цип работы кнопки, подключен- ной по выбранной нами схеме. На рис. 4.2 изображена часть схемы внутреннего устройства одного из разрядов порта ввода/ вывода микроконтроллера AVR. Именно к такой линии под- ключен каждый цифровой (да и аналоговый) контакт модуля Ардуино. +5 В Управление На вход порта ATmega328 Дкн Рис. 4.2. Схема работы кнопки и подтягивающего резистора
44 Программирование ARDUINO. Создаем практические устройства Схема, приведенная на рис. 4.2, представляет собой лишь часть общей схемы линии порта — ту часть, которая отвечает за ввод информации в порт. Схема содержит резистор Rn (подтя- гивающий резистор). При помощи программно управляемого ключа «К» резистор может подключаться между входом линии порта и шиной питания +5 В. ВНИМАНИЕ! В нашем случае ключ «К» должен быть постоян- но включен. В результате, если кнопка Skh не нажата, то благодаря рези- стору Rh, на входную линию порта поступает сигнал логиче- ской единицы. Если контакт Skh замкнуть, то вход порта будет соединен с общим проводом, а это эквивалентно подаче логи- ческого нуля. В результате этого микроконтроллер может легко считывать состояние кнопки. Если прочитанный сигнал равен единице — кнопка отпущена. Если нулю — кнопка нажата. ВНИМАНИЕ! При написании программы нужно обязательно предусмотреть команду, которая включит ключ «К» и подключит к схеме подтягивающий рези- стор. Если резистор не подключен, то при разом- кнутых контактах кнопки Skh на входе будут наводиться электромагнитные помехи, мешаю- щие работе программы. Отключают подтягивающий резистор в двух случаях: ♦ когда линия порта работает на вывод информации; ♦ когда на вход подается сигнал с выхода других микросхем с четкими уровнями нуля и единицы.
Глава 4. Простейшая программа «Hello, world!» 45 Свой подтягивающий резистор имеется на каждой линии каждого порта микроконтроллера, и все они включаются и выключаются независимо друг от друга. Для подключения светодиода выбран классический спо- соб (см. рис. 4.1). Светодиод подключается через токоогра- ничивающий резистор между цифровым контактом модуля и шиной питания. Для того чтобы зажечь светодиод, подклю- ченный по такой схеме, нужно установить на выходе (в данном случае на контакте 8) уровень логического нуля. В этом случае перепад потенциалов между нулем на выходе и напряжением питания составит 5 вольт. Светодиод загорится. ПРИМЕЧАНИЕ. Токоограничивающий резистор необходим для того, чтобы ток через светодиод не превысил максимально допустимого значения. Описанную выше схему удобнее всего собрать с использо- ванием универсального макетного контактного модуля, так как это показано на рис. 4.3. Алгоритм Программа должна непрерывно многократно считывать состояние кнопки, подключенной к контакту 2 модуля, и, в зависимости от считанного уровня, управлять сигналом на контакте 8 (выход светодиода). Если кнопка нажата (на входе 2 логический ноль), то про- грамма должна включить светодиод (подать логический ноль на выход 8). Если кнопка отпущена (на входе единица) — выключить светодиод (подать на выход единицу). Фактически, программа должна многократно в цикле считывать сигнал с входа 2 и передавать его на выход 8.
46 Программирование ARDUINO. Создаем практические устройства Рис. 4.3. Пример монтажа простейшей схемы на макетной плате
Глава 4. Простейшая программа «Hello, world!» 47 Первый вариант| программы II Для решения поставленной выше задачи предлагается два варианта программы. Первый вариант — это решение задачи «В лоб». Программа, приведенная в листинге 4.1, реализует сформулированный нами алгоритм буквально. Разберем под- робнее синтаксис программы. ПРИМЕЧАНИЕ. Для удобства восприятия материала читателем мы пронумеровали строки программы. Так удоб- нее ссылаться на ту или иную строку в тексте описания. Хотя синтаксис языка Ардуино не пред- полагает нумерации строк. Каждая команда программы отделяется от следующей сим- волом «точка с запятой». А на строки программа разбивается из соображений удобочитаемости. Так что программа — это то, что в листинге находится справа от столбца с номерами строк. Именно этот текст вы должны набрать при создании скетча в окне редактора про- грамм среды разработки. Кроме команд и операторов, составляющих собственно программу, текст программы содержит еще и комментарии. ПРИМЕЧАНИЕ. Любой язык программирования обязательно име- ет средства, которые позволяют вставлять в текст программы комментарии.
48 Программирование ARDUINO. Создаем практические устройства В языке Ардуино используется Си-подобный синтак- сис. Поэтому комментарии в программу вставляются двумя способами. Первый способ. Как комментарий воспринимается любая часть текста, ограниченная символами /* */. То есть все, что находится между парой символов /* и второй парой символов V воспринимается программой как комментарий. Второй способ позволяет добавлять комментарий в конце строки. Все, что находится после двойного слеша // до конца строки, считается комментарием. При трансляции программы комментарии игнорируются. Они предназначены для чело- века, который читает программу, для лучшего ее понимания. ПРИМЕЧАНИЕ. В листинге 4.1, как и в других листингах этой книги, пронумерованы только те строки, ко- торые содержат команды и операторы языка. Строки, содержащие только комментарии или строки, в которых располагается продолжение команд, занимающих несколько строк, не нуме- рованы. Любая программа, написанная на языке Ардуино, должна содержать две обязательные функции: setup() и 1оор(). Эти две функции обязательно должны присутствовать в каждой программе, даже если они не содержат никаких команд. Это структурные функции, определяющие два основных этапа работы любой программы для микроконтроллера. Функция setup() выполняется один раз, сразу после запу- ска программы или после нажатия кнопки сброса. В эту функ- цию нужно включить команды начальной настройки режимов всех узлов и систем микроконтроллера, и определение началь- ных значений переменных.
Глава 4. Простейшая программа «Hello, world!» 49 Функция loop() — это основной цикл программы. Этот цикл начинает выполняться сразу после окончания функции начальных установок и выполняется многократно все время, пока работает микроконтроллер. ПРИМЕЧАНИЕ. Любая программа для микроконтроллера всегда работает по такой схеме. Сначала однократно выполняется модуль начальной инициализации, а затем начинается основной цикл программы, в котором обычно и реализуется весь основной алгоритм. В языке Ардуино такая типовая организация программы заложена в структуру языка путем применения двух обяза- тельных функций setup() и 1оор(). Кроме двух обязательных функций, программист может создавать любое количество соб- ственных функций по своему усмотрению, Любая функция в языке Ардуино объявляется следую- щим образом: тип ИмяФункции(тип Параметр!., тип Прараметр2 ... ) { Команда1; Команда2; Имя функции выбирается произвольно. Имя должно под- чиняться обычным требованиям для синтаксиса имен: ♦ только латинские буквы и цифры; ♦ начинается с буквы; ♦ не допускаются пробелы. Перед именем функции необходимо указать тип возвра- щаемого значения. Тип может принимать значения int, char или другой допустимый в языке Ардуино.
50 Программирование ARDUINO. Создаем практические устройства ПРИМЕЧАНИЕ. Полный список допустимых типов значений при- веден в Приложении 2 в конце книги. В круглых скобках указывается список параметров, переда- ваемых в функцию при ее вызове. Для каждого параметра вы также должны указать его тип. Сразу за определением функ- ции в фигурных скобках указываются команды, составляю- щие тело функции. ПРИМЕЧАНИЕ. Если ваша функция в результате своей работы должна возвращать некое значение, то тело функции должно содержать хотя бы один опера- тор return, в параметре которого указывается это самое возвращаемое значение. Оператор return, в какой бы строке внутри функции он не находился, немедленно завершает выполнение функции. В языке Ардуино нет правила «сначала описание функции, затем ее использование». Вы можете помещать описание функции в любое место программы (но не внутрь другой функции). Особый случай, когда функция не возвращает никакого значения. ПРИМЕЧАНИЕ. По-другому такие функции иногда называют процедурами.
Глава 4. Простейшая программа «Hello, world!» 51 Если функция не возвращает никакого значения, то перед функцией вместо типа возвращаемого значения нужно поме- стить управляющее слово void. Функции setup() и 1оор() отно- сятся к функциям никогда не возвращающим значения. Более подробно о том, как используются функции, мы рассмотрим далее, по мере разбора конкретных примеров. Теперь, когда мы немного разобрались с некоторыми эле- ментами синтаксиса, давайте разберем по порядку программу, приведенную в листинге 4.1. В строках 1 и 2 происходит определение констант. Использование констант добавляет программе наглядности, а также упрощает в будущем изменение значений этих самых констант. Например, в нашей программе определяются значе- ния двух констант. В строке 1 определяется константа buttonPin (номер кон- такта для подключения кнопки). В строке 2 определяется константа ledPin (номер контакта для подключения светодиода). Этим константам присваива- ются значения 2 и 8, соответственно. Далее в программе эти константы используются вместо номеров этих контактов. ПРИМЕЧАНИЕ. Вместо номера контакта для подключения кноп- ки мы будем писать buttonPin, а вместо номера контакта для подключения светодиода - ledPin. Согласитесь, что это нагляднее. А если вы захотите поменять номер контакта? Например, решите подключить светодиод к контакту номер 9. Вам доста- точно будет просто поменять 8 на 9 в строке 2 программы. После этого везде, где в программе используется ledPin, про- грамма будет обращаться к контакту номер 9. Если программа большая и сложная, то в ней каждая кон- станта может использоваться десятки раз.
52 Программирование ARDUINO. Создаем практические устройства ПРИМЕЧАНИЕ. Использование именованных констант гаранти- рует от ошибок при изменении параметров про- граммы и упрощает изменение значений опреде- ляемых константами параметров. Описание константы начинается с ключевого слова const. Далее указывается тип значения определяемой константы. В данном случае тип значения — int (intedger). Это означает — целое число. В строке 3 определяется переменная. Имя переменной buttonState. ЧТО ЕСТЬ ЧТО. Переменная - это некая величина, которая мо- жет изменяться в процессе работы программы. Каждой переменной (так же, как и константе) присваивается свое уникальное имя. Далее в про- грамме обращение к переменной происходит по ее имени. Под переменную в оперативной памяти микроконтроллера выделяется одна или несколько ячеек. Программа может: ♦ присваивать переменной различные значения; ♦ читать содержимое переменной; ♦ использовать переменную в различных математических выражениях. ПРИМЕЧАНИЕ. В нашем случае переменная buttonState использу- ется для временного хранения значения состоя- ние кнопки.
Глава 4. Простейшая программа «Hello, world!» 53 В строке 4 программы начинается описание функции setupQ. В теле функции всего две команды (строки 5 и 6). В строке 5 определяется режим работы контакта ledPin (вывода для подключения светодиода). Режим работы контакта определяется как OUTPUT (вывод информации). Определение режима любого цифрового контакта в языке Ардуино произ- водится при помощи функции pinMode(). Эту функцию мы уже описывали в предыдущей главе книги. В строке 6 при помощи той же функции определяется режим работы контакта buttonPin (контакт для подключения кнопки). Этот режим определяется как INPUT_PULLUP (ввод информации с включенным подтягивающим резистором). В строке 7 программы объявляется функция 1оор() (основ- ной цикл). Функция 1оор() также содержит всего две команды (строки 8 и 9). В строке 8 происходит чтение состояния кнопки (считыва- ется значение сигнала на входе buttonPin). Для этого использу- ется функция digitalRead(). Считанное значение присваивается переменной buttonState. В строке 9 это значение выводится на выход ledPin при помощи функции digitalWhte(). Функции digitalRead() и digitalWriteQ используемые для чтения и вывода информации так же были описаны в предыдущей главе. ПРИМЕЧАНИЕ. Полный список функций вы всегда можете найти в Приложении 1. В результате работы функции 1оор() программа все время считывает состояние кнопки и тут же выводит его на свето- диод. Если кнопка не нажата, то считанное состояние будет равно 1. Единица на выходе светодиода приведет к тому, что он не будет гореть.
54 Программирование ARDUINO. Создаем практические устройства Листинг 4.1. Пример простой программы (вариант 1) 4 5 6 8 9 /* Простейшая программа: Светодиод и кнопка */ // Постоянные константы. В данной программе // константы определяют номера используемых контактов: const int buttonPin =2; // номер контакта кнопки const int ledPin =8; // номер контакта светодиода // Изменяемые переменные: int buttonState =0; // статус кнопки (нажата/не нажата) void setup () { // Инициализируем контакт светодиода (как выход): pinMode(ledPin, OUTPUT); // Инициализируем контакт кнопки (как вход): pinMode(buttonPin, INPUT_PULLUP); void loop () { // Читаем статус кнопки и помещаем // его значение в переменную: buttonState = digitalRead(buttonPin); // Помещаем считанное значение на выход светодиода: digitalWrite(ledPin, buttonState); Если при очередном считывании кнопка окажется нажатой, то ее состояние станет равно нулю. Ноль тут же передастся на выход светодиода и светодиод загорится.
Глава 4. Простейшая программа «Hello, world!» 55 Второй вариант | программы II Недостаток программы, приведенной в листинге 4.1, состоит в том, что микроконтроллер полностью занят всего одной задачей — сканированием кнопки и передачей значения ее состояния на светодиод. В такую программу трудно встроить какие-нибудь другие задачи. Но существует простой способ, как реализовать этот же алгоритм так, чтобы он выполнялся в фоновом режиме. То есть, так, чтобы основной цикл программы был не занят. ПРИМЕЧАНИЕ. Это дает возможность использовать основной цикл для решения любых других задач. И поможет нам создать такую программу механизм преры- ваний. Механизм прерываний существует в любом современном микроконтроллере. Очень подробно все вопросы организации прерываний описаны в [1]. В нашем случае в данном конкрет- ном примере мы будем использовать лишь один из видов пре- рываний, поддерживаемых микроконтроллером ATmega328. Это, так называемое, внешнее прерывание. Прерывание работает при поступлении внешнего сигнала на специальный вход микроконтроллера. При поступлении сигнала на вход прерывания выполнение текущей программы приостанавливается, и контроллер переходит к выполнению закрепленной за этим прерыванием процедуры, которая назы- вается процедурой обработки прерывания. Микроконтроллер Atmega328, а, значит, и модуль Ардуино, имеет два входа внешних прерываний. Они совмещены с линиями PD2 и PD3 порта PD микроконтрол- лера и, соответственно, с цифровыми контактами 2 и 3 модуля Ардуино. В нашей схеме кнопка как раз подключена к входу 2.
56 Программирование ARDUINO. Создаем практические устройства ПРИМЕЧАНИЕ. Поэтому мы без изменения схемы можем исполь- зовать прерывание по этому входу для обработ- ки события: «нажатие кнопки». Теперь основной цикл программы не будет заниматься сканированием кнопки и переключением светодиода. Эту задачу мы возложим на процедуру обработки прерывания. А на основной цикл программы можно возложить любые другие придуманные вами задачи. Новая программа будет работать так. В момент нажатия кнопки: ♦ возникает прерывание; ♦ выполнение основной программы приостанавливается; ♦ запускается процедура обработки прерывания. Эта процедура однократно читает состояние кнопки с того же самого входа 2 и посылает считанное значение на выход светодиода. Затем процедура обработки прерывания заверша- ется, и выполнение основной программы продолжается. При отпускании кнопки снова будет вызвано то же самое прерывание. И снова процедура обработки прерывания передаст состояние кнопки на светодиод. Обработка прерывания происхо- дит мгновенно и практически не отражается на скорости выпол- нения основной программы. Поэтому все операции с кнопкой и светодиодом будут выполняться как бы параллельным процессом. ПРИМЕР. Подобным образом выглядит процесс управления указателем мыши на экране компьютера. Какую бы сложную программу не выполнял компьютер, курсор мышки легко бегает по экрану, подчиняясь движениям самой мыши по столу. А ведь прори- совка курсора на экране - это тоже специальная небольшая программа, которая также работает по прерыванию от сигнала мыши.
Глава 4. Простейшая программа «Hello, world!» 57 Программа, реализующая задачу управления светодиодом при помощи кнопки с использованием внешнего прерывания, приведена в листинге 4.2. Благодаря тому, что язык Ардуино имеет очень простые и удобные средства работы с этим видом прерываний, программа настолько же проста, как и программа без прерываний. Рассмотрим листинг программы подробнее. Определения констант (строки 1 и 2) происходит так же, как и в предыдущем случае. В строке 3 также мы видим команду определения пере- менной buttonState. Однако теперь перед строкой определения добавлено управляющее слово: volatile. ПРИМЕЧАНИЕ. Это указание для компилятора, которое означа- ет, что изменение значения данной переменной будет производиться в теле процедуры обра- ботки прерывания, а, значит, не под контролем основной программы. То есть, в какой-то момент, когда выполнение основной программы будет приостановлено, значение переменной может из- мениться. Программа должна учитывать такую возможность. Ведь для основной программы это будет выглядеть так, как будто значе- ние неожиданно прямо в процессе вычислений вдруг измени- лось. Возможна ситуация, когда в основной программе перемен- ная используется несколько раз в сложном математическом выражении. Такое выражение вычисляется не мгновенно, а поэтапно. И может получиться так, что первая часть выраже- ния будет вычисляться с одним значением переменной, а вто- рая часть — уже с другим. Если переменная имеет тип volatile, то при трансляции в машинные коды программа строится так, что не переходит к обработке прерывания до тех пор, пока очередное выражение
58 Программирование ARDUINO. Создаем практические устройства не будет вычислено до конца. В нашем простом случае такая предосторожность не обязательна, но мы для порядка все же выполним это правило. Далее в теле функции setup() в строках 5,6 мы видим уже знакомые нам команды, устанавливающие режимы работы внешних контактов ledPin и buttonPin. ПРИМЕЧАНИЕ. Но кроме этого в новом варианте программы в тело функции setupQ добавлены еще две, отсут- ствующие ранее команды. В строке 7 на выход светодиода подается высокий логиче- ский уровень, который принудительно гасит светодиод. Такая команда понадобилась здесь потому, что в новой программе оценка состояния кнопки и передача значения на выход све- тодиода происходит только в момент нажатия либо отпускания кнопки. При запуске программы кнопка не нажата, а на любом выходе (в том числе и на ledPin) по умолчанию устанавливается логический ноль. ВНИМАНИЕ. Если не предусмотреть принудительное гашение светодиода, то при включении питания светоди- од загорится, и будет гореть, пока не нажмешь кнопку В первом варианте программы, когда опрос кнопки и вывод ее состояния на светодиод начинались сразу после включения питания, и продолжались непрерывно все время работы, све- тодиод принимал значение состояния кнопки моментально и гас, не успевая загореться.
Глава 4. Простейшая программа «Hello, world!» 59 Листинг 4.2. Простая программа вариант 2 (с использованием прерываний) 4 5 9 10 11 12 13 /* Простейшая программа Светодиод и кнопка с использованием прерывания */ // Определение констант: const int buttonPin =2; // номер вывода с кнопкой const int ledPin = 8; // номер вывода со светодиодом // Определение переменных: volatile int buttonState =0; // состояние кнопки void setup () { // Установить режим работы контакта светодиода: pinMode(ledPin, OUTPUT); // Установить режим работы контакта кнопки: pinMode(buttonPin, INPUTJPULLUP); digitalWrite(ledPin, HIGH); // Прикрепить прерывание к вектору ISR attachlnterrupt(digitalPinToInterrupt(buttonPin), pinlSR, CHANGE); // Основной цикл программы void loop () { // Здесь ничего нет! // Обработчик прерывания void pinlSRO { buttonState = digitalRead(buttonPin) ; digitalWrite(ledPin, buttonState);
60 Программирование ARDUINO. Создаем практические устройства Ну и последняя команда, добавленная в новой версии про- граммы в тело функции setup(), — это команда определения вектора прерывания. ПРИМЕЧАНИЕ. Выражение «определить вектор прерывания» оз- начает - закрепить за конкретным прерывани- ем процедуру его обработки. Сама процедура расположена ниже, в конце программы (строки 11,12,13). Оператор, определяющий вектор преры- вания, в чистом виде выглядит так: attachlnterrupt(interrupt, function, mode) Оператор имеет три параметра. Параметр interrupt — номер прерывания. Наш Arduino UNO, как уже говорилось выше, имеет всего два внешних пре- рывания. Прерывания в Ардуино различаются по номеру. Нумеруются они по порядку. Разные модели Ардуино отлича- ются как количеством внешних прерываний, так и номерами контактов, от которых они работают. Но в любом Ардуино есть прерывание 0 и прерывание 1. Некоторые модели имеют боль- шее число прерываний. ПРИМЕР. Arduino Mega2560 имеет шесть внешних пре- рываний. Они нумеруются цифрами от 0 до 5. Модуль Arduino UNO имеет следующие два внеш- них прерывания: прерывание 0, работающее от входа 2 и прерывание 1, работающее от входа 3. Параметр function — имя функции обработки прерывания.
Глава 4. Простейшая программа «Hello, world!» 61 Параметр mode — режим работы прерывания. Параметр mode может принимать следующие значения: LOW — вызывает прерывание, когда на входе прерывания установится низкий логический уровень (LOW); CHANGE — прерывание вызывается при смене значения на входе, с LOW на HIGH или наоборот; RISING — прерывание вызывается только при смене значе- ния на входе с низкого логического уровня (LOW) на высокий (HIGH); FALLING — прерывание вызывается только при смене зна- чения на входе с высокого уровня (HIGH) на низкий (LOW). Если мы посмотрим на строку 8 программы (листинг 4.2), то увидим, что вместо номера прерывания в функцию attachlnterrupt() подставлена другая функция: digitalPinToInterrupt(buttonPiri)9 в качестве параметра которой выступает номер контакта для подключения кнопки. Как легко догадаться эта специальная функция возвращает номер прерывания по номеру цифрового входа. Конечно, мы могли бы просто поставить в этом месте номер прерывания (в данном случае — это 0). Но использование специальной функ- ции делает программу более гибкой. Теперь, если вы захотите подключить кнопку не на кон- такт 2, а на контакт 3, вам нужно будет просто изменить номер контакта в строке 1 программы. СОВЕТ. В данной простейшей программе это не очень актуально, но в больших сложных программах, где прерывание вызывается много раз, такой прием очень облегчит изменение конфигурации системы.
62 Программирование ARDUINO. Создаем практические устройства В строке 9 программы начинается основной цикл про- граммы. В теле цикла нет ни одного оператора. Вместо комментария в строке 10 вы можете поместить в основной цикл любую сложную программу, которая будет выполнять нужные вам задачи совершенно независимо от задачи управления светодиодом при помощи кнопки. Описание функции обработки прерывания начинается в строке 11. ПРИМЕЧАНИЕ. Прежде чем приступить к разбору этой функции, опишем общие принципы построения подобных функций. Любая функция, предназначенная для обработки прерыва- ния, имеет следующие особенности: 1. функция не должна иметь никаких входных параметров и не должна возвращать никаких значений; 2. внутри функции обработки прерывания не работает опе- ратор deiayO, а значения возвращаемые millisQ не изменя- ются; 3. возможна потеря данных, передаваемых по последователь- ному соединению в момент выполнения функции обра- ботки прерывания; 4. как уже говорилось выше, переменные, изменяемые в этой функции, должным быть объявлены как volatile. Такой тип функции имеет специальное название Interrupt Service Routine (обработчик прерывания). Сокращенно ISR. Именно поэтому мы назвали нашу функцию pinlSR. В теле функции pinlSR (строки 12,13) мы видим те же две команды, которые в первом варианте программы были разме- щены в основном цикле: ♦ первая команда (в строке 12) читает состояние кнопки; ♦ вторая команда (в строке 13) записывает его на выход све- тодиода.
Глава 4. Простейшая программа «Hello, world!» 63 ПРИМЕЧАНИЕ. Оба приведенных выше варианта программы можно упростить. Синтаксис языка СИ, который используется в Ардуино, позволяет отказаться от переменной buttonState. ПРИМЕР. Два оператора в строках 8 и 9 листинга 4.1, так же как два аналогичных оператора в строках 12 и 13 листинга 4.2 можно заменить на одну стро- ку, в которую поместить следующее выражение: digital Write(ledPin, digitalRead(buttonPin)); Результат работы этого выражения точно та- кой же, как и результат работы двух строк, ис- пользующих промежуточную переменную. Так как переменная в этом случае больше не нужна, в обо- их вариантах программы можно удалить строку с ее определением. В обоих вариантах программы это строка 3.
ГЛАВА 5 ПЕРЕКЛЮЧАЕМЫЙ СВЕТОДИОД Постановка задачи В этом примере мы немного изменим программу, не изме- няя схемы. ЗАДАЧА. Создать программу, переключающую состояние светодиода (горит / не горит) при каждом на- жатии кнопки. Если светодиод не горит, то при кратковременном нажатии и отпускании кнопки он должен загореться. При очередном кратко- временном нажатии и отпускании кнопки свето- диод должен снова погаснуть. II Схема Схема устройства такая же, как в предыдущем примере (см. рис. 4.1).
Глава 5. Переключаемый светодиод 65 Алгоритм Алгоритм явственно вытекает из поставленной задачи. Есть только одна тонкость. Наша программа должна опреде- лить факт кратковременного нажатия кнопки. То есть момент, когда кнопка будет нажата, а затем отпущена. Поэтому алго- ритм будет звучать так. 1. Если кнопка не нажата, ждать нажатия кнопки. 2. Когда кнопка будет нажата, проверить состояние свето- диода. Если светодиод горит, программа должна его потушить. Если светодиод потушен — нужно его зажечь. 3. Снова проверить состояние кнопки. Если кнопка еще нажата, ждать отпускания. 4. Когда кнопка будет отпущена, перейти к пункту 1 алго- ритма. Первый вариант| программы II Простейшая программа, реализующая вышеописанный алгоритм, приведена в листинге 5.1. Так как схема у нас не поменялась, светодиод и кнопка подключены к тем же контак- там, начало программы у нас полностью соответствует про- граммам из главы 4. В строках 1 и 2 определяются константы. В строках 3, 4, 5 расположена функция setup(). Она, как и раньше, определяет режимы работы используемых в про- грамме цифровых контактов ledPin и buttonPin. Оставшуюся часть (строки 6...12) занимает основной цикл программы 1оор(). Именно в основном цикле реализу- ется нужный нам алгоритм. Разберем команды основного цикла подробнее.
66 Программирование ARDUINO. Создаем практические устройства В строке 7 реализован локальный цикл ожидания нажатия кнопки. Для организации этого цикла используется оператор whileQ. While — это один из стандартных операторов цикла языка СИ. Формат этого оператора следующий: while (условие) { Тело цикла; Английское слово while означает «пока». Операторы, вхо- дящие в тело цикла, выполняются многократно, пока условие в круглых скобках — истина. В нашей программе (см. строку 7) условием является digitalRead (buttonPin)==HlGU. Двойное «равно» — это операция сравнения двух вели- чин. Результат операции «истина», если сравниваемые вели- чины равны. Очевидно, что цикл будет выполняться до тех пор, пока значение, считанное с входа buttonPin, будет равно HIGH. Зарезервированное слово HIGH означает высокий логиче- ский уровень (или просто 1). В теле цикла ожидания нет ника- ких команд. Цикл должен просто ждать, пока сигнал с кнопки станет равным LOW (то есть нулю). Как только кнопка будет нажата, программа выйдет из цикла ожидания и перейдет к процедуре переключения све- тодиода (строки 8, 9,10,11). Для переключения светодиода используется оператор //. Формат оператора следующий: if (условие) Команда1; else Команда2; Слово if с английского переводится как «если», а слово else — как «иначе».
Глава 5. Переключаемый светодиод 67 ПРИМЕЧАНИЕ. Если условие в круглых скобках - истина, то вы- полняется Команда!, иначе - Команда2. Вместо каждой команды можно поставить группу команд, заключив их в фигурные скобки. Оператор if в строке 8 программы используется для оценки состояния светодиода (горит / не горит). Для этого использу- ется следующий прием: при помощи оператора digitalRead() мы считываем информацию с контакта ledPin. ПРИМЕЧАНИЕ Напоминаю, что контакт ledPin у нас запрограм- мирован на вывод информации! Но это не от- меняет возможность считывать информацию с этого контакта. Так устроены все информацион- ные контакты микроконтроллера. Оператор if проверяет значение сигнала, считанного с выхода на светодиод. Если считанное значение равно HIGH, то выполняется команда в строке 9 программы, которая устанав- ливает на том же выходе светодиода уровень LOW. В противном случае, выполняется строка 11, и на выходе светодиода устанавливается HIGH. В строке 12 расположен цикл ожидания отпускания кнопки. Это такой же пустой цикл, как и цикл ожидания нажа- тия в строке 7 программы. Изменилось только условие. Цикл выполняется до тех пор, пока значение состояния кнопки, считанное с входа buttonPin, равно LOW. Когда кнопка будет отпущена, цикл ожидания отпускания заканчивается. Но про- должается основной цикл /оорО-УпРавление передается на его начало, то есть к строке 7, и все повторяется заново.
68 Программирование ARDUINO. Создаем практические устройства Листинг 5.1. Первый вариант программы переключаемого светодиода з 4 5 б 7 8 9 10 11 12 /* Программа «Переключаемый светодиод» */ // Постоянные константы. const int buttonPin =2; // номер контакта кнопки const int ledPin =8; // номер контакта светодиода void setup () { // инициализируем контакт светодиода на вывод: pinMode(ledPin, OUTPUT); // инициализируем контакт кнопки на ввод: pinMode(buttonPin, INPUT_PULLUP); void loop () { // Ожидание нажатия кнопки: while (digitalRead(buttonPin)==HIGH) {}; // Переключение светодиода: if (digitalRead(ledPin)==HIGH) digitalWrite(ledPin, LOW); else digitalWrite(ledPin, HIGH); // ожидание отпускания кнопки: while (digitalRead(buttonPin)==LOW) {};
Глава 5. Переключаемый светодиод 69 Второй вариант II программы II Этот вариант программы приведен для того, чтобы проде- монстрировать, как можно написать программу, не используя операцию чтения уровня сигнала с выхода на светодиод. Новый вариант программы приведен в листинге 5.2. Новая программа мало отличается от предыдущей. Рассмотрим раз- личия. Так как в новом варианте программы мы отказываемся от считывания непосредственно с выхода ledPin > мы вводим переменную для хранения текущего состояния светодиода. Имя переменной ledState. Переменная будет хранить текущее значение состояния светодиода. При каждом нажатии кнопки это значение будет изме- няться на противоположное (с ноля на единицу, а с единицы на ноль), а затем выводить уже новое значение на контакт ledPin. В строке 3 программы не только создается эта переменная, но ей сразу присваивается начальное значение. В теле функции setup() также появилась новая команда. Она уже на стадии начальных установок выводит значение пере- менной ledState на контакт светодиода ledPin (строка 7 про- граммы). Эта команда нужна для того, чтобы начальное состо- яние светодиода (выключен), к тому времени уже записанное в переменную ledState, установилось на выходе светодиода. В цикле 1оор() новой программы также есть существенные изменения. Циклы ожидания нажатия и отпускания кнопки (строки 9 и 12) остались без изменений. А вот процедура изме- нения состояния светодиода изменилась полностью. Сразу после обнаружения факта нажатия кнопки выполня- ется инверсия значения переменной ledState. Значение пере- менной инвертируется и присваивается ей же. В языке СИ оператор «!» означает инверсию. Эта опера- ция как раз и изменяет ноль на единицу, а единицу на ноль. В строке 11 это новое, уже инвертированное значение выводится на контакт светодиода.
70 Программирование ARDUINO. Создаем практические устройства Листинг 5.2. Второй вариант программы переключаемого светодиода 8 9 10 11 12 /* Переключаемый светодиод (с использованием переменной для хранения состояния кнопки) */ // Определение констант: const int buttonPin =2; // номер контакта кнопки const int ledPin =8; // номер контакта светодиода // Определение переменной: int ledState = HIGH; // состояние светодиода void setup () { // Установка режимов контактов pinMode(ledPin, OUTPUT); // контакт светодиода pinMode (buttonPin, INPUT__PULLUP); // контакт кнопки digitalWrite(ledPin, ledState); // нач. сост. светодиода void loop () { // Ожидание нажатия кнопки while (digitalRead(buttonPin)==HIGH) {}; // Определение состояния светодиода ledState = !ledState; // Инвертирование digitalWrite(ledPin, ledState); // Вывод // Ожидание отпускания кнопки while (digitalRead(buttonPin)==LOW) {}; ПРИМЕЧАНИЕ. В остальном программа работает так же, как и в предыдущем варианте.
Глава 5. Переключаемый светодиод 71 Третий вариант | программы I В третьем варианте программы мы покажем, как можно задачу переключаемого светодиода решить с использова- нием механизма внешнего прерывания. Такая программа приведена в листинге 5.3. Начало программы (строки с 1 по 7) полностью соответ- ствует предыдущему варианту программы (листинг 5.2). В строке 8 мы видим уже знакомое нам выражение, опре- деляющее вектор прерывания. Такое же прерывание мы использовали в главе 4. ПРИМЕЧАНИЕ. В связи с использованием прерывания основной цикл, и в этой программе оставлен пустым. Как мы уже знаем, туда вы можете поместить команды, которые будут выполнять любые другие нужные вам задачи. В строках 11,12,13 находится процедура обработки пре- рывания. ПРИМЕЧАНИЕ. В теле процедуры имеются всего две команды. Это те же самые команды, которые в программе из листинга 5.2. были расположены в основном цикле. Алгоритм работы программы (листинг 5.3) очень про- стой. После выполнения команд начальных установок в теле функции setup(), программа переходит к основному циклу 1оор().
72 Программирование ARDUINO. Создаем практические устройства Листинг 5.3. Программа переключаемого светодиода с использованием прерывания 9 10 11 12 13 /* Переключаемый светодиод (с использованием механизма внешнего прерывания) */ // Константы const int buttonPin =2; // номер контакта кнопки const int ledPin =8; // номер контакта светодиода // Переменная volatile int ledState = HIGH; // состояние светодиода void setup () { // Установка режимов контактов pinMode(ledPin, OUTPUT); // контакт светодиода pinMode(buttonPin, INPUT_PULLUP); // конт. кнопки digitalWrite(ledPin, ledState); // нач. состояние св-диода // Прикрепление прерывания к вектору ISR: attachlnterrupt(digitalPinToInterrupt(buttonPin), pinlSR, FALLING); void loop() { // Основной цикл остается свободным! // Тут можно размещать основную программу, на // фоне которой будет выполняться переключение // светодиода в фоновом режиме // Обработчик прерывания void pinlSRO { ledState = !ledState; digitalWrite(ledPin, LEDstate);
Глава 5. Переключаемый светодиод 73 При нажатии кнопки активизируется прерывание. Выпол- нение основного цикла программы прерывается и вызывается процедура обработки прерывания. Эта процедура инвертирует значение переменной ledState (строка 12) и выводит новое ее значение на выход ledPin (строка 13). ПРИМЕЧАНИЕ. Циклы ожидания нажатия и отпускания кнопки в данном случае не требуется. Факт нажатия кнопки определяется настройками внешнего прерывания. При инициализации вектора прерывания (в строке 8) был выбран режим FALLING. В этом режиме прерывание возникает только в момент смены сигнала на входе с высокого логиче- ского уровня на низкий. А значит только в момент отпускания кнопки.
ГЛАВА 6 БОРЕМСЯ С ДРЕБЕЗГОМ КОНТАКТОВ Постановка задачи Иногда, собрав схему и опробовав программу, приведенную в предыдущей главе 5, вы можете заметить, что программа переключения светодиода работает нестабильно. Выражается это в том, что при нажатии кнопки светодиод иногда может не переключиться. Это связано с таким явлением, как дребезг контактов. ПРИМЕЧАНИЕ. Дребезг происходит в момент замыкания или размыкания любых механических контактов. Эффект дребезга состоит в том, что в момент замыкания надежное соединение не происходит моментально. Замыкание сопровождается целым рядом очень быстрых замыканий и размыканий, прежде чем установится надежный контакт. Такие же процессы сопровождают и в процесс размыкания контактов.
Глава 6. Боремся с дребезгом контактов 75 Дребезг контактов является проблемой и мешает работе элек- троники. Например, в программе переключения светодиода при нажатии кнопки из-за дребезга происходит не одно, а несколько переключений за одно нажатие. Это известная проблема, которая хорошо знакома разработчикам электронной аппаратуры. В инженерной практике существует множество приемов борьбы с этой проблемой. Подробно о дребезге контактов рас- сказывается в [1]. Существуют аппаратные и программные методы борьбы с дребезгом. Аппаратные методы — это различные схемные ухищрения. Их мы сейчас рассматривать не будем. При жела- нии подробнее смотрите в [1]. Мы рассмотрим несколько про- граммных методов борьбы с дребезгом путем усовершен- ствования программы. Самый простой метод борьбы с дребезгом — введение программной задержки. Как уже говорилось, дребезг контак- тов происходит некоторое время в момент после замыкания контактов либо некоторое время сразу после размыкания. По этому методу для борьбы с дребезгом после обнаруже- ния самого первого замыкания контакта программа должна выдержать небольшую паузу. ПРИМЕЧАНИЕ. Длительность паузы должна быть такой, что- бы за время паузы закончился весь процесс дре- безга. То есть программа должна пропустить все остальные замыкания и размыкания контактов, возникающие из-за дребезга. Точно такую же задержку нужно сформировать сразу после первого же размыкания контактов. Более продвинутые про- граммные методы борьбы с дребезгом контактов: ♦ метод статистической обработки сигналов, поступающих от кнопки; ♦ метод программного интегрирования цифрового сигнала;
76 Программирование ARDUINO. Создаем практические устройства ♦ так называемый алгоритм ожидания момента стабильного состояния кнопки. Сформулируем задачу. ЗАДАЧА. Доработать программу переключения светоди- ода, описанную в главе 5, изменив ее так, чтобы она обеспечивала эффект антидребезга. Схема Схема у нас опять остается без изменений (смотри рис. 4.1). I Антидребезг простыми средствами Простейший способ обеспечить антидребезг — вклю- чить в нужные места программы команды, обеспечивающие задержку на время дребезга. Для формирования задержки в языке Ардуино имеется специальная команда delay (Период). Команда формирует программную задержку. Длительность задержки определяется параметром «Период». Длительность задается в миллисекундах. Алгоритм Алгоритм в основном остается таким же, который был описан в главе 5 для программы переключения светодиода.
Глава 6. Боремся с дребезгом контактов 77 Отличие состоит лишь в том, что после цикла ожидания нажа- тия кнопки, а также после цикла ожидания отпускания, мы введем команду программной задержки. Программа В листинге 6.1 приведена доработанная программа, основу которой составляет программа, приведенная в листинге 5.1, в которую введены задержки антидребезга. Для этого в блок определений констант (строка 3 листинга 6.1) введена новая константа DelayTime. Константа определяет время задержки антидребезга. Команды задержки введены в основной цикл программы: ♦ одна— в строке 9сразу после цикла ожидания нажатия кнопки; ♦ вторая — в строке 12 сразу после цикла ожидания отпуска- ния кнопки. Кроме изменений, связанных с антидребезгом, в про- грамме на листинге 6.1 представлен еще один способ реали- зации процедуры переключения состояния светодиода. ПРИМЕЧАНИЕ. Вся процедура реализуется в виде выражения, ко- торое занимает всего одну строку программы (строка 10). Это сложное выражение, которое при помощи оператора digitalWrite() выводит на контакт светодиода ledPin инверти- рованное значение, считанное оператором digitalRead() с того же самого контакта ledPin. В результате программа обходится и без переменной для хранения состояния светодиода и без опе- ратора if(). ..else.
78 Программирование ARDUINO. Создаем практические устройства Листинг 6.1. Программа переключаемого светодиода с антидребезгом 10 11 12 /* Программа переключаемого светодиода с функцией антидребезга */ // Определение констант. const int buttonPin =2; // номер контакта кнопки const int ledPin =8; // номер контакта светодиода const int DelayTime =20; // Задержка антидребезга void setup () { // Инициализация контактов: pinMode(ledPin, OUTPUT); pinMode(buttonPin, INPUT_PULLUP); void loop() { // Ожидание нажатия кнопки: while (digitalRead(buttonPin)==HIGH) {}; // Задержка после нажатия кнопки delay (DelayTime); // Переключение светодиода: digitalWrite(ledPin, !digitalRead(ledPin)); // Ожидание отпускания кнопки: while (digitalRead(buttonPin)==LOW) {}; // Задержка после отпускания кнопки delay (DelayTime);
Глава 6. Боремся с дребезгом контактов 79 Применение внешней | библиотеки Button II Простейший способ антидребезга, примененный в преды- дущей версии программы (листинг 6.1), не всегда качественно отрабатывает все проблемы, связанные с вводом данных от внеш- них контактов. В сложных условиях эксплуатации, при изношен- ных контактах дребезг контактов может быть достаточно велик. К тому же, кроме проблем, создаваемых дребезгом контак- тов, на проводах, идущих от кнопки к плате, могут наводиться электромагнитные помехи, особенно если их длина значи- тельная. Помехи также мешают правильной работе. Для надеж- ного избавления от всех видов помех требуются более сложные алгоритмы. И вот тут очень удобно воспользоваться внешней библио- текой функций обслуживания кнопок и контактов. ПРИМЕЧАНИЕ. Стандартный набор библиотек пакета про- грамм Ардуино не содержит такой библиотеки. Но в Интернете вы можете найти множество вариантов подобных библиотек. Все они обычно доступны для свободного скачивания. Для примера возьмем библиотеку, которая называется «Button», автор Калинин Эдуард. Скачать библиотеку можно на сайте «Мир книг по электронике» (book.mirmk.ru). Библиотека представляет собой папку с именем «Button», кото- рая содержит три файла: ♦ собственно библиотека (Button.h); ♦ текст программ библиотеки на языке C++ (Button.cpp); ♦ список ключей (keywords.txt). После скачивания эту папку нужно извлечь из архива и поместить в пакет программ Ардуино в папку «libraries».
80 Программирование ARDUINO. Создаем практические устройства Если вы поместили пакет программ среды разработки в корневом каталоге диска «С:», то путь к папке библиотеки Button должен стать следующим: «С :\arduino\libraries\Button\». Теперь эту библиотеку вы можете использовать в ваших программах. ПРИМЕЧАНИЕ. Как это сделать в нашем конкретном случае, рас- смотрим на примере программы, приведенной в листинге 6.2. Для начала нужно присоединить библиотеку к нашей про- грамме. Среда разработки Ардуино имеет встроенную функцию, облегчающую поиск нужной библиотеки. Выбранная библио- тека автоматически включается в программу пользователя. Для включения библиотеки в программу выберите пункт меню «Скетч/Подключить библиотеку». В открывшемся списке библиотек выберите «Button». Сразу после того, как вы щел- кните по названию выбранной библиотеки мышкой, в текст программы в верхней ее части добавится оператор #include, a за ним в треугольных скобках — имя файла библиотеки. В листинге 6.2 этот оператор вы можете видеть в строке 1. ПРИМЕЧАНИЕ. Наличие оператора «include в тексте програм- мы означает, что библиотека подключена. Оператор «include — это стандартный оператор, имею- щийся практически в любом языке программирования. Этот оператор как бы вставляет библиотечные функции в текст вашей программы в том ее месте, где этот оператор находится.
Глава 6. Боремся с дребезгом контактов 81 Листинг 6.2. Программа переключения светодиода с использованием внешней библиотеки ю и 12 13 14 /* Программа переключаемого светодиода антидребезг с использованием внешней библиотеки */ ♦include <Button.h> // Определение констант: const int buttonPin =2; // номер контакта кнопки const int ledPin =8; // номер контакта светодиода const int DelayTime =20; // Задержка антидребезга // Создание объекта «кнопка» Button buttonl (buttonPin, DelayTime); void setup () { // Инициализация контактов модуля pinMode(ledPin, OUTPUT); // конт. светодиода pinMode(buttonPin, INPUT_PULLUP); // конт. кнопки digitalWrite(ledPin, HIGH); void loop () { // Вызов метода ожидания стабильного состояния кнопки buttonl.scanState(); if ( buttonl.flagClick == true ) { buttonl.flagClick = false; // сброс признака клика // Переключение светодиода: digitalWrite(ledPin, !digitalRead(ledPin));
82 Программирование ARDUINO. Создаем практические устройства Вы можете использовать ^include для подключения своих собственных программных процедур к своей основной про- грамме. Запишите текст этих процедур в отдельный файл, а в основную программу впишите оператор ^include. В качестве параметра укажите имя вашего присоединяемого файла. СОВЕТ. Для подключения библиотеки совсем не обяза- тельно использовать команду «Подключить би- блиотеку». Вы можете просто вписать нужную строку в текст вашей программы вручную. Если вы скачаете с сайта «Мир книг по микроэлектронике» приведенный ниже пример программы в электронном виде, то там уже имеется нужная запись, а, значит, библиотека уже под- ключена. После присоединения к вашей программе библиотеки «Button» в распоряжении программиста появляется ряд новых функций, помогающих работать с кнопками и контактами. Эти функции реализуют алгоритмы, устраняющие как проблемы с дребезгом контактов, так и влияние других видов помех. ПРИМЕЧАНИЕ. В связи с тем, что библиотека «Button» написана на языке C++, все библиотечные функции работа- ют в стиле объектного программирования. Это значит, что сначала мы должны создать объект «Кнопка», при помощи которого мы и будем осуществлять доступ к нашей физической кнопке. Если бы у нас в программе было несколько кнопок, то для каждой из них мы должны были бы создать свой отдельный объект типа «Кнопка».
Глава 6. Боремся с дребезгом контактов 83 ПРИМЕЧАНИЕ. О том, как создается объект и как с ним нужно работать, мы узнаем в процессе построчного разбора программы, приведенной в листинге 6.2. Итак, начнем по порядку. В начале программы, в строках 2, 3,4 определяются все необходимые для работы программы кон- станты. Эта часть программы полностью соответствует ее пре- дыдущей версии (см. листинг 6.1). Оператор, создающий объект «Кнопка», расположен в строке 5. Формат оператора следующий: Button ИмяКнопки (НомерКонтакта, ПериодОбработки); Имя кнопки программист может выбрать по своему усмо- трению. ПРИМЕЧАНИЕ. Оно должно подчиняться стандартным требо- ваниям к синтаксису имен. Параметр «НомерКонтакта», в нашем случае, соответствует переменной buttonPin. ПериодОбработки — имеет такой же смысл, как время задержки в предыдущей версии программы. ПРИМЕЧАНИЕ. Это время должно немного превышать продол- жительность дребезга для ваших конкретных контактов. Однако оно не должно быть слишком большим. ВремяОбработки должно быть значи- тельно меньше интервала времени между двумя последовательными нажатиями кнопки. Иначе вы не сможете нажимать кнопку слишком быстро.
84 Программирование ARDUINO. Создаем практические устройства В нашей программе в строке 5 мы создаем объект «Кнопка» с именем Buttonl. В объектном программировании любой объект имеет свои «свойства» и «методы». При обращении к «методу» объекта он выполняет определенные действия. ЧТО ЕСТЬ ЧТО. «Метод» - это аналог понятию «функция» в обычной программе. Понятие «свойство» анало- гично понятию «переменная». Значение «свой- ства» объекта можно прочитать. «Свойству» можно присвоить новое значение. Объект «Кнопка» имеет два встроенных метода: ♦ метод проверки ожидания стабильного состояния сигнала; ♦ метод фильтрации сигнала по среднему значению. Например, для объекта Buttonl методы будут выглядеть следующим образом: Buttonl.scanState(); // метод проверки ожидания стабиль- ного состояния сигнала Buttonl.filterAvarage(); // метод фильтрации сигнала по сред- нему значению В своей программе вы можете использовать любой, но только один из этих двух методов. Но ваша программа должна обеспечить многократный вызов выбранного вами метода. ПРИМЕЧАНИЕ. Для этого вы должны либо включить его в ос- новной цикл программы (желательно в самое его начало), либо обеспечить вызов метода по пре- рыванию от таймера с максимально возможной частотой вызова.
Глава 6. Боремся с дребезгом контактов 85 Что же делает каждый их этих методов? Метод проверки ожидания | стабильного состояния сигнала I При каждом вызове производит опрос состояния закре- пленной за ним кнопки и ведет от вызова к вызову статисти- ческую обработку. По результатам этой обработки устанавли- ваются следующие свойства объекта: Button.flagPress — примет значение true при стабильно нажатой кнопке; Button.flagPress — примет значение false при стабильно отжатой кнопке; Button.flagClick — примет значение true при кратковремен- ном нажатии и отпускании кнопки. Все свойства устанавливаются только в момент достижения стабильного состояния контактов кнопки. Метод фильтрации сигнала | по среднему значению II Отличается от предыдущего метода лишь алгоритмом обработки входного сигнала. В процессе работы устанавливает следующие свойства: Button.flagPress — примет значение true когда на входе ста- бильно установится сигнал низкого уровня Button.flagPress — примет значение false когда на входе ста- бильно установится сигнал высокого уровня Button.flagClick — примет значение true при изменении сигнала на входе с высокого уровня на низкий.
86 Программирование ARDUINO. Создаем практические устройства ПРИМЕЧАНИЕ. Подробное описание алгоритма работы и тек- ста программ библиотеки Button вы можете найти в Интернете по адресу: http://mypractic.ru/urok-8-cifrovaya-filtraciya- signalov-v-programmax-dlya-arduino.html Вернемся к нашей программе и посмотрим, как методы и свойства объекта «Кнопка» используются для решения нашей задачи. Описывать функцию setup() мы не будем, так как размещенные в теле этой функции команды уже много раз описывались в предыдущих программах. Нововведения мы увидим в функции 1оор(). Первая команда функции 1оор() (строка 11) вызывает метод scanStateQ объекта Buttonl. Метод производит заложенные в нем статистические дей- ствия, и управление переходит к строке 12. В этой строке находится оператор //, который оценивает значение свойства flagClick объекта Buttonl. Строки 13 и 14 выполняются только в том случае, если значение свойства flagClick равно true. To есть, если обнаружен факт кратковременного нажатия и отпускания кнопки. В строке 13 программа сбрасывает значение flagClick для того, чтобы избежать повторной обработки события «Нажатие кнопки». В строке 14 мы видим уже знакомое нам выражение, пере- ключающее светодиод на выходе ledPin. В результате работы описанной выше программы при каж- дом нажатии кнопки светодиод переключится в противопо- ложное состояние. При этом факт нажатия будет определен путем статистической обработки исключающей влияния дре- безга контактов и электромагнитных помех.
ГЛАВА 7 МИГАЮЩИЙ СВЕТОДИОД Постановка задачи Программу переключаемого светодиода, описанную в пре- дыдущей главе, легко превратить в программу мигающего све- тодиода. ЗАДАЧА. Создать программу, которая при нажатии кноп- ки будет вызывать мигание светодиода, а при отпускании кнопки мигание должно прекра- титься, и светодиод должен погаснуть. Схема Мы снова используем схему, которая использовалась во всех предыдущих примерах (см. рис. 4.1).
88 Программирование ARDUINO. Создаем практические устройства II Алгоритм 1. Считать состояние кнопки. 2. Оценить состояние кнопки. Если кнопка нажата, перейти к процедуре мигания. Если кнопка не нажата, принуди- тельно погасить светодиод. 3. В процедуре мигания один раз выполнить следующие действия: 3.1. зажечь светодиод; 3.2. сформировать задержку заданной длительности; 3.3. погасить светодиод; 3.4. сформировать заданную задержку. 4. Перейти к пункту 1 алгоритма. В результате работы такого алгоритма перед каждым циклом загорания и потухания светодиода происходит оценка состояния кнопки. Это позволяет прекратить мигание сразу, после того, как кнопка будет отпущена. Программа Вариант программы, реализующий описанный выше алго- ритм, приведен в листинге 7.1. Начальная часть программы (строки 1...6) пояснений, я думаю, не требуют. Они уже не раз встречались в наших пре- дыдущих программах. В строке 7 начинается основной цикл программы. Команды основного цикла последовательно выполняют все пункты при- веденного выше алгоритма. В строке 8 считывается и оценивается значение на входе buttonPin. Если это значение равно LOW (кнопка нажата), выполняются операции мигания светодиода (строки 9... 12). В противном случае выполняется оператор, непосред- ственно следующий за оператором else (строка 13). Он запи- сывает на выход светодиода (ledPiri) значение высокого логи- ческого уровня (тушит светодиод).
Глава 7. Мигающий светодиод 89 Листинг 7.1. Программа мигающего светодиода 9 10 И 12 13 /* Программа «Мигающий светодиод» */ // Определение констант const int buttonPin =2; // номер контакта кнопки const int ledPin =8; // номер контакта светодиода const int DelayBlink = 200; // Задержка мигания void setup () { // Инициализиация контактов модуля: pinMode(ledPin, OUTPUT); // конт. светодиод pinMode(buttonPin, INPUT PULLUP); // конт. кнопки void loop () { // Если кнопка нажата: if (digitalRead(buttonPin)==LOW) { digitalWrite(ledPin, LOW); // Зажигаем светодиод delay (DelayBlink); // Задержка digitalWrite(ledPin, HIGH); // Тушим светодиод delay (DelayBlink); // Задержка } else digitalWrite(ledPin, HIGH); // Тушим светодиод Процедура мигания также очень проста. В строке 9 на выход светодиода ledPin записывается низкий логический уро- вень (светодиод зажигается). В строке 10 формируется задержка. Для этого используется команда delay(). В качестве параметра процедуры используется константа DelayBlinky заданная в начале программы (строка 3).
90 Программирование ARDUINO. Создаем практические устройства ПРИМЕЧАНИЕ. Константа DelayBlink определяет время свече- ния светодиода, а, значит, половину периода мер- цания. В строке 11 находится команда, которая тушит светодиод. В строке 12 мы видим еще одну команду задержки. Она определяет длительность нахождения светодиода в погашен- ном состоянии. Эта длительность также равна DelayBlink. Если учесть, что значение константы DelayBlink равно 200, то период мигания нашего светодиода будет равен 400 миллисекундам, то есть 0,4 секунды. ПРИМЕЧАНИЕ. Временем выполнения остальных операций (чте- ния состояния кнопки, оценки этого состояния и вывода значений на светодиод) можно прене- бречь. Поэтому частота мигания светодиода будет равна: 1/0,4 = 2,5 Гц (два с половиной герца). Главным недостатком программы, приведенной в листинге 7.1, можно считать то, что при своей работе она пол- ностью забирает все ресурсы микроконтроллера. Эта проблема легко решается применением механизма прерываний. ПРИМЕЧАНИЕ Прерывания по таймеру мы рассмотрим в гла- ве 10. А в следующей главе, мы добавим к нашей схеме еще несколько дополнительных светодио- дов, вспомним новогодний праздник и заставим «бежать огни».
ГЛАВА 8 БЕГУЩИЕ ОГНИ Постановка задачи В настоящее время тема бегущих огней потеряла свою актуальность. В продаже имеется такое огромное количество всяческих гирлянд, у которых вся схема управления запрятана прямо в вилке или в малюсенькой коробочке, висящей на про- воде. Все уже забыли те времена, когда созданием и сборкой различных схем бегущих огней в канун нового года занима- лись тысячи радиолюбителей нашей страны. Но мы в данной главе все-таки используем эту задачу в качестве примера. ЗАДАЧА. Создать схему и программу, реализующую эф- фект «бегущих огней». Программа «бегущих ог- ней» должна управлять цепочкой из восьми све- тодиодов (в дальнейшем вместо каждого свето- диода можно будет подключить целую гирлянду). В каждом цикле «пробегания» огня светодиоды должны зажигаться по очереди, начиная с перво- го и заканчивая последним. В каждый момент времени должен гореть один из восьми свето-
92 Программирование ARDUINO. Создаем практические устройства диодов. Разрабатываемое устройство также должно иметь кнопку управления. Назначение кнопки- реверс направления движения огней. При отжатой кнопке огни должны бежать слева направо, а при нажатой кнопке - справа налево. Схема Один из вариантов возможной схемы бегущих огней приведен на рис. 8.1. Собрать такую схему можно на универсальном монтаж- ном контактном поле (см. рис. 8.2). Такое поле позволяет создавать несложные устройства без использования пайки. Подобные монтажные контактные модули, соединительные кабели с разъемами и другие комплектующие, используемые в нашем примере, можно купить в магазине радиодеталей, на радиорынке или в Интернете. Алгоритм 1. Проверить состояние кнопки. 2. Если кнопка отпущена, запустить процесс однократного пробегания огня слева направо (один раз по очереди заго- раются все восемь светодиодов). 3. Если кнопка отпущена, запустить процесс пробегания огня справа налево. 4. По окончанию процесса однократного пробегания, про- должить выполнение алгоритма сначала. То есть, перейти к пункту 1 данного алгоритма.
Глава 8. Бегущие огни 93 Рис. 8.1. Схемо Бегущих огней Рис. 8.2. Внешний вид «бегущих огней» но монтожной плоте
94 Программирование ARDUINO. Создаем практические устройства ПРИМЕЧАНИЕ. Особенность данного алгоритма в том, что про- верка состояния кнопки происходит один раз за один цикл пробегания (8 шагов). Поэтому, если на- жать кнопку в тот момент, когда огонь еще не «добежал» до конца, он сначала закончит свой «бег», а уже потом «побежит» в обратную сторону Первый вариант программы, реализующей описанный выше алгоритм, приведен в листинге 8.1. I Первый вариант программы Начинается программа, как обычно, с определения кон- стант и переменных. В данной программе мы определяем зна- чение всего двух констант. В строке 1 определяется номер контакта для подключения кнопки. В строке 2 задается величина задержки одного шага «бега» огней. В строках 3...6 расположена функция начальных установок setup(). В теле функции происходит установка режимов работы всех используемых в программе информационных контактов модуля Ардуино. ПРИМЕЧАНИЕ. Для задания режимов работы группы контактов, предназначенных для подключения светодиодов, организован программный цикл.
Глава 8. Бегущие огни 95 Для организации цикла в данном случае мы будем исполь- зовать оператор for. Этот оператор применяется и в других местах данного программного примера (см. листинг 8.1). Например, в строках 4,10,15. Формат оператора for следующий: for (НачЗнач, Условие, Инкремент); { Команды тела цикла; Оператор имеет три параметра. Параметр «НачЗнач» — это команда, присваивающая начальное значение переменной, которая называется «счет- чик цикла». Значение счетчика цикла изменяется в процессе его работы. В строке 4 программы в качестве счетчика цикла используется переменная thisPin. Параметр «Условие» — это логическое выражение. Цикл выполняется до тех пор, пока результат этого выражения — TRUE (Истина). Обычно в этом выражении сравнивается зна- чение счетчика цикла и его предельное значение. Но допу- стимо и любое другое логическое выражение. Параметр «Инкремент» — представляет собой выраже- ние, которое увеличивает значение счетчика цикла при каж- дом проходе (итерации). Чаще всего за один проход значение счетчика цикла увеличивается на единицу. ПРИМЕЧАНИЕ. Но никаких ограничений тут нет. Увеличивать счетчик цикла, при необходимости, можно на лю- бую величину, а также Инкремент можно менять на Декремент. То есть с каждой итерацией не увеличивать, а уменьшать значение счетчика.
96 Программирование ARDUINO. Создаем практические устройства Здесь уместно сказать, что в языке СИ именно для Инкрементации и Декрементации различных величин часто используются два оператора, характерных именно для этого языка программирования: ♦ двойной плюс «++»; ♦ двойной минус «- -». Каждый из этих операторов используется двумя разными способами. Например, для переменной X применение этих операторов будет выглядеть так: Х++ — увеличение значения переменной на единицу после ее использования; ++Х — увеличение значения переменной на единицу перед ее использованием; X уменьшение значения переменной на единицу после ее использования; —X — уменьшение значения переменной на единицу перед ее использованием. В табл. 8.1 работа операторов инкрементации и декремен- тации описана на примерах. Значение, которое мы решили присвоить переменной X перед началом операции, взято про- сто для примера. Под операцией мы понимаем процесс вычис- ления Выражения. Логика работы операторов инкременации и декременации Таблица 8.1 Выражение Y = X++ Y = ++X Y = X-- Y = --X Значение X до операции 5 5 5 5 Значение Y после операции 5 6 5 4 Значение X после операции 6 6 4 4 Но вернемся к описанию программы. В строке 4 программы организован цикл, который переби- рает цифровые контакты модуля Ардуино со второго по девя-
Глава 8. Бегущие огни 97 тый, и настраивает режим работы каждого из этих контактов. Эти контакты используются для подключения светодиодов. ПРИМЕЧАНИЕ. Как уже говорилось, в качестве счетчика цикла используется переменная thisPin. Первый параметр цикла присваивает счетчику значение 2. В качестве условия используется выражение thisPin<10. В тре- тьем параметре значение счетчика цикла увеличивается на единицу. Такие параметры обеспечивают перебор значений переменной thisPin от 2 до 9. В теле цикла всего два оператора: ♦ первый (строка 5) настраивает контакт с номером thisPin на вывод информации; ♦ второй (строка 6) устанавливает на выводе номер thisPin высокий логический уровень. ВЫВОД. Таким образом, цикл настраивает по очереди режим работы всей группы контактов, исполь- зуемых для подключения светодиодов, и тушит каждый светодиод, подавая на выход логическую единицу Основной цикл программы 1оор() начинается с команды, считывающей и оценивающей состояние кнопки (строка 9). Если состояние кнопки равно HIGH (кнопка отпущена) выпол- няется цикл однократного «пробегания огня» в прямом направлении (строки 10, 11, 12 и 13). В противном случае выполняется цикл «пробегания огня» в обратном направлении (строки 15,16,17 и 18).
98 Программирование ARDUINO. Создаем практические устройства Листинг 8.1. Программа «Бегущие огни» 9 10 11 12 13 14 15 16 17 18 /* Простая программа «Бегущие огни» */ const int buttonPin = 13; // номер контакта кнопки - const int LoopTime = 200; // задержка шага бег. огня void setup () { // Цикл инициализации контактов светодиодов: for (int thisPin = 2; thisPin < 10; thisPin++) { pinMode(thisPin, OUTPUT); digitalWrite(thisPin, HIGH); } // Инициализация контакта кнопки: pinMode(buttonPin, INPUT); void loop () { if (digitalRead(buttonPin)==HIGH) { // Цикл одного пробега в прямом направлении: for (int thisPin = 2; thisPin < 10; thisPin+f) { // Включение текущего светодиода: digitalWrite(thisPin, LOW); delay(LoopTime); // Задержка // Выключение текущего светодиода: digitalWrite(thisPin, HIGH); else { // Цикл одного пробега в обратном направлении: for (int thisPin = 9; thisPin > 1; thisPin--) { digitalWrite(thisPin, LOW); delay(LoopTime); // Задержка digitalWrite(thisPin, HIGH);
Глава 8. Бегущие огни 99 ПРИМЕЧАНИЕ. Оба цикла (прямой и обратный) практически одинаковы. Разница только в том, что перемен- ная счетчика цикла в одном случае с каждой ите- рацией цикла увеличивается на единицу, а во вто- ром случае - уменьшается. Обратимся сначала к прямому циклу, который начина- ется в строке 10 программы. Все параметры этого цикла ана- логичны уже рассмотренному нами циклу, который находится в строке 4. Поэтому цикл в строке 10 тоже перебирает все зна- чения счетчика цикла thisPin от 2 до 9. Для каждого значения thisPin программа выполняет три последовательных операции: ♦ в строке 11 программа зажигает текущий светодиод, под- ключенный к выходу thisPin; ♦ в строке 12 формирует задержку длительностью LoopTime; ♦ в строке 13 затем тушит текущий светодиод. ПРИМЕЧАНИЕ. В результате за восемь итераций (шагов) цикла загораются по очереди все 8 светодиодов. Цикл обратного «пробегания» расположен в строках 15,16,17 и 18. Обратный цикл работает так же, как и прямой цикл, но перебирает контакты светодиодов в обратной после- довательности (с девятого по второй). По этой причине обратный цикл от прямого отличается лишь значениями своих параметров. Теперь в качестве началь- ного значения счетчику цикла thisPin присваивается значение 9. В качестве условия продолжения цикла используется выражение thisPin>l.
100 Программирование ARDUINO. Создаем практические устройства А вместо команды инкрементации используется команда декрементации (thisPin—). В результате мы получим эффект обратного перебора значений. Набор команд в теле обратного цикла ничем не отличается от таких же команд цикла прямого. I Второй вариант - используем один универсальный цикл ПРИМЕЧАНИЕ. Если вы посмотрите на текст предыдущей про- граммы (листинг 8.1), вы легко заметите, что цикл прямого и цикл обратного «пробега» от- личаются только параметрами. Это дает воз- можность использовать вместо двух циклов один универсальный цикл. В листинге 8.2 показано, как это сделать. Программа, приве- денная в листинге 8.2, представляет собой доработанную про- грамму из листинга 8.1. Поэтому опишем только изменения. Для начала в программу мы введем три переменных (строки 3,4 и 5). В этих переменных будут храниться и в нуж- ный момент изменяться параметры цикла, именно: ♦ значение начала цикла; ♦ значение конца цикла; ♦ значение шага (+1 или -1). Функция setupQ не имеет никаких изменений. А вот в теле функции 1оор() все становится немного по-другому. Оператор if в строке 12 считывает и оценивает состояние клавиатуры. В зависимости от этого состояния он присваивает трем нашим новым переменным соответствующие значения. Если кнопка нажата, то переменным присваиваются значе- ния параметров цикла для пробега вперед (строка 13). Если кнопка отпущена, то переменным присваиваются параметры цикла «пробега назад» (строка 15).
Глава 8. Бегущие огни 101 Листинг 8.2. Программа «Бегущие огни» с общим универсальным циклом б 7 9 10 11 12 13 14 15 16 17 18 19 /* Программа «Бегущие огни» с общим универсальным циклом */ const int buttonPin = 13; // Номер контакта кнопки const int LoopTime =200; // Задержка шага бег. огня int loopStart =2; // Начало цикла бега int loopStop =10; // Конец цикла бега int loopStep = 1; // Шаг цикла бега void setup () { // Цикл инициализации контактов светодиодов: for (int thisPin = 2; thisPin < 10; thisPin++) { pinMode(thisPin, OUTPUT); digitalWrite(thisPin, HIGH); } // Инициализация контакта кнопки: pinMode(buttonPin, INPUT); void loop() { if (digitalRead(buttonPin)==HIGH) { loopStart = 2; loopStop = 10; loopStep = 1; } else { loopStart = 9; loopStop = 1; loopStep = -1; } // Цикл одного пробега бегущего огня: for (int thisPin = loopStart; thisPin != loopStop; thisPin+=loopStep) { // Включение текущего светодиода: digitalWrite(thisPin, LOW); delay(LoopTime); // Задержка // Выключение текущего светодиода: digitalWrite(thisPin, HIGH);
102 Программирование ARDUINO. Создаем практические устройства Сам универсальный цикл пробега находится в строках 16...19. Строки 17, 18 и 19 составляют тело цикла и полностью соответствуют командам, которые мы видели в теле цикла в предыдущем примере. Ну, а параметры цикла определяются следующим образом. В качестве начального значения для переменной thisPin присваивается значение переменной loopStart. В качестве условия работы цикла используется логическое выражение thisPin != loopStop. Оператор «!=» — в языке СИ представляет собой логиче- ский оператор, выполняющий проверку условия «не равно». Представленное выражение возвращает значение TRUE («Истина») в том случае, если значения переменных thisPin и loopStop не равны между собой. То есть, цикл будет длиться до тех пор, пока значение счетчика цикла thisPin будет не равно его предельному значению, хранящемуся в переменной loopStop. Ну, а третий параметр нашего универсального цикла выглядит следующим образом: thisPin += loopStep; Составной оператор «+=» означает: присвоить перемен- ной в левой части выражения значение суммы значений из левой и правой части. То есть, вышеприведенное выражение полностью эквивалентно выражению thisPin = thisPin + loopStep; Если кнопка не была нажата, значение переменной loopStep равно единице, и при каждой итерации значение thisPin увели- чивается на единицу. Если кнопка нажата, значение loopStep равно минус один. А это значит, что при каждой итерации счетчик цикла thisPin будет уменьшаться на единицу. Это приведет к тому, что при нажатой и при отпущенной кнопке цикл будет: ♦ в первом случае — прямым; ♦ во втором случае — обратным. И в том, и в другом случае цикл будет начинаться со зна- чения loopStart и заканчиваться значением loopStop. Но при
Глава 8. Бегущие огни 103 прямом и при обратном направлении перебора значения, при- своенные переменным loopStart и loopStop, будут разные (см. строки 13 и 15). Для того чтобы превратить схему, у которой лишь 8 све- тодиодов на выходе, в настоящие бегущие огни нужно под- ключить на выходы Ардуино вместо каждого из светодиодов силовой ключ. К каждому ключу нужно подключить гирлянду лампочек или светодиодов. ПРИМЕЧАНИЕ. Светодиоды в каждой гирлянде могут быть вклю- чены как последовательно, так и параллельно. Нужно только рассчитать питающее напряжение. При последовательном соединении придется подавать достаточно большое напряжение. Оно будет вычисляться по формуле: где игерл — напряжение на гирлянде; Upa6 — рабочее напряжение одной лампочки (светодиода); NCB — количество светодиодов. Все восемь гирлянд нужно сложить таким образом, чтобы светодиоды от разных гирлянд перемежались по порядку. Сначала светодиод первой гирлянды, затем светодиод второй, затем третьей и т. д. до светодиода восьмой. После этого все должно повторяться: светодиод первой гирлянды, второй и т. д. Мы не будем приводить подробную схему полной гир- лянды, так как это не является целью данной книги. Схема и программа «бегущих огней» является просто хорошим учеб- ным примером в конструировании и составлении программ. СОВЕТ. При желании, схему силового ключа можно легко найти в Интернете.
ГЛАВА 9 АЛЬТЕРНАТИВНЫЕ СПОСОБЫ ФОРМИРОВАНИЯ ЗАДЕРЖКИ Постановка задачи Во всех предыдущих программах, приведенных в этой книге, в которых нужно было сформировать задержку по вре- мени, мы это делали при помощи функции delay(). ПРИМЕЧАНИЕ. Однако у этой функции есть большой недоста- ток: в процессе своей работы она полностью за- нимает все ресурсы микроконтроллера. Если программа вызывает команду delay(), то до тех пор, пока эта команда не закончит свою работу, никакая другая команда выполниться не может. То же самое касается и еще одной функции, которая также используется для формирова- ния программной задержки в языке Ардуино.
Глава 9. Альтернативные способы формирования задержки 105 Функция называется delayMicroseconds() и от функции delay() отличается тем, что формирует задержку, которая зада- ется в микросекундах. Специально для тех случаев, когда в момент формирования задержки необходимо выполнять еще какие-либо действия, в языке Ардуино имеется другой механизм формирования интервалов времени. В основе этого механизма лежат встроенные системные часы. Их, как мы знаем, транслятор автоматически создает при трансляции вашей программы. Системные часы постоянно отсчитывают время, прошедшее с момента запуска программы. Для чтения текущего времени системных часов в наборе команд языка Ардуино имеются две специальные функции: millisQ — возвращает количество миллисекунд microsQ — возвращает количество микросекунд Используются эти функции следующим образом. Перед тем, как сформировать задержку, программа должна при помощи одной из вышеприведенных функций считать текущее значе- ние системного таймера, и сохранить считанное значение в одной из переменных. ПРИМЕЧАНИЕ. Момент времени, когда мы запомнили значение системных часов, и будет считаться началом пе- риода формирования задержки. Затем программа может выполнять любые другие необ- ходимые действия, но периодически она должна проверять текущее время. Для этого она должна периодически считывать значение системного таймера и вычислять разность между текущим и ранее сохраненным значениями времени. Момент, когда эта разница превысит заданное время задержки, счита- ется моментом ее окончания.
106 Программирование ARDUINO. Создаем практические устройства ЗАДАЧА. Доработать одну из программ бегущих огней, из главы 8, исключив из нее операторы delay(), и ис- пользуя для формирования задержки системные часы и функцию mills(). Для начала возьмем за ос- нову программу, приведенную в листинге 8.1. Схема Разумеется, схема бегущих огней останется прежней (см. рис. 8.1). Алгоритм 1. Проверить состояние кнопки. 2. Если кнопка отпущена, запустить процесс однократного пробегания огня слева направо (один раз по очереди загора- ются все восемь светодиодов). 3. Если кнопка отпущена, запустить процесс пробегания огня справа налево. 4. По окончанию процесса однократного пробегания, про- должить выполнение алгоритма сначала. То есть, перейти к пункту 1 данного алгоритма. ПРИМЕЧАНИЕ. По большому счету, алгоритм программы не из- меняется. Меняется лишь способ формирования задержки.
Глава 9. Альтернативные способы формирования задержки 107 Подробнее о том, как это делается, рассмотрим в процессе детального разбора каждой из двух версий представленных ниже программ. Первый вариант| программы 1 Самый простой способ решения вышеописанной задачи — заменить команду delay() другой, специально созданной про- граммой задержки, которая, в свою очередь, будет использо- вать системный таймер и функцию millsQ. Программа, реализующая данный способ формирования задержки, приведена в листинге 9.1. Если сравнить эту про- грамму с оригиналом (см. листинг 8.1), то изменения в новой версии программы минимальны. В программу добавлена новая переменная lastTime (строка 3 программы). В этой переменной мы будем хранить значение системного таймера на момент начала периода фор- мирования задержки. ПРИМЕЧАНИЕ. Обратите внимание на тип этой переменной. Она имеет тип: unsigned long. Переменная, ко- торая должна хранить значение системного счетчика времени, должна иметь именно такой тип, так как размерность системного счетчика времени - четыре байта. Такую же размерность должна иметь и переменная! Следующее изменение новой программы по сравнению со старой — своя собственная специально созданная проце- дура задержки. Процедуру мы назвали ndelayQ и поместили в конце программы в строках 20,21 и 22.
108 Программирование ARDUINO. Создаем практические устройства Листинг 9.1. Бегущие огни с альтернативной процедурой задержки 9 10 11 12 13 14 15 16 17 18 /* Бегущие огни. Без использования функции delay() */ const int buttonPin = 13; // Номер контакта кнопки const int LoopTime = 200; // Задержка шага бег. огня unsigned long lastTime; // Переменная текущ. время void setup () { // Цикл инициализации контактов светодиодов: for (int thisPin = 2; thisPin < 10; thisPin++) { pinMode(thisPin, OUTPUT); digitalWrite(thisPin, HIGH); } // Инициализация контакта кнопки: pinMode(buttonPin, INPUT); void loop () { if (digitalRead(buttonPin)==HIGH) { // Цикл одного пробега в прямом направлении: for (int thisPin = 2; thisPin < 10; thisPin++) // Включение текущего светодиода: digitalWrite(thisPin, LOW); ndelay(LoopTime); // Задержка // Выключение текущего светодиода: digitalWrite(thisPin, HIGH); else { // Цикл одного пробега в прямом направлении: for (int thisPin = 9; thisPin > 1; thisPin--) { digitalWrite(thisPin, LOW); ndelay(LoopTime); // Задержка
Глава 9. Альтернативные способы формирования задержки 109 19 20 21 22 23 digitalWrite(thisPin, HIGH); void ndelay (int delayTime) { lastTime = millis(); while (millisO - lastTime < delayTime) { // тут можно вставить любые команды Процедура имеет один входной параметр delayTime (см. строку 20). При помощи этого параметра в процедуру пере- дается длительность задержки. Функция ndelay() работает следующим образом. В строке 21 считывается текущее значение системного таймера. Считанное значение помещается в переменную lastTime. Далее в строке 22 начинается цикл ожидания. Это уже знакомый нам цикл while (цикл пока...). Условием выполнения цикла является выражение, которое читает значение систем- ного таймера и вычисляет время, прошедшее с момента начала задержки. То есть, вычисляет разность millisQ - lastTime. ПРИМЕЧАНИЕ. Пока это разность не превышает значение пере- менной delayTime, цикл продолжается. В против- ном случае цикл завершается. В результате за- вершается и сама функция ndelayQ. Функция ndelay() используется везде в программе там, где раньше использовалась функция delay(). Мы можем видеть вызов нашей новой функции в строках 13 и 18 программы.
110 Программирование ARDUINO. Создаем практические устройства Применение функции ndelay() дает нам возможность в момент формирования задержки выполнять еще какие-либо дополнительные действия. Команды, выполняющие эти дей- ствия, мы должны разместить вместо комментариев в строках, обозначенных в листинге номером 23. Второй вариант программы Как легко заметить из описания предыдущей версии про- граммы (листинг 9.1), она действительно позволяет выполнять дополнительные действия в момент формирования задержки, но для очень узкого спектра применений. ПРИМЕЧАНИЕ. Тот факт, что добавлять дополнительные ко- манды придется не в основной цикл, а в отдель- ную процедуру, очень сужает возможности этих дополнительных операций. Однако, искусство программирования позволяет скомпоновать программу таким образом, чтобы место, куда можно будет вставить дополнительные коман- ды, оказалось в самом центре основного цикла программы. Для решения этой задачи мы возьмем в качестве исход- ной другую версию программы из главы 8. А именно, про- грамму, приведенную в листинге 8.2. Этот вариант программы использует единый универсальный цикл для прямого и обратного «бега» «огней». Это облегчает нам поставленную выше задачу. В листинге 9.2 представлен вариант программы, в которой в один общий цикл объединены все входящие в нее циклы.
Глава 9. Альтернативные способы формирования задержки 111 Это, во-первых, универсальный реверсивный цикл «пробе- гания огня». Во-вторых, цикл формирования задержки. И все это заключено в главный цикл программы 1оор(). Начальная часть программы (строки 1...11) полностью соответствует программе-оригиналу. Лишь в конце функции setup() добавлены две строки, в которых определяются началь- ные значения двух важных переменных. В строке 12 указателю текущего выхода thisPin присваи- вается начальное значение. А именно, номер самого первого из выходов на светодиод (loopStart). В данном случае первым является выход, с которого будет начинаться «бег вперед». Затем в строке 13 обнуляется переменная lastTime. ПРИМЕЧАНИЕ. Как говорилось выше, переменная lastTime пред- назначена для хранения текущего значения си- стемного таймера. Это значение считывается и помещается в переменную в момент начала формирования периода задержки. Нулевое же значение присваивается этой переменной в том случае, когда программа еще не начала формирование задержки. В результате переменная lastTime используется в этой программе двояко: ♦ для хранения мгновенного значения системного времени; ♦ и еще в одном качестве — в качестве флага. ЧТО ЕСТЬ ЧТО. Флагом в программировании называют перемен- ную или ячейку памяти, которая в процессе ра- боты программы принимает два значения (флаг установлен, флаг сброшен) и служит для регули- рования хода выполнения программы. Где-то в одном месте программы, исходя из каких-либо условий, устанавливается или сбрасывается флаг. А затем в
112 Программирование ARDUINO. Создаем практические устройства других местах, в зависимости от значения флага, меняется ход выполнения программы. Под значением «Флаг установлен» и «Флаг сброшен» можно понимать любые допустимые значения переменной. В нашем случае будет считаться, что флаг сброшен, если значение пере- менной lastTime равно нулю. Когда lastTime больше нуля, то мы будем считать, что флаг установлен. То есть, значение времени, которое будет записано в переменную в процессе работы и будет означать, что «флаг установлен». Значение системного времени в процессе работы про- граммы постоянно меняется. ПРИМЕЧАНИЕ. Приблизительно через 50 дней непрерывной ра- боты системных часов, счетчик системных часов переполнится, и в течение одной миллисекунды его значение будет равно нулю. Но вероятность того, что именно это значение будет запи- сано в переменную lastTime, ничтожно мала. Поэтому исполь- зование этой переменной в качестве флага вполне оправдано. В данном конкретном случае наш флаг, будучи сброшен- ным (равным нулю), индицирует тот факт, что формирование задержки еще не началось. Если же переменная lastTime содер- жит значение текущего времени (отличное от нуля), это озна- чает, что флаг установлен и программа находится в режиме формирования задержки. Итак, в программе существует один универсальный цикл. И этим циклом служит основной цикл программы 1оор(). В начале и в конце основного цикла 1оор() находятся два про- граммных модуля, каждый из которых выполняется только при определенных условиях (далее мы узнаем, при каких). Начальный модуль цикла располагается в строках 15,16 и 17. А конечный модуль расположен в строках 19...26. Условия, при которых выполняется каждый из модулей, определены соот-
Глава 9. Альтернативные способы формирования задержки 113 ветствующими операторами if (в строке 15 и в строке 19, соот- ветственно). Посредине между этими двумя модулями располагается область, куда программист может включить небольшую про- грамму, выполняющую дополнительные действия. Рассмотрим работу основного цикла loopQ программы под- робнее. В строке 15 происходит оценка значения переменной lastTime. Остальные команды начального блока (строки 16 и 17) выполняются только в том случае, если lastTime равно нулю, то есть в том случае, если формирование задержки еще не нача- лось. Если условие выполнено, то сначала в строке 16 соответ- ствующая команда включает текущий светодиод (подавая LOW на выход thisPiri). А в строке 17 считывается и запоминается текущее значе- ние системного времени. После этого значение lastTime уже не равно нулю, а, значит, программа переходит в режим форми- рования задержки. С этого момента начальный блок команд (строки 16 и 17) не выполняется до момента окончания задержки. При этом зажженный в строке 16 светодиод на все время задержки про- должает гореть. За окончание периода задержки отвечает конечный блок основного цикла (строки 19...26). В строке 19 размещено выражение, при помощи которого программа читает текущее значение системного времени, вычисляет время, прошедшее с начала периода ожидания и сравнивает с заданным в пере- менной LoopTime контрольным значением времени задержки. Если время окончания периода задержки еще не наступило, то остальные команды этого блока (строки 20...26) не выпол- няются, и управление передается на начало основного цикла (т. е. к строке 15 программы). Не выполняются также и команды начального блока (строки 16 и 17), так как lastTime не равно нулю.
114 Программирование ARDUINO. Создаем практические устройава Листинг 9.2. Бегущие огни с одним общим циклом 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 /* Бегущие огни без использования функции delay() с одним универсальным общим циклом */ // Определение констант: const int buttonPin = 13; // номер контакта кнопки const int LoopTime =200; // задержка шага бегущ. огня // Определение переменных int loopStart =2; // начало цикла бега int loopstop =10; // конец цикла бега int loopStep =1; // шаг цикла бега int thisPin; // текущий контакт unsigned long lastTime; // текущее время void setup () { // Цикл инициализации контактов светодиодов: for (int thisPin = 2; thisPin < 10; thisPin++) { pinMode(thisPin, OUTPUT); digitalWrite(thisPin, HIGH); } // Инициализация контакта кнопки: pinMode(buttonPin, INPUT); thisPin = loopStart; lastTime = 0; void loop () { if (lastTime == 0) { digitalWrite(thisPin, LOW); lastTime = millisO; // Тут можно выполнять другие действия if (millisO - lastTime > LoopTime) digitalWrite(thisPin, HIGH); thisPin += loopStep; if (thisPin == loopStop) {
Глава 9. Альтернативные способы формирования задержки 115 23 24 25 26 if (digitalRead(buttonPin)==HIGH) { loopStart = 2; loopStop =10; loopStep = 1; } else { loopStart = 9; loopStop = 1; loopStep = -1; thisPin = loopStart; } lastTime = 0; ПРИМЕЧАНИЕ. Поэтому весь основной цикл какое-то время, пока программа находится в режиме формирования задержки, крутится вхолостую. Выполняются лишь дополнительные команды, если вы их вклю- чили вместо комментариев в строке 18. Длительность периода задержки в нашей программе выбрана равной 200 миллисекундам (см. строку 2). Этого времени хватит, чтобы дополнительные команды выполнили достаточно сложный круг задач. Так продолжается до тех пор, пока системное время достигнет такого значения, когда условие оператора if в строке 19 станет равно TRUE (конец задержки). В этом случае выполняется последняя группа операторов, расположенная в строках 20... 26. Сначала оператор в строке 20 гасит текущий светодиод. Затем в строке 21 производится Инкремент (или Декремент в режиме реверса) значения переменной thisPin. To есть изменя- ется номер текущего выхода на светодиод. В строке 22 проверяется, не достигла ли переменная thisPin своего предельного значения. Если достигла, это означает, что «огонь» добежал до конца линейки светодиодов и «бег» нужно начинать сначала. Для этого нужно присвоить переменной thisPin ее начальное значение.
116 Программирование ARDUINO. Создаем практические устройства Но сначала в строках 23 и 24 считывается и проверяется состояние кнопки. Это делается для того, чтобы при измене- нии состояния кнопки можно было бы изменить направление «бега» огней. По результатам оценки состояния кнопки переменным, loopStarty loopStop и loopStep присваиваются значения, соответ- ствующие выбранному направлению «бега» огней: ♦ если кнопка отпущена, то переменным присваиваются значения для «бега вперед»; ♦ если кнопка нажата, то значения для «бега назад». После того, как параметры бега определены, в строке 25 переменной thisPin присваивается начальное значение уже с учетом выбранного направления. В завершении, в строке 26 обнуляется переменная lastTime. Нулевое значение переменной lastTime укажет программе, что формирование периода задержки закончено, и можно начи- нать новую задержку. На этом завершающий блок команд цикла и сам основной цикл заканчиваются, и управление снова передается в начало (то есть к строке 15). Теперь lastTime равно нулю, поэтому начальный блок команд основного цикла на этот раз выпол- няется. В результате зажигается уже новый светодиод, начина- ется новый период задержки, и все повторяется сначала.
ГЛАВА 10 РАБОТА С ПРЕРЫВАНИЯМИ ПО ТАЙМЕРУ Постановка задачи В предыдущих двух главах мы рассмотрели два способа формирования задержки в программе бегущих огней. Способ, описанный в главе 9, в котором для формирования задержки использовались системные часы и функция mills(), позволил нам параллельно с задачей бегущих огней дополнительно выполнять вспомогательные задачи. ПРИМЕЧАНИЕ. Однако самый эффективный способ формиро- вания временных интервалов - использование механизма прерываний. При этом используется новый для нас вид прерывания - прерывание по таймеру. В главе 4 мы использовали другой вид прерываний — внешнее прерывание (прерывание, вызываемое при посту- плении внешнего сигнала). Для вызова прерывания по таймеру используется один из системных таймеров микроконтроллера.
118 Программирование ARDUINO. Создаем практические устройства Микроконтроллер ATmega328 имеет на борту три тай- мера: ♦ таймер 0 (8-ми разрядный); ♦ таймер 1 (16-ти разрядный); ♦ таймер 2 (так же 8-ми разрядный). Каждый из таймеров имеет множество режимов работы, которые могут выбираться программным путем. Для форми- рования интервалов времени обычно используют режим под- счета импульсов от внутреннего тактового генератора. С каждым таймером связано несколько видов прерываний. Чаще всего для формирования временных интервалов исполь- зуется так называемое прерывание по переполнению. Как работает прерывание по переполнению? Счетчик тай- мера подсчитывает тактовые импульсы. Каждый такой импульс увеличивает значение счетного регистра таймера на единицу. Когда оно достигнет максимального значения, возникает пере- полнение таймера. Таймер обнуляется, и счет продолжается с нуля. В момент переполнения вырабатывается сигнал преры- вания. При вызове прерывания, как мы знаем, приостанавли- вается выполнение основной программы, и микроконтроллер переходит к выполнению процедуры обработки прерывания. Частота импульсов на входе таймера выбирается таким образом, чтобы переполнение происходило с нужным нам периодом. ПРИМЕР. Таймер 0 модуля Ардуино, как мы знаем, исполь- зуется как системные часы. Частота такто- вых импульсов, поступающая на его вход, выбра- на так, что переполнение таймера происходит один раз за одну миллисекунду Процедура обработки этого прерывания просто добавляет единицу к значению, хранящемуся в составном 32-х разрядном
Глава 10. Работа с прерываниями по таймеру 119 регистре системного времени, составленном из четырех ячеек, зарезервированных в ОЗУ микроконтроллера. ПРИМЕЧАНИЕ. Используя прерывание по таймеру, легко создать программу, в которой некие действия, происходя- щие в строго фиксированные моменты времени, будут выполняться исключительно в процедуре обработки прерывания. А основной цикл програм- мы останется свободным и может быть исполь- зован для решения любых дополнительных задач. При использовании прерываний по таймеру в модуле Ардуино возникают определенные коллизии. Как уже говори- лось выше, все таймеры модуля уже используются системой. Напомним: ♦ таймер 0 используется в качестве системных часов, отве- чает за ШИМ на цифровых выходах 5 и 6, а также обслужи- вает функции millisQy micros(), delay (); ♦ таймер 1, формирует ШИМ на цифровых выходах 9 и 10; ♦ таймер 2 формирует ШИМ на выводах 3 и 11 и использует- ся при формировании звукового сигнала при помощи ко- манды tone(). Если мы будем использовать один из таймеров для органи- зации прерывания, то закрепленными за ним функциями нам, возможно, придется пожертвовать. Далее, в этой главе мы опишем два способа организации прерывания для работы программы «бегущие огни». ЗАДАЧА. Создать программу «Бегущие огни», в которой для создания временных интервалов будет ис- пользоваться прерывание по таймеру.
120 Программирование ARDUINO. Создаем практические устройава II Схема Схема и на этот раз остается прежней. Она приведена на рис. 8.1. I Используем внешнюю библиотеку прерываний по таймеру Для того, чтобы в своих программах на Ардуино вы могли использовать прерывание по таймеру, проще всего применить соответствующую внешнюю библиотеку. В стандартном пакете такая библиотека отсутствует. Поэтому нужно использовать библиотеку стороннего разработчика. В нашем первом примере мы будем использовать библио- теку MsTimer2. ПРИМЕЧАНИЕ. Описание этой библиотеки приведено на сайте «Уроки программирования Ардуино» (mypractic.ru). Скачать zip-архив, содержа- щий все файлы библиотечного пакета можно на сайте «Мир книг по микроэлектронике» (book.mirmk.ru). Подключить библиотеку MsTimer2 к нашей программе так же просто, как библиотеку Button, которую мы учились под- ключать в главе 6. Библиотека MsTimer2 — это специализиро- ванная библиотека для работы с самым последним таймером модуля Ардуино: таймером 2. После присоединения библиотеки к программе, в распо- ряжении программиста появляются три новые функции. Все три функции являются методами класса MsTimer, поэтому
Глава 10. Работа с прерываниями по таймеру 121 для обращения к каждой из функций используется синтаксис вызова метода языка C++. Ниже приводится описание всех этих трех функций. MsTimer2::set(TimeInt, fnamelnt); Функция инициализации прерывания. Параметр Timelnt — это период вызова прерывания по таймеру. Период задается в миллисекундах. Переменная Timelnt должна иметь тип unsigned long. Параметр fnamelnt — это имя функции обработки преры- вания. Функцию вы должны создать самостоятельно. Эта функ- ция должна удовлетворять требованиям, предъявляемым к ISR функциям (Interrupt Service Routine). ПРИМЕЧАНИЕ. . Подробно эти требования описаны в главе 4. MsTimer2::start() Запускает процесс периодического вызова прерыва- ния по таймеру, определенный оператором MsTimer2::set(). Прерывание вызывается многократно с периодом, который был задан параметром Timelnt MsTimer2::stop() Останавливает процесс вызова прерывания по Таймеру. Исходя из вышеописанного набора функций, мы можем составить алгоритм нашей будущей программы. ПРИМЕЧАНИЕ. 1 N Определимся сразу: весь алгоритм «бегущих ог- ней» должен выполняться исключительно в про- цедуре обработки прерывания. Основной цикл про- граммы должен остаться абсолютно свободным.
122 Программирование ARDUINO. Создаем практические устройства Для определенности при описании алгоритма введем два вспомогательных понятия: ♦ «текущий зажженный светодиод»; ♦ «предыдущий зажженный светодиод». Алгоритм 1. Организовать прерывание по таймеру. Установить период вызова прерывания равным времени одного шага «бегущего огня» (в предыдущих программах эта величина определялась временем задержки). 2. После вызова процедуры обработки прерывания выпол- нить следующие действия: 2.1. потушить предыдущий зажженный светодиод. Если он и так не горит, то команда лишь подтверждает это его состояние; 2.2. считать состояние кнопки и определить направление «бега» в зависимости оттого, нажата она или отпущена; 2.3. перейти к следующему по порядку светодиоду. Если кнопка отпущена, то к следующему в прямом направле- нии, если нажата — к следующему в обратном направ- лении; 2.4. зажечь новый текущий светодиод. 3. Выйти из процедуры обработки прерывания и продолжить выполнение основной программы вплоть до очередного вызова прерывания. II Программа Вариант программы «Бегущих огней», работающий по пре- рыванию от таймера и использующий библиотеку MsTimer2, приведен в листинге 10.1. Рассмотрим программу по порядку.
Глава 10. Работа с прерываниями по таймеру 123 Листинг 10.1. Использование внешней библиотеки прерывания по таймеру 10 11 12 13 14 15 16 /* Пример 7. (вариант 1) Бегущие огни с использованием прерываний по таймеру */ ♦include <MsTimer2.h> // Определение констант: const int buttonPin = 13; // номер контакта кнопки const int LoopTime = 200; // задержки шага бегущ. огня const int loopStart =2; // начало цикла бега const int loopStop =9; // конец цикла бега // Определение переменных: volatile int thisPin; // текущий разряд светодиодов void setup () { // Цикл инициализации контактов светодиодов: for (int thisPin = loopStart; thisPin < = loopStop; thisPin++) { pinMode(thisPin, OUTPUT); digitalWrite(thisPin, HIGH); // Инициализация контакта кнопки: pinMode(buttonPin, INPUT); thisPin = loopStart; // уст. нач. значения указателя // Инициализация таймера и привязка к прерыванию MsTimer2::set(LoopTime, timerlnterupt); // уст. периода MsTimer2::start(); // разрешаем прерывание void loop () {
124 Программирование ARDUINO. Создаем практические устройства 17 18 19 20 21 22 23 24 25 26 // Основной цикл остается свободным // Сюда можно поместить основную программу // на ее фоне будет работать программа бегущ. огней // Обработчик прерывания void timerInterupt() { digitalWrite(thisPin, HIGH); // гасим тек. св.диод if (digitalRead(buttonPin)==HIGH) { // Один шаг бегущ. огня в прямом направлении: thisPin++; if (thisPin > loopStop) thisPin = loopStart; } else { // Один шаг бегущ. огня в обрати, направлении: thisPin—; if (thisPin < loopStart) thisPin = loopStop; digitalWrite(thisPin, LOW); // зажечь следующ. св.диод В строке 1 к тексту программы присоединяется библиотека MsTimer2. В строках 2...5 определяются все необходимые константы программы. Константы buttonPin и LoopTime нам хорошо зна- комы. Так как в данной программе значения начала и конца цикла «пробега огней» (loopStart и loopStop) не изменяются, они оформлены в виде констант, а не переменных. В строке 6 определяется переменная thisPin, которая указывает на текущий обрабатываемый выход светодиода. Изменение значения этой переменной будет производиться в процедуре обработки прерывания, поэтому она объявляется как volatile. Строки 8... 12 функции setup() нам хорошо знакомы из предыдущих программ. Они настраивают все входы и выходы модуля.
Глава 10. Работа с прерываниями по таймеру 125 В строке 13 присваивается начальное значение указателю текущего светодиода thisPin. В последних строках функции setup() мы видим две команды, связанные с настройкой пре- рывания. В строке 14 определяются режимы прерывания. Период вызова прерывания устанавливается равным Loop Time. В каче- стве процедуры обработки прерывания указывается процедура с именем timerlnterupt. В строке 15 дается старт процессу вызова прерывания. ПРИМЕЧАНИЕ. Как видите, основной цикл нашей программы остается абсолютно свободным. Вместо строк, помеченных номером 17, можно вставить прак- тически любую программу, выполняющую любые дополнительные функции. Как мы и обещали, весь алгоритм «бегущих огней» выпол- няется в теле процедуры обработки прерывания. Процедура расположена в конце программы и начинается в строке 18. Сначала, в строке 19 программа гасит текущий светодиод. Этот светодиод обычно горит еще с предыдущего вызова про- цедуры обработки прерывания. Между двумя вызовами пре- рывания всегда горит один из светодиодов. ПРИМЕЧАНИЕ. Период вызова прерываний определяет длитель- ность горения очередного светодиода (то есть длительность одного шага «бега» «огней»).
126 Программирование ARDUINO. Создаем практические устройства Далее программа должна определить, какой светодиод дол- жен загореться следующим. Определяется это так: в строке 20 программа читает и оценивает состояние кнопки. Если кнопка отпущена, перемещаем «огонь» на шаг в пря- мом направлении (строки 21 и 22). Если кнопка нажата, делаем один шаг назад (строки 24 и 25). Шаг вперед и шаг назад очень похожи. Для продвижения в прямом направлении сначала в строке 21 производится инкремент указателя текущего светодиода thisPin. В строке 22 производится оценка нового значения указа- теля. Если это значение превысит верхний предел, то указа- телю присваивается значение нижнего предела («движение» «огня» начинается сначала). При обратном шаге производятся обратные действия. В строке 24 производится декремент указателя светодиода. В строке 25 проверяется, не оказался ли указатель ниже нижнего предела. Если оказался, то ему присваивается зна- чение верхнего предела. В результате номер нового текущего светодиода будет определен. Затем в строке 26 новый текущий светодиод зажигается. И на этом процедура обработки прерывания заканчивается. Микроконтроллер возвращается к выполнению основной программы, а текущий светодиод продолжает гореть до следу- ющего вызова прерывания. Совместное использование таймера 0 На сайте «Роботоша» (robotosha.ru) я нашел очень остро- умное решение, как организовать прерывание по таймеру для своих целей и при этом не нарушить ни одной из стандартных функций языка Ардуино, связанных с использованием тайме- ров. Фокус состоит в том, что все настройки всех трех таймеров,
Глава 10. Работа с прерываниями по таймеру 127 устанавливаемые транслятором среды разработки, остаются в неприкосновенности. Но при этом активизируется еще один вид прерываний, который язык Ардуино не использует. Этот вид прерываний, который предлагается использовать называется «прерывание по совпадению». Прерывание по совпадению также связано с тайме- ром. Например, таймер 0 поддерживает два прерывания по совпадению. Для этого в составе таймера имеется специ- альный регистр, который называется регистр совпадения. Программным способом в этот регистр можно записать некое число — порог совпадения. ПРИМЕЧАНИЕ. Прерывание по совпадению возникает в тот мо- мент, когда содержимое основного счетного ре- гистра таймера окажется равным содержимому регистра совпадения. Меняя содержимое реги- стра совпадения, можно менять момент возник- новения прерывания. Идея, предложенная на сайте «Роботоша», состоит в том, чтобы двояко использовать таймер 0. Как мы уже не одно- кратно говорили, таймер 0 в любой программе, написанной на языке Ардуино, работает в режиме генерации миллисекундных импульсов. За одну миллисекунду таймер успевает просчитать от 0 до своего максимального значения — 255 и затем вызвать прерывание по переполнению. При помощи этого прерывания происходит подсчет системного времени. А вот прерывание по совпадению система Ардуино не использует. Это прерывание мы и будем исполь- зовать для наших нужд, не меняя никаких других параметров таймера.
128 Программирование ARDUINO. Создаем практические устройства ПРИМЕЧАНИЕ. Если в регистр совпадения поместить число, при- мерно равное половине максимального значения счетного регистра таймера, и активизировать пре- рывание по совпадению, то такое прерывание будет вызываться также 1 раз каждую миллисекунду, но со сдвигом во времени от основного прерывания. Сдвиг по времени будет очень полезен для того, чтобы процедуры обработки прерываний не мешали друг другу. У таймера 0 микроконтроллера ATmega323 имеется два реги- стра совпадения. Имя первого из них OCR0A. Его мы и будем использовать для вызова нашего прерывания. Но как же быть с периодом вызова прерывания? Ведь нам нужно, чтобы процедура обработки прерывания в про- грамме «бегущих огней» вызывалась один раз за 200 миллисе- кунд? Да очень просто! В нашей новой программе прерывание будет вызываться каждую миллисекунду, но выполнять нужные нам действия только один раз за 200 вызовов. Как это сделать, сейчас увидим. Для этого разберем подробно текст нового программного примера, приведенный в листинге 10.2. За основу программы в листинге 10.2 взят первый вариант программы, приведенный в листинге 10.1. В новом варианте реализован описанный выше способ формирования преры- вания по таймеру. Нам пришлось сделать совсем небольшие изменения. Разберем по порядку все эти изменения. Для начала из программы исключен оператор #includey так как нам теперь не нужна внешняя библиотека. Полностью изменены команды инициализации прерывания. Новые команды инициализации вы можете видеть в стро- ках 13 и 14 листинга 10.2.
Глава 10. Работа с прерываниями по таймеру 129 Лиаинг 10.2. Использование прерывания по таймеру 0 9 10 11 12 13 14 15 16 /* Бегущие огни, использование прерываний по таймеру О (минимум вмешательства в настройки Ардуино) */ // Определение констант: const int buttonPin = 13; // номер контакта кнопки // Опреление переменных: int loopStart =2; // начало цикла бега int loopStop =9; // конец цикла бега int LoopTime =200; // время задержки шага бегущ. огня volatile int TimeCore =0; // Счетчик таймера volatile int thisPin; // Текущий разряд светодиодов void setup () { // Цикл инициализации контактов светодиодов: for (int thisPin = 2; thisPin < 10; thisPin++) { pinMode(thisPin, OUTPUT); digitalWrite(thisPin, HIGH); // Инициализация контакта кнопки: pinMode(buttonPin, INPUT); thisPin = loopStart; // начальн. знач. указателю ев.диода // Инициализация таймера (режим совпадения) OCR0A = ОхАО; // значение регистра совпадения TIMSK0 |= (1 « OCIE0A); // разрешение прерывания void loop () { // Основной цикл остается свободным // Сюда вы можете поместить основную программу // на ее фоне будет выполняться программа бегущ. огней
130 Программирование ARDUINO. Создаем практические уаройства 17 18 19 20 21 22 23 24 25 26 27 28 29 // Обработчик прерывания SIGNAL(TIMER0_COMPA_vect) { TimeCore++; if (TimeCore>LoopTime) { TimeCore=0; digitalWrite(thisPin, HIGH); // погасить текущ. св.диод if (digitalRead(buttonPin)==HIGH) { // Один шаг бегущ. огня в прямом направлении: thisPin++; if (thisPin > loopStop) thisPin = loopStart; digitalWrite(thisPin, LOW); // зажечь следующ. св.диод else { // Один шаг бегущ. огня в обратном направлении: thisPin—; if (thisPin < loopStart) thisPin = loopStop; digitalWrite (thisPin, LOW); // Зажечь следующий светодиод ПРИМЕЧАНИЕ. Так как для инициализации прерывания мы ис- пользуем нестандартный прием, приходится программировать на уровне регистров.
Глава 10. Работа с прерываниями по таймеру 131 Так, в строке 13 записывается значение в регистр совпаде- ния OCR0A. Как уже говорилось, это значение находится где-то посредине между минимальным и максимальным значениями таймера. ПРИМЕЧАНИЕ. Автор идеи предложил величину OxAF (записано в шестнадцатеричном формате). В десятичном формате OxAF соответствует 175. Я выбрал ее- личину поближе к середине диапазона: ОхАО (то есть 160). При выборе значения, записываемого в регистр совпаде- ния, разумнее всего учитывать соотношение времени выпол- нения обеих процедур обработки прерывания (по переполне- нию и по совпадению). В строке 14 определяется значение разряд OCIE0A в реги- стре маски TIMSKO. Регистр маски TIMSKO микроконтроллера управляет включением и выключением всех видов поддержи- ваемых им прерываний по таймеру 0. Для каждого таймера есть свой регистр маски прерываний. Каждый разряд регистра включает или выключает (маскирует) свой вид прерывания. Если какой-либо разряд равен нулю, соответствующее ему прерывание не разрешено. Чтобы разрешить прерывание, нужно установить соответствующий разряд в единицу. Каждый разряд регистра имеет свое собственное имя. ЧТО ЕСТЬ ЧТО. Разряд, который отвечает за прерывание по совпадению, называется OCIE0A. Выражение в строке 14 программы устанавливает в единицу разряд с номером OCIE0A.
132 Программирование ARDUINO. Создаем практические устройства Если вы желаете досконально изучить синтаксис логиче- ских выражений языка СИ, рекомендую обратиться к книге [1]. Но если говорить кратко, то выражение: TIMSK0 |= (1«ОС1Е0А) выполняет два действия. Составной оператор «|=» выполняет операцию побитового ИЛИ между значением переменной TIMSK0 и результатом выражения в скобках (1«ОС1Е0А), а результат этой операции присваивается переменной TIMSK0. Операция побитового ИЛИ используется во многих язы- ках программирования для установки в единицу одного или нескольких двоичных разрядов числа, оставляя при этом все остальные двоичные разряды без изменений. ЧТО ЕСТЬ ЧТО. Значение в правой части от оператора «\=» на- зывается маской. Маска должна иметь единицы во всех двоичных разрядах, которые в исходном числе (в данном случае - в TIMSK0) необходимо установить в единицу. А те разряды, которые из- менять в исходном числе не нужно, в маске долж- ны быть равны нулю. Смысл выражения в скобках — получить маску с единицей в разряде номер OCIE0A. Для получения такой маски берется число 1 (в двоичном представлении это число, у которого во всех старших разрядах нули и единица в младшем разряде — 00000001). Двоичные разряды этого числа сдвигаются побитно влево. Число сдвигов равно OCIE0A. Следующее изменение программы, приведенной в листинге 10.2 по сравнению с программой из листинга 10.1, — это имя функции обработки прерывания.
Глава 10. Работа с прерываниями по таймеру 133 ПРИМЕЧАНИЕ. Как вы могли заметить, при инициализации пре- рывания мы не определяли имени этой функции. По законам программирования на уровне регистров кон- троллера у такой процедуры имеется фиксированное систем- ное имя. Все функции обработки прерываний всегда имеют имя SIGNALQ, а параметр функции— это системное имя выбранного нами прерывания. В нашем случае имя прерыва- ния TIMER0_COMPA_yect. Достаточно создать в своей программе функцию с таким именем и таким параметром, и вектор прерывания окажется определен. В листинге 10.2 описание этой функции начина- ется в строке 17. Тело функции расположено в строках 18...29. В строке 18 производится инкрементация значения пере- менной TimeCore. Эта переменная используется в качестве счетчика миллисекунд. ПРИМЕЧАНИЕ. Как уже говорилось, процедура SIGNALQ будет вы- зываться каждую миллисекунду, и каждый раз зна- чение счетчика будет увеличиваться на единицу. В строке 19 оператор //сравнивает значение счетчика TimeCore с заданным в начале программы периодом шага «бегущих огней» — LoopTime, которое как раз и равно 200. Операторы в фигурных скобках (строки 20...29) выпол- няются только тогда, когда значение счетчика миллисекунд достигнет значения LoopTime. Пока значение не достигнуто, никаких действий не выполняется, и процедура обработки прерывания заканчивается без последствий.
134 Программирование ARDUINO. Создаем практические устройства Когда значение счетчика миллисекунд TimeCore достиг- нет нужного значения (окажется равным LoopTime), пер- вой же командой (строка 20) счетчик TimeCore обнуляется и выполняются команды, реализующие один шаг «бега огней» (строки 21...29). ПРИМЕЧАНИЕ. Эти команды полностью аналогичны таким же командам из листинга 10.1. После этого процедура обработки прерываний также завер- шается. Следующий раз при вызове процедуры обработки прерывания подсчет миллисекунд начнется сначала. Таким образом, основная часть процедуры обработки прерывания будет выполняться один раз за время, заданное в переменной LoopTime, точно так же как это происходило в первом варианте программы (листинг 10.1).
ГЛАВА 11 ФОРМИРОВАНИЕ ЗВУКА Постановка задачи | В языке Ардуино извлечение звука — достаточно неслож- ная задача. Для этого используется простая и удобная функция tone(). Формат этой функции следующий: Юпе(Контакт, ВысотаТона, Длительность); Параметр «Контакт» — это номер контакта модуля Ардуино, на котором будет генерироваться звуковой сигнал. Параметр «ВысотаТона» — это частота генерируемого сигнала в герцах. Тип значения этого параметра — unsigned int. Параметр «Длительность» — не обязательный. Если он есть, то определяет длительность генерируемого звукового сигнала в миллисекундах. Тип значения — unsigned long. ПРИМЕЧАНИЕ. Если параметр «Длительность» отсутствует, то звуковой сигнал генерируется непрерывно до тех пор, пока не придет команда noToneQ.
136 Программирование ARDUINO. Создаем практические устройства По команде tone() на выбранном контакте вырабатывается цифровой сигнал, представляющий собой меандр (т. е. сигнал у которого длительность импульса равна половине периода). Для формирования сигнала используется таймер 2 микро- контроллера. В каждый момент времени может генериро- ваться только один звуковой сигнал заданной частоты только на одном из контактов. ПРИМЕЧАНИЕ. Если сигнал уже генерируется на каком-либо кон- такте, то повторное использование функции toneQ для этого контакта просто приведет к изменению частоты тона (если конечно пара- метр «ВысотаТона» новой команды изменился). В то же время вызов функции toneQ для любого другого кон- такта не будет иметь никакого эффекта. Сначала нужно выклю- чить звук командой поТопе(), и только после этого можно при- менить tone() для нового контакта. ЗАДАЧА. Разработать схему и программу устройства на основе модуля Ардуино, позволяющего издавать различные звуки. При нажатии на одну из деся- ти кнопок, подключенных к этому устройству на выходе, к которому подключен звукоизлучатель, должен генерироваться звуковой сигнал. Для раз- ных кнопок звук должен иметь разную высоту тона. Когда все кнопки отпущены, звук должен прекратиться.
Глава 11. Формирование звука 137 Схема Вариант схемы для реализации выше описанной задачи при- веден на рис. 11.1. На этой схеме впервые задействованы цифровые контакты О и 1 модуля Ардуино. ПРИМЕЧАНИЕ. Во всех предыдущих схемах мы не использова- ли цифровые контакты 0 и 1, чтобы не мешать процессу обмена информацией по последователь- ному каналу между модулем и компьютером. Но с увеличением количества контактов, необходи- мых для решения поставленных задач, нам все равно придеться когда-то их использовать. С1 ,, 4.7мкф S1...S10 ~L_>ZJ Рис. 11.1. Схема устройства генерации звуковых сигналов
138 Программирование ARDUINO. Создаем практические устройства Рис. 11.2. Внешний вид устройства генерации звуковых сигналов Использование этих контактов для подключения кнопок — одно из самых безопасных решений. Если кнопка не нажата, то она никак не влияет на подключенные к ней цепи. ВНИМАНИЕ. Главное помнить - в момент обмена данными с компьютером ни в коем случае нельзя нажимать на кнопки, подключенные к контактам 0 и 1. В качестве звукоизлучателя в схеме используется любой маломощный динамик. Он прекрасно работает, если его под- ключить через конденсатор емкостью 3...5 микрофарад. Вариант готовой конструкции, собранной на универсальной монтажной плате, приведен на рис. 11.2.
Глава 11. Формирование звука 139 Алгоритм 1. Организовать цикл опроса кнопок. В цикле кнопки должны опрашиваться по возрастанию, начиная с кнопки номер 0 и заканчивая кнопкой номер 9. 2. При обнаружении в процессе опроса первой же нажатой кнопки программа должна прекратить опрос и запустить процесс генерации звука на выходе, куда подключен звуко- излучатель. Высота тона должна быть взята из таблицы. Для каждой кнопки своя нота. При этом перебор кнопок должен быть начат сначала (с кнопки номер 0). 3. Если в результате сканирования всех десяти кнопок ни одна из них не оказалась нажатой, программа должна принуди- тельно прекратить генерацию звука. 4. В любом случае (нажата кнопка или нет) после завершения операций, описанных в пунктах 2 или 3, управление пере- дается к пункту 1 данного алгоритма. В результате выполнения описанного выше алгоритма про- грамма будет постоянно опрашивать все кнопки, при обна- ружении нажатия начнется генерация звука, а опрос кнопок начнется сначала. Если в процессе очередного перебора про- грамма обнаружит нажатие той же кнопки, какая была нажата при предыдущем переборе, то новая команда вызова звукового сигнала только подтвердит прежний режим генерации звука. Если номер нажатой кнопки окажется другим, то тон звуко- вого сигнала изменится. Если же после перебора всех кнопок окажется, что ни одна кнопка не нажата, то генерация звука прекратится. Программа Вариант программы простейшего звукового устрой- ства, реализующий описанный выше алгоритм, приведен в
140 Программирование ARDUINO. Создаем практические устройства листинге 11.1. Далее мы будем детально изучать используе- мые в программе приемы программирования, рассматривая ее построчно. В строке 1 определяется константа SoundPin — номер кон- такта, к которому подключается звукоизлучатель. В строке 2 определяется переменная ButtonPin (номер теку- щей кнопки). При помощи этой переменной программа будет осуществлять перебор кнопок. Интерес для нас представляет строка 3 программы. В этой строке описывается массив. ЧТО ЕСТЬ ЧТО. Массив - это набор однотипных переменных или констант используемых как единый набор дан- ных. Каждый массив имеет свое имя. В данном случае имя массива tabFreq (массив хранит таблицу частот). В квадратных скобках указывается размер массива. В нашем случае размер массива равен 10. Это значит, что массив может хранить десять разных значений. Все значения нашего конкретного массива присваиваются тут же, в строке 3. Значения перечисляются в фигурных скоб- ках, следующих сразу за определением массива после символа «=» (равно). Значения в списке разделяются запятой. Обращение в программе к любому значению из массива происходит по его номеру в формате ИмяМассива[НомерЭлементаМассива]. ПРИМЕЧАНИЕ. Элементы массива нумеруются, начиная с эле- мента номер ноль. Это значит, что массив раз- мерностью 10 состоит из элементов с номерами от 0 до 9.
Глава 11. Формирование звука 141 Листинг 11.1. Программа «Десять кнопок - десять нот» 4 5 10 11 12 13 14 15 /* Сигнализатор звука «десять нот» */ // Определение констант: const int SoundPin = 12; // номер контакта звукоизлучателя // Определение переменных: int ButtonPin; // указатель номера контакта текущ. кнопки // Определение массива констант (частоты нот) const int tabFreq[10] = { 1046, 987, 932, 880, 831, 784, 740, 698, 659, 622 }; void setup () { // инициализируем контакт звука: pinMode(SoundPin, OUTPUT); // инициализируем контакты кнопок: for (ButtonPin=0; ButtonPin<=9; ButtonPin++) { pinMode(ButtonPin, INPUT_PULLUP); void loop() { boolean btFree = true; // сброс флага «кнопки отпущены» // Сканирование кнопок и воспроизведение звука: for (ButtonPin=0; ButtonPin<=9; ButtonPin++) { if (digitalRead(ButtonPin)==LOW) { tone (SoundPin, tabFreq[ButtonPin]); btFree = false; break; if (btFree) noTone(SoundPin);
142 Программирование ARDUINO. Создаем практические устройства После блока команд определения констант и переменных начинается функция начальных установок setup(). В теле функ- ции setup() производится настройка режимов всех контактов модуля. В строке 5 контакт звукоизлучателя переводится в режим OUTPUT. В строках 6 и 7 мы видим цикл, который перебирает все входы от кнопок с 0 по 9 и каждый из них переводит в режим INPUTJ4JLLUP (режим входа с включенным подтягивающим резистором). Далее в программе начинается ее основной цикл 1оор(). В теле функции 1оор() реализуется основной алгоритм программы. В строке 9 определяется локальная переменная btFree. ЧТО ЕСТЬ ЧТО. Локальная переменная - это переменная опре- деленная внутри какой-либо функции. Действие локальной переменной распространяется только на ту функцию, в которой она была определена. Вне этой функции локальная переменная не су- ществует. Переменная btFree имеет тип boolean, то есть принимает значения true или false. В данной программе эта переменная служит флагом «Все кнопки отпущены». В строке 9 сразу после определения флаг устанавливается в true. Далее в строке 10 организуется цикл, который перебирает по очереди входы кнопок и проверяет, не нажата ли одна из них. Как только программа обнаружит нажатую кнопку, флаг сбрасы- вается в false (что сигнализирует в дальнейшем для программы, что не все кнопки отпущены). Если же в конце цикла перебора значение переменной btFree все еще равно true, то это значит, что ни одна кнопка не нажата, и нужно выключать звук. В качестве счетчика цикла перебора кнопок используется переменная ButtonPin (см. строку 10). Ее начальное значение
Глава 11. Формирование звука 143 равно нулю, а цикл выполняется пока ButtonPin меньше или равен девяти. В строке 11 считывается и оценивается состояние текущей кнопки с номером ButtonPin. Если значение состояния кнопки равно LOW (кнопка нажата), то выполняются строки 12,13 и 14. В строке 12 вызывается функция генерации звука на выходе SoundPin. ЧТО ЕСТЬ ЧТО. Частота звука - это значение элемента масси- ва tabFreq с номером, равным значению перемен- ной ButtonPin. В строке 13 сбрасывается переменная btFree («Все кнопки отпущены»), потому что обнаружилась нажатая кнопка. В строке 14 мы видим оператор break. Оператор break досрочно завершает текущий цикл опроса, и оставшиеся кнопки не опрашиваются. Это нужно для того, что бы выполня- лось правило — программа реагирует только на самую первую из нажатых кнопок. Строка 15 программы выполняется уже после цикла пере- бора кнопок. Это оператор //, который проверяет значение флага btFree. Если флаг установлен (его значение равно true), то выполняется оператор поТопе()9 и звук прекращается. Так как переменная btFree имеет логический тип, то составлять из нее логическое выражение не нужно. Ее значение уже само явля- ется условием для оператора //(имеет значение true или false). На этом заканчивается тело основного цикла программы, и управление передается на его начало. Многократно сканируя все кнопки, программа реагирует на их нажатие, издавая раз- личные звуки.
ГЛАВА 12 ВВОД АНАЛОГОВОЙ ИНФОРМАЦИИ Постановка задачи Конструкция модуля Ардуино, а также синтаксис его языка программирования, разработаны таким образом, чтобы макси- мально облегчить работу с аналоговым сигналом. Для ввода аналогового сигнала? Как уже говорилось выше, используются специальные ана- логовые входы. Если говорить о модуле Arduino UNO, то он имеет шесть таких входов. Для чтения уровня сигнала на любом из этих вхо- дов в языке Ардуино имеется специальный оператор analogRead(HoMepBxoda). Параметр НомерВхода может прини- мать следующие значения: АО, А1, А2, A3, А4, А5. Функция analogRead() возвращает уровень сигнала на выбранном входе в относительных единицах. Допустимый диапазон напряжений на любом аналоговом входе от 0 до +5 вольт. При изменении напряжения на входе в этих пределах возвращаемое значение изменяется от 0 до 1024. То есть, если напряжение на входе равно нулю, функция возвращает 0. Если же на входе +5 вольт, то функция возвращает 1024. Как видите, сама задача чтения аналоговых данных проста до примитивности. Поэтому рассмотрим пример, в котором эта задача будет дополнена задачей создания светодиодного индикатора уровня аналогового сигнала.
Глава 12. Ввод аналоговой информации 145 ЗАДАЧА. Создать на основе модуля Ардуино устройство, которое будет считывать напряжение с движка переменного резистора и наглядно отображать считанное напряжение при помощи линейного индикатора, выполненного в виде цепочки свето- диодов. Светодиоды должны загораться в виде светящегося столбика. Чем больше напряжение на входе модуля, тем больше длина столбика. Схема Схема устройства, позволяющая реализовать поставленную выше задачу, приведена на рис. 12.1. Переменный резистор R1 включен по схеме потенциометра между цепью питания (+5 В) и общим проводом. Напряжение с движка потенциометра поступает на вход АО модуля. Светодиоды индикатора подключаются к контактам 2...9 точно R1 10К Рис. 12.1. Схема устройства чтения и индикации аналогового сигнала
146 Программирование ARDUINO. Создаем практические устройства Рис. 12.2. Внешний вид устройства чтения и индикации аналогового сигнала так же, как в схеме бегущих огней. Внешний вид уже собран- ного устройства показан на рис. 12.2. Алгоритм Программа должна представлять бесконечный цикл, в котором будет выполняться следующая последовательность действий. 1. Прочитать напряжение с аналогового входа. Далее мы будем оперировать понятием «считанное значение».
Глава 12. Ввод аналоговой информации 147 2. Подготовить программу к выполнению цикла вывода считанного значения на индикатор. Далее в цикле будет использоваться понятие «плавающего порога». 3. Присвоить плавающему порогу начальное значение рав- ное нулю. 4. В цикле по очереди перебирать все выходы на светодиоды. 5. С каждым шагом перебора увеличивать значение пла- вающего порога на значение, которое мы назовем «шаг порога». Таким образом, с каждым шагом «плавающий порог» будет расти. Шаг порога должен быть выбран таким образом, чтобы за 8 шагов цикла порог изменился бы от О до 1024 (что перекроет весь диапазон считанного значения сигнала). 6. На каждом шаге сравнить считанное значение со значе- нием плавающего порога. 7. Если считанное значение больше плавающего порога, теку- щий светодиод зажечь. 8. В противном случае светодиод потушить. 9. Продолжить перебор до тех пор, пока не будут перебраны все восемь светодиодов. 10. После перебора всех восьми светодиодов перейти к пункту 1 данного алгоритма. В результате выполнения описанного выше алгоритма пла- вающий порог за восемь шагов перебора светодиодов изме- нится от нуля до максимума. Вначале перебора порог будет ниже считанного значения, и светодиоды на каждом шаге будут зажигаться. В какой-то момент плавающий порог пре- высит считанное значение. С этого момента на каждом шаге перебора программа будет тушить текущий светодиод. Чем выше считанное с аналого- вого входа напряжение, тем больше светодиодов загорится. Напряжение на аналоговом входе вы можете менять, повора- чивая вал переменного резистора. При этом длина столбика светящихся светодиодов будет меняться от минимума, когда ни один светодиод не горит, и до максимума, когда горят все светодиоды.
148 Программирование ARDUINO. Создаем практические устройства II Программа Программа, реализующая описанный выше алгоритм, при- ведена в листинге 12.1. Начинается программа с определения всех необходимых констант. В строке 1 определяется имя аналогового входа, к которому подключен движок потенциометра. В строке 2 мы определяем величину шага изменения пла- вающего порога. Так как за восемь шагов порог должен изме- ниться от 0 до 1024, то шаг порога вычисляется так: 1024/8=128. В строках 3 и 4 определяются константы максимального и минимального номера контакта в группе контактов для под- ключения светодиодов. Эти константы используются для орга- низации цикла перебора светодиодов. В теле функции setup() находится цикл, определяющий режимы работы всех выводов для подключения светодиодов. ПРИМЕЧАНИЕ. Все контакты настраиваются на вывод инфор- мации, и на каждом выходе устанавливается вы- сокий логический уровень. То есть, все светодио- ды в начале работы программы оказываются вы- ключенными. Основной цикл программы (функция 1оор()) выпол- няет периодическое считывание сигнала с аналогового входа и вывод считанного значения на светодиодный индикатор. Для начала в строке 11 обнуляется значение плавающего порога iVol. В строке 12 программа считывает уровень входного сиг- нала с аналогового входа analogPin и присваивает считанное значение переменной AnalogVoL
Глава 12. Ввод аналоговой информации 149 Листинг 12.1. Программа ввода и индикации аналогового сигнала б 7 10 11 12 13 14 15 16 /* Ввод и индикация аналогового сигнала */ // Определение констант: const int analogPin = АО; // имя аналогового входа const int StepVol = 128; // шаг разрешения индикатора const int loopStart =2; // начало цикла бега const int loopStop =9; // конец цикла бега // Определения переменных: int thisPin; // текущий разряд светодиодов void setup () { // Цикл инициализации контактов светодиодов: for (thisPin = loopStart; thisPin <= loopStop; thisPin++) { pinMode(thisPin, OUTPUT); digitalWrite(thisPin, HIGH); void loop() { int iVol = 0; int AnalogVol = analogRead(analogPin); for (thisPin = loopStart; thisPin <= loopStop; thisPin++) { if (iVoKAnalogVol) digitalWrite(thisPin,LOW); else digitalWrite(thisPin,HIGH); iVol += StepVol; В строках 13... 16 находится цикл вывода полученного зна- чения на светодиодный индикатор. В строке 13 этот цикл объявляется. В качестве счетчика цикла используется переменная thisPin. В процессе работы
150 Программирование ARDUINO. Создаем практические устройства цикла значение этой переменной изменяется от loopStart до loopStop, перебирая, таким образом, все контакты, к которым подключены светодиоды. В теле цикла происходит следующее. В строке 14 идет сравнение считанного аналогового значения (переменная AnalogVol) и текущего значения плавающего порога (перемен- ная iVol). ПРИМЕЧАНИЕ. Если значение iVol окажется меньше чем значе- ние AnalogVol, то на текущий светодиод (кон- такт thisPin) подается низкий логический уро- вень (LOW) и соответствующий светодиод заго- рается. В противном случае выполняется оператор, следующий за управляющим словом else (строка 15). Этот оператор подает на контакт thisPin высокий логический уровень (HIGH), и соот- ветствующий светодиод гаснет. В строке 16 значение плавающего порога iVol увеличива- ется на величину шага порога StepVol Теперь при следующей итерации цикла считанное значение будет уже сравниваться с новым значением плавающего порога. Итак, в этой главе мы научились вводить значение анало- гового сигнала. Дополнительно мы узнали, как простыми про- граммными средствами можно индицировать уровень анало- гового сигнала при помощи команд цифрового вывода. В следующей главе мы узнаем, как в модуле Ардуино реа- лизован удобный способ аналогового вывода.
ГЛАВА 13 ВЫВОД АНАЛОГОВОЙ ИНФОРМАЦИИ Широтно-импульсная | модуляция I Язык программирования Ардуино имеет команду вывода аналогового сигнала analogWrite()> очень похожую по синтак- сису на команду цифрового вывода digitalWriteQ. Однако, как уже говорилось, при аналоговом выводе на выход поступает сигнал, представляющий из себя прямоугольные импульсы логических уровней (сам импульс имеет высокий логический уровень, в паузе — логический ноль). Частота этих импульсов примерно равна 490 Гц. ЧТО ЕСТЬ ЧТО. Аналоговая составляющая заложена в ширине импульсов, а вернее в соотношении импульса и паузы. Такой метод, как отмечалось ранее, назы- вается «Широтно-импульсной модуляцией» или ШИМ. В английской транскрипции это звучит так: «Pulse-width modulation» (PWM).
152 Программирование ARDUINO. Создаем практические устройства Функция analogWrite() имеет всего один параметр — число в диапазоне от 0 до 255. Чем больше значение параметра, тем шире импульс сигнала ШИМ. При значении, равном 255, импульсы сливаются в постоянный единичный логический уровень. При уменьшении значения входного параметра ширина импульсов уменьшается. Когда параметр окажется равным нулю, импульсы прекращаются, и на выходе устанав- ливается постоянный уровень логического нуля. ПРИМЕЧАНИЕ. Такой способ формирования аналогового уровня при определенных условиях вполне может заменить обычный способ формирования аналогового на- пряжения. Например, любые инерционные приборы (лампочки, моторчики, светодиоды, нагреватель- ные элементы), будучи подключенными через сило- вой ключ к такому выходу, прекрасно работают от сигнала с ШИМ. Даже лучше, чем с выходом, на кото- ром плавно меняется аналоговое напряжение. Дело в том, что ШИМ работает в ключевом режиме, и сило- вой ключ, через который на нагрузку подается основная мощ- ность, в таком случае имеет максимальный КПД. Это происхо- дит потому, что в процессе работы силовой ключ: ♦ либо закрыт, и ток через него равен нулю; ♦ либо полностью открыт, и падение напряжения на ключе минимально. ПРИМЕЧАНИЕ. В обоих случаях рассеиваемая на ключе мощность незначительна. А это значит, что такая схема не потребует громоздкого радиатора, а может и вообще может обойтись без какого либо охлаж- дения. При этом, чем шире импульсы, тем сильнее
Глава 13. Вывод аналоговой информации 153 светится лампочка, больше греется нагреватель, быстрее крутится моторчик (это должен быть мотор постоянного тока). Если же подключаемая нагрузка не предназначена для пита- ния импульсным сигналом, то импульсный сигнал с выхода ШИМ можно пропустить через интегрирующий фильтр, кото- рый превратит импульсный сигнал в аналоговый. Уровень такого сигнала тоже будет зависеть от ширины импульсов. Для вывода сигнала в режиме ШИМ используются аппа- ратные возможности основного микроконтроллера модуля Ардуино. В связи с этим при использовании команды analogWrite() имеется ряд ограничений. ПРИМЕЧАНИЕ. Следует отметить, что аналоговый вывод про- исходит не на аналоговые, а на цифровые кон- такты микроконтроллера. Причем не на все, а только на некоторые. На плате Ардуино контакты, которые поддерживают вывод сигнала в режиме ШИМ, обозначены символом «~». Перед использованием контакта для аналогового вывода не требуется настраивать режим его работы, как это было необхо- димо сделать прежде, чем производить ввод или вывод цифро- вых сигналов при помощи тех же самых контактов. Команда analogWrite() сама автоматически настроит кон- такт в режим ШИМ, и на контакте появятся импульсы заданной длительности. Эти импульсы будут присутствовать на выходе до тех пор, пока: ♦ не поступит другая команда analogWrite(); ♦ на тот же контакт не будет произведен вывод цифрового сигнала при помощи команды diginalWrite(). В модуле Arduino UNO аналоговый вывод возможен на кон- такты 3,5,6,9,10 и 11.
154 Программирование ARDUINO. Создаем практические устройства I Простейший способ аналогового вывода Самый простой способ использования аналогового вывода — создать программу, аналогичную простейшей про- грамме цифрового ввода и вывода, приведенной в главе 4 нашей книги (см. листинг 4.1). Достаточно просто заменить команды digitalRead() и digitalWrite() в той первой программе на команды analogRead() и analogWrite(). А так же следует выбрать другие входной и выходной контакты. Новая программа будет считывать уро- вень напряжения с аналогового входа и передавать его на ана- логовый выход. ЗАДАЧА. Создать схему и программу устройства, которое будет считывать напряжение с переменного ре- зистора, включенного по схеме потенциометра, и передавать считанный уровень командой ана- логового вывода на светодиод. При нулевом на- пряжении на входе светодиод должен полностью погаснуть. При максимальном напряжении на входе - светодиод должен гореть в полную силу. Схема Схема устройства приведена на рис. 13.1. В качестве аналогового входа используется вход АО, а в качестве выхода — цифровой контакт 9.
Глава 13. Вывод аналоговой информации 155 R2 270 О VD1 R1 10К JF- Рис. 13.1. Схема простейшего устройства аналогового ввода-вывода Алгоритм В основном цикле программы многократно выполнять сле- дующие действия: 1. Прочитать аналоговый уровень с входа. 2. Привести значение считанного аналогового уровня в диа- пазоне команды аналогового вывода, учитывая, что диа- пазон аналогового ввода 0...1024, диапазон аналогового вывода 0...255. 3. Вывести приведенный уровень аналогового сигнала на ана- логовый выход. 4. Перейти к пункту 1 данного алгоритма.
156 Программирование ARDUINO. Создаем практические устройства Листинг 13.1. Простейшая программа ввода и вывода аналогового сигнала 5 6 7 /* Простейшая программа аналогового ввода и вывода */ const int analogPin = АО; // номер аналогового входа const int ledPin = 9; // номер контакта светодиода int analogVol; // текущий аналоговый уровень void setup () {} void loop () { // Читаем значение сигнала с аналогового входа: analogVol = analogRead(analogPin); // Выводим аналоговое значение на выход светодиода analogWrite(ledPin, analogVol/4); Программа Простейшая программа аналогового ввода-вывода приве- дена в листинге 13.1. Как видите, эта программа даже проще своего цифрового аналога. В строках 1...3 уже знакомым вам образом определяются две константы и одна глобальная переменная. Константы содержат номера контактов аналогового входа (analogPin) и аналогового выхода (ledPin).
Глава 13. Вывод аналоговой информации 157 ПРИМЕЧАНИЕ. Переменная analogVol предназначена для времен- ного хранения считанного аналогового значения. Так как режимы аналоговых входа и выхода на- страивать не нужно, функция setupQ (строка 3) не содержит никаких команд. В строке 4 программа читает уровень сигнала на входе analogPin и помещает его значение в переменную analogVol В строке 7 считанное значение делится на 4 (приведение диапазонов) и результат от деления записывается на выход ledPin. Более сложный пример | аналоговой индикации | В качестве более сложного примера использования анало- гового выхода предлагаю взять за основу схему из предыду- щего раздела (см. рис. 13.1) и усовершенствовать ее работу, применив плавное зажигание и потухание светодиодов. ПРИМЕЧАНИЕ. Правда, для этого нам придется сократить ко- личество светодиодов до шести и подключить их только к тем выходам, которые работают с ШИМ.
158 Программирование ARDUINO. Создаем практические устройства ЗАДАЧА. Создать схему и программу устройства, считы- вающего напряжение с движка переменного ре- зистора, включенного по схеме потенциометра и выводящего считанное аналоговое значение на светодиодный индикатор, состоящий из 6 свето- диодов. Индикатор должен выводить уровень сиг- нала в виде светящегося столбика разной длины. Чем больше напряжение на входе, тем больше длина светящегося столбика светодиодов. Если напряжение на входе равно нулю, все светодиоды должны потухнуть. При максимальном напря- жении на входе все светодиоды должны гореть. Отображая все изменения длины светящегося столбика, светодиоды должны загораться и по- тухать, плавно изменяя свою яркость, используя для этого аналоговый вывод. Схема Схема устройства, реализующая поставленную задачу, изо- бражена на рис. 13.2. Алгоритм Многократно повторять основной цикл, в котором про- грамма должна выполнять два основных действия: 1. считать уровень входного сигнала на аналоговом входе; 2. выполнить цикл вывода считанного уровня на индикатор, состоящий из шести светодиодов.
Глава 13. Вывод аналоговой информации 159 m юк Рис. 13.2. Схема усложненного устройства аналогового ввода-вывода В свою очередь, алгоритм работы светодиодного индика- тора должен выглядеть следующим образом. 1. Для вывода считанного уровня аналогового сигнала должен производиться перебор всех шести светодиодов индика- тора, по порядку с первого по шестой. 2. Для каждого светодиода определяется яркость его свече- ния. Для этого используется специальное вспомогательное значение — плавающий порог. В процессе перебора све- тодиодов плавающий порог должен изменяться от нуля до максимального значения. 3. На каждом шаге перебора светодиодов считанное значение сравнивается со значением плавающего порога. Разность между этими двумя значениями корректируется (масшта- бируется, смещается по уровню) и выводится на текущий светодиод командой аналогового вывода. Корректировка уровня, выводимого на светодиоды должна обеспечивать такую работу всего индикатора, чтобы с увеличением вход- ного напряжения от нуля до максимума плавно должны
160 Программирование ARDUINO. Создаем практические устройства зажигаться сначала младший светодиод, затем по очереди более старшие. Когда входное считанное напряжение ока- жемся в середине диапазона, свечение всех светодиодов должно быть таково, чтобы самый младший светодиод све- тился максимально. При дальнейшем увеличении вход- ного напряжения по очереди к своему максимуму должны приближаться более старшие светодиоды. При достижении входного напряжения верхнего предела диапазона все шесть светодиодов должны гореть по максимуму. Программа Программа, реализующая описанный выше алгоритм, при- ведена в листинге 13-2. Несмотря на более сложный алгоритм работы, программа также получилась достаточно простая. В строке 1 определяется константа, означающая имя используемого в данной программе аналогового входа. В строке 2 определяется шаг изменения плавающего порога. Значение шага определяется по формуле 255/6. То есть, диапазон входного значения команды analogWrite() делится на количество шагов перебора светодиодов. Результат деления мы округляем до целого, для того, чтобы не усложнять задачу про- цессору работой с десятичными дробями В строке 3 определяется массив констант, представляющий собой таблицу номеров контактов, поддерживающих режим аналогового вывода. Именно к этим контактам подключены наши светодиоды. Таблица нам понадобится для того, чтобы по номеру светодиода находить номер контакта, к которому он подключен. В строке 4 определяется переменная, в которой будет хра- ниться номер текущего светодиода. Как и в первой версии про- граммы, функция setup() остается пустой. В строках 6...9 расположен основной цикл программы. Первая же команда в теле основного цикла (строка 7) читает
Глава 13. Вывод аналоговой информации 161 уровень входного аналогового сигнала с входа analogPin. Считанное значение делится на два и помещается в перемен- ную AnalogVol. ПРИМЕЧАНИЕ. Уменьшение считанного сигнала вдвое делается для того, что бы привести его диапазон к требу- емому диапазону для вывода на светодиоды. Вы спросите: почему делим на два? Ведь, как мы говорили выше, диапазон считанного аналогового сигнала 0...1024, а диапазон выходного — O...255. Диапазоны отличаются в 4 раза! Двойной диапазон нам нужен потому, что при повышении напряжения от нуля до половины максимального значения, младший светодиод должен плавно изменить яркость от пол- ностью потушенного до полностью горящего. При этом самый старший светодиод должен только-только загореться. Еще пол- диапазона нам понадобится, чтобы старший светодиод плавно загорелся до максимума. Итак, в строке 8 начинается цикл перебора светодиодов. Переменная thisLed меняется от 0 до 5, перебирая все 6 свето- диодов. В строке 9 находится оператор аналогового вывода. Он выводит на контакт текущего светодиода с номером thisLed значение сигнала, которое определяется при помощи специ- ально разработанной функции приведения уровня rangeVol(). Номер контакта, куда подключен текущий светодиод, опреде- ляется при помощи массива AnalogPin[]. В качестве указателя массива подставляется номер текущего светодиода thisLed. Аргументом функции rangeVolQ выступает выражение: AnalogVol+StepVol*thisLed.
162 Программирование ARDUINO. Создаем практические устройства Листинг 13.2. Индикация столбиком светодиодов с плавным гашением 10 11 12 13 14 /* Ввод и вывод аналогового сигнала Плавная индикация */ const int analogPin = АО; // номер аналогового входа const int StepVol =42; // шаг разрешения индикатора const int AnalogPin[] = {3,5,6,9/10,11}; // аналог, выходы int thisLed; // текущий разряд светодиодов void setup () {} void loop () { int AnalogVol = analogRead(analogPin)/2; for (thisLed = 0; thisLed < 6; thisLed++) { analogWrite(AnalogPin[thisLed], rangeVol(AnalogVol+StepVol*thisLed)); int rangeVol (int InpVol) { int OutpVol = InpVol - 256; if (OutpVol>255) OutpVol= 255; if (OutpVoKO) OutpVol= 0; return 255 - OutpVol; В этом выражении к уровню считанного с аналогового входа сигнала добавляется текущий уровень плавающего порога. В свою очередь, уровень плавающего порога вычис- ляется путем перемножения номера текущего светодиода на величину шага плавающего порога. Таким образом, на каждом шаге плавающий порог будет выше, а, значит, каждый следую- щий светодиод будет гореть сильнее.
Глава 13. Вывод аналоговой информации 163 Программа приведения уровней расположена в строках 10... 14. Она выполняет следующие три важных преобразо- вания. Преобразование 1. Смещение полученного входного значе- ния вниз для того, чтобы при нулевом уровне AnalogVol ни один светодиод не горел. Так как для последнего светодиода уровень плавающего порога будет равен 42х6=252, то при нулевом зна- чении AnalogVol входное значение функции и будет равно 252. ПРИМЕЧАНИЕ. Значит, именно на эту величину нужно сместить I вниз входной сигнал. Данное действие происходит в строке 11 программы. Только вместо 252 там используется число 256. Почему? А вспомните, как мы получили величину шага плавающего порога 42. Мы округлили до целого (отбросили дробную часть). Чтобы точнее попасть в центр нужного нам диапазона, мы должны добавить несколько единиц к величине смещения. Полученное значение помещается в переменную OutpVol. Преобразование 2. Ограничение диапазона сверху и снизу. Все предыдущие преобразования уровней приведут к тому, что значение переменной OutpVol будет изменяться в диапазоне, выходящем за пределы стандартного (от 0 до 255). Поэтому сначала (в строке 12) производится ограничение этой вели- чины сверху. Оператор // проверяет уровень значения OutpVol. Если OutpVol выше 255, ему присваивается значение 255. Точно также в строке 13 ограничивается уровень снизу. Если содержимое переменной OutpVol после предыдущих преобразований ока- жется меньше нуля, то ему присваивается значение ноль. Преобразование 3. Инвертирование выходного значения. Наши светодиоды подключены таким образом, что загораются при уровне ноль на выходе, а гаснут при единичном уровне.
164 Программирование ARDUINO. Создаем практические устройства Наш же сигнал тем выше, чем выше сигнал на входе. Это зна- чит, что при повышении сигнала на входе интенсивность све- чения светодиода будет не увеличиваться, а уменьшаться. Операция инвертирования исправляет этот недостаток. Инвертирующее выражение включено в команду завершения функции. В строке 14 вы можете видеть команду return (возврат). Этот оператор завершает функцию и возвращает результат ее работы. Возвращаемое значение указывается как параметр команды return. В данном случае оно вычисляется при помощи выражения 255 - OutpVol. Такое выражение переворачивает диапазон значений. При изменении значения OutpVol от 0 до 255, возвращаемое значение будет изменяться от 255 до нуля. В результате работы данной программы при изменении напряжения на входе устройства, на светодиодном индика- торе отображается светящийся столбик переменной длины. Чем больше напряжение, тем длиннее столбик. Причем свето- диоды, составляющие этот столбик, будут плавно загораться с каждым шагом. При уменьшении напряжения на входе длина светящегося столбика будет уменьшаться. При этом с каждым шагом свето- диоды будут тухнуть по очереди, но также плавно. В табл. 13.1 приведены значения, подаваемые на свето- диоды при помощи команды аналогового вывода при разных значениях входного сигнала. ПРИМЕЧАНИЕ. Для наглядности в табл. 13.1 указаны неинвер- тированные значения сигнала (то есть вместо 255 - OutpVol указаны значения OutpVol). По сути, цифры в таблице отражают интенсивность свечения светодиодов: 0 - светодиод потушен, 255 - полностью зажжен. Не забывайте, что на самом деле в программе цифры обратные (вме- сто 0 подается 255, а вместо 255 подается 0).
Глава 13. Вывод аналоговой информации 165 Значения, подаваемые на светодиоды при разных входных уровнях Таблица 13.1 Номер светодиода Светодиод 1 Светодиод 2 Светодиод 3 Светодиод 4 Светодиод 5 Светодиод 6 Порог 0 42 84 126 168 210 Значение сигнала на входе 0 0 0 0 0 0 0 100 0 0 0 0 0 4 200 0 0 0 0 12 54 300 0 0 0 20 62 104 400 0 0 28 70 112 154 500 0 36 78 120 162 204 600 44 86 128 170 212 254 700 94 136 178 220 255 255 800 144 186 228 255 255 255 900 194 236 255 255 255 255 1000 244 255 255 255 255 255
ГЛАВА 14 ПЕРЕДАЧА ДАННЫХ ИЗ АРДУИНО НА КОМПЬЮТЕР II Постановка задачи Еще одна функция, которая легко выполняется при про- граммировании для Ардуино по сравнению с программирова- нием на других языках для микроконтроллеров, — это органи- зация канала передачи данных из модуля Ардуино в компью- тер по последовательному каналу. Программист легко может воспользоваться тем самым каналом, через который происходит загрузка оттранслирован- ной программы в модуль. ПРИМЕЧАНИЕ. . ^ В книге [1] такой вопрос не рассматривался. Ведь задача передачи данных между компьютером и схемой на микроконтроллере предполагает, что на стороне компьютера должна работать специ- альная программа. Она должна принимать и хотя бы отображать то, что передаст микроконтрол- лер. А программы на компьютере - это отдельная большая тема, которую в рамках книги о микро- контроллерах поднимать нецелесообразно.
Глава 14. Передача данных из Ардуино на компьютер 167 Среда разработчика Ардуино уже имеет свои встроенные средства, способные как принять данные от модуля Ардуино, так и передать информацию на модуль. Поэтому мы можем смело поставить следующую простую и достаточно интерес- ную задачу. ЗАДАЧА. Разработать схему и программу устройства, которое должно иметь десять кнопок для вво- да цифр (помеченные как «1», «2», и т. д. до «9»), плюс еще одну кнопку «Ввод». При нажатии циф- ровых кнопок в последовательный канал долж- ны передаваться коды соответствующих цифр, а при нажатии на кнопку «Ввод» на компьютер должна передаваться команда перевода строки. Передаваемые цифры мы будем наблюдать при помощи инструмента «Монитор порта», входя- щего в состав среды разработки Ардуино. Схема Вариант схемы, которую мы будем использовать для реали- зации вышеописанной задачи, приведен на рис. 14.1. ПРИМЕЧАНИЕ. В связи с тем, что наша программа использует последовательный канал, мы не стали исполь- зовать контакты 0 и 1 для подключения кнопок. Кнопки ввода цифр подключены к контактам 2...11. Кнопка «Ввод» подключена к контакту 12. Такая схема подключения клавиатуры позволяет программе перебирать все 11 кнопок одним цик- лом перебора.
168 Программирование ARDUINO. Создаем практические устройства S1...S11 Ввод/ Рис. 14.1. Схема устройства для передачи цифр в последовательный порт Перед тем, как приступить к формулированию алгоритма, следует отметить, что последовательный USB канал модуля Ардуино работает в режиме эмуляции СОМ порта. Как уже говорилось выше, получать и просматривать переданные из Ардуино данные мы собираемся при помощи Монитора порта, встроенного в среду разработки Ардуино. Монитор порта, как и любая программа «монитор», работает исключительно с кодами символов в ASCII кодировке. Монитор отображает символы, коды которых поступают по последовательному каналу. Поэтому при нажатии цифровых кнопок наш модуль Ардуино должен передавать в последова- тельный канал ASCII коды символов цифр (см. табл. 14.1). А при нажатии кнопки «Ввод» в последовательный порт должны передаться два символа: ♦ символ возврата каретки (код 13); ♦ символ перевода строки (код 10).
Глава 14. Передача данных из Ардуино на компьютер 169 ASCII коды символов цифр Символ Код «0» 48 «1» 49 «2» 50 «3» 51 «4» 32 «5» 53 «6» 54 «7» 55 «8» 56 «9» 57 Таблица 14.1 Возврат каретки 13 Перевод строки 10 Алгоритм 1. Начать бесконечный цикл перебора всех 11 кнопок. 2. При обнаружении первой же нажатой кнопки перейти к процессу вывода кода, соответствующего этой кнопке через последовательный канал в компьютер. Для этого: 2.1. выполнить задержку антидребезга контактов; 2.2. выполнить цикл ожидания отпускания кнопки (осу- ществлять процесс передачи данных на компьютер лучше всего в момент отпускания кнопки); 2.3. сформировать небольшую защитную задержку на время дребезга при отпускании кнопки, и затем пере- дать код в последовательный порт; 2.3.1. Если была нажата одна из цифровых кнопок, передать код кнопки. 2.3.2. Если нажатой оказалась кнопка «Ввод», передать коды перевода строки и возврата каретки. 2.4. сформировать защитную задержку на время передачи информации по последовательному порту; 2.5. продолжить цикл перебора кнопок сначала, передав управление на пункт 1 данного алгоритма. Программа Вариант программы, реализующий описанный выше алго- ритм, приведен в листинге 14.1. Рассмотрим программу по порядку.
170 Программирование ARDUINO. Создаем практические устройства Листинг 14.1. Программа «Цифры на Монитор» 4 5 б 7 10 11 12 13 14 15 16 17 /* Передача данных на компьютер по USB каналу */ // Константы const int nPinMin =2; // Первый из контактов кнопок const int nPinMax = 12; // Последний из контактов кнопок // Переменные: byte NumberPin; // Номер контакта текущей кнопки void setup () { // инициализируем контакты кнопок на ввод: for (NumberPin=nPinMin; NumberPin<=nPinMax; NumberPin++) { pinMode(NumberPin, INPUT_PULLUP); } Serial.begin(9600); //, Инициализация послед, канала void loop() { // Сканирование кнопок и воспроизведение звука: for (NumberPin=nPinMin; NumberPin<=nPinMax; NumberPin++) { if (digitalRead(NumberPin)==LOW) { delay(20); while (digitalRead(NumberPin)==LOW) {}; delay(20); if (NumberPin!=12) Serial.print(NumberPin-2,DEC); else Serial.println( * *); delay(20); break;
Глава 14. Передача данных из Ардуино на компьютер 171 В строках 1 и 2 происходит определение двух констант: ♦ номера первого контакта из группы контактов для под- ключения кнопок; ♦ номера последнего контакта из группы контактов для под- ключения кнопок. В строке 3 определяется переменная NumberPin, в которой мы будем хранить указатель на текущий контакт при переборе кно- пок. В функции setup() в строках 5 и 6 расположен цикл, задаю- щий режимы работы для всех контактов кнопок. Новая для нас команда расположена в строке 7. Эта команда инициализирует последовательный канал. Все команды работы с последовательным каналом являются производными от набора команд Serial. Команда Serialbegin(cKopocmb) подготав- ливает последовательный порт для его использования в вашей программе. В момент инициализации задается скорость, с которой будет работать последовательный порт. ПРИМЕЧАНИЕ. Скорость порта в компьютере и в модуле Ардуино должны быть одинаковы. Вы должны зайти в на- стройки порта в компьютере и узнать, какая скорость порта там установлена. В программе на Ардуино нужно установить такое же зна- чение скорости. Скорость может принимать одно значение из стандартного набора: ПО; 150; 300; 600; 1200; 2400; 4800; 9600;
172 Программирование ARDUINO. Создаем практические устройства 14400; 19200; 38400; 57600; 115200. ПРИМЕЧАНИЕ. Но чаще всего в современных компьютерах по умолчанию порт настроен на 9600. После setup() начинается основной цикл программы 1оор(). В теле функции 1оор() в строке 9 инициализируется цикл пере- бора кнопок. Фактически, цикл занимает все тело функции 1оор(). Цикл перебирает контакты со 2 по 12, к которым под- ключены кнопки с «0» по «9» и кнопка «Ввод». Первая команда в теле цикла (строка 10) — это чтение и оценка состояния кнопки, подключенной к текущему входу. ПРИМЕЧАНИЕ. Строки 11...17 выполняются только в том слу- чае, если кнопка окажется нажатой. В строке 11 вызывается задержка антидребезга. В строке 12 расположен цикл ожидания отпускания кнопки. В строке 13 — первая защитная задержка. В строке 14 производится оценка текущего номера кон- такта. Номер контакта проверяется на условие «не равно 12». К контакту номер 12 подключена кнопка «Ввод». Если условие выполнено (нажатой оказалась не кнопка «Ввод»), то в последовательный порт передается код кнопки (команда Serial.print(NumberPin-2,DEC) в строке 14). В против- ном случае в последовательный порт передается пустое значе- ние с командой перевода строки и возврата каретки (строка 15).
Глава 14. Передача данных изАрдуино на компьютер 173 ПРИМЕЧАНИЕ. Как видно из программы, передача данных в по- следовательный порт производится при помощи двух разных команд. Это аналоги команды printQ и println(), которые имеются в любой версии языка СИ. Формат команд следующий: Serial.print(3Ha4eHue, Формат); SeriaLprintln(3Ha4eHue, Формат); Параметр «Значение» может быть любого из стандартных типов (см. Приложение 2), но данные любого типа преобразо- вываются и передаются в виде текста. ПРИМЕР. Число 32.43 передается как строка «32.43». В ка- честве значения также можно передавать стро- ку символов, например, «Hello word». ВНИМАНИЕ! Недостаток транслятора Ардуино в том, что строки, написание кириллицей, передаются не- правильно. Переданные по последовательному каналу строки, записанные русскими буквами, при отображении в мониторе будут выглядеть как набор иероглифов. При необходимости, вы все же можете передать кириллический текст, но вам придется самостоятельно правильно опреде- лять и передавать в порт код каждой буквы. Параметр «Формат» в функциях Serial.print() и Serial. println() не обязательный. Он предназначен для изменения способа отображения данных. Допустимые значения, BIN (дво-
174 Программирование ARDUINO. Создаем практические устройства ичный), ОСТ (восьмеричный), DEC (десятеричный), HEX (шест- надцатеричный). Для вещественных (дробных) чисел параметр «Формат» задает количество знаков после запятой. Пример: Serial.print(78, BIN) выводит «1001110» Serial.print(78, ОСТ) выводит «116» Serial.print(78, DEC) выводит «78» Serial.print(78, HEX) выводит «4Е» Serial.print(l. 23456,0) выводит «1» Serial.print(l. 23456,2) выводит «1.23» Serial.print(l.23456,4) выводит «1.2346» В строке 14 программы команда Serial.print() передает в последовательный порт номер нажатой кнопки в виде деся- тичного числа. Номер нажатой кнопки вычисляется из номера текущего контакта путем уменьшения его на 2. Если номер текущего контакта равен 12, то выполняется команда Serial. println() в строке 15. Команда Serial.println() аналогична команде Serial.print(), но отличается тем, что печать содержимого параметра «Значение» завершает передачей символов возврата каретки и перевода строки. ПРИМЕЧАНИЕ. Так как в нашем случае ничего, кроме возвра- та каретки, передавать не нужно, параметр «Значение» содержит один символ пробела. ВНИМАНИЕ! Функция Serial.println() не позволяет печатать пустую строку. Поэтому параметр «Значение» должен содержать хотя бы один любой символ! В нашем случае это пробел.
Глава 14. Передача данных из Ардуино на компьютер 175 После передачи данных в последовательный порт наша программа переходит к строке 16. В этой строке находится команда формирования защитной задержки. Далее, в строке 17 мы видим команду break. По этой команде программа досрочно выходит из цикла перебора кнопок. То есть, текущий цикл перебора кнопок преры- вается. Однако функция 1оор() продолжает выполняться. Поэтому весь процесс повторяется. То есть управление пере- дается на строку 9 программы и перебор кнопок начинается сначала. ПРИМЕЧАНИЕ. В результате работы программы при каждом обнаружении нажатой кнопки ее код передается в последовательный порт. Повторной передачи кода уже нажатой кнопки удается избежать бла- годаря циклу ожидания отпускания кнопки. Пока кнопка нажата, сканирование кнопок приоста- новлено. Для того чтобы увидеть поступающие из модуля Ардуино данные на компьютере, нужно, находясь в среде разработки Ардуино, запустить Монитор порта. Это можно сде- лать: ♦ либо при помощи меню: Инструменты/Монитор порта; ♦ либо при помощи иконки [р] в панели инструментов (в правом верхнем углу окна программы). В открывшемся окне монитора при нажатии на цифро- вые кнопки, подключенные к модулю Ардуино, в поле приема данных будут появляться цифры, соответствующие нажатым кнопкам. Цифры выстроятся в число. После нажатия на кнопку «Ввод» произойдет перевод строки, и следующие цифры будут выводиться уже в новой строке. Как это выглядит на экране, показано на рис. 14.2.
176 Программирование ARDUINO. Создаем практические устройства сомз <4rduino/6enu«no Uoo) \^£lj; fc! i 21D 2 5 Нет коша строки -г 9600 боа Рис. 14.2. Окно Монитора порта с результатами работы программы ПРИМЕЧАНИЕ. В заключение, приведем несколько полезных заме- чаний по поводу использования контактов 0 и 1. Возможна ситуация, когда для решаемой вами задачи может потребоваться больше, чем 11 кнопок. С некоторыми ограничениями и оговорками вы все же сможете использовать два этих контакта для подключения кнопок. ПРИМЕЧАНИЕ. Контакт номер 0 в нашем случае использовать проще всего. Этот контакт связан с цепью, ко- торая используется для передачи данных от компьютера в модуль Ардуино. Конфликт может возникать, только если случайно нажать на кнопку в момент загрузки программы в модуль. При работе программы данные от компьютера в модуль не передаются, поэтому кнопка будет работать без проблем.
Глава 14. Передача данных из Ардуино на компьютер 177 ПРИМЕЧАНИЕ. Контакт номер 1 можно использовать только в крайнем случае. В нашей программе это воз- можно. Передача данных в компьютер у нас происходит после отпускания кнопки. В программе даже предусмотрена защит- ная задержка. То есть, в момент передачи данных контакты кнопки гарантированно будут не замкнуты. Данные будут переданы без искажений. Но само нажатие кнопки воспринимается последователь- ным каналом как передача пустого значения (символа про- бела). Если в нашей программе для подключения одной из кнопок использовать контакт 1, то при нажатии на эту кнопку в окне монитора порта перед символом номера кнопки будет выводиться лишний пробел. В некоторых программах, для некоторых задач это вполне допустимо. Тем более, что такая ситуация может быть легко обработана программно.
ГЛАВА 15 ПЕРЕДАЧА ДАННЫХ С КОМПЬЮТЕРА НААРДУИНО Постановка задачи В предыдущей главе мы передавали информацию от модуля Ардуино на компьютер. В этой главе мы будем решать обрат- ную задачу. ПРИМЕЧАНИЕ. Процесс приема данных из последовательно- го канала отличается от процесса передачи. Последовательный канал микроконтроллера сра- зу после его инициализации переходит в режим ожидания данных от компьютера. Как только придет байт данных, электроника последова- тельного канала записывает этот байт в специальный буфер обмена. Все процессы по приему байтов в буфер обмена выполняются в фоновом режиме с использованием прерыва- ния, параллельно и не зависимо от основной программы. И
Глава 15. Передача данных с компьютера на Ардуино 179 программисту ничего, кроме инициализации порта, для этого делать не нужно. Буфер обмена может хранить до 64 байт. Для чтения и использования данных из буфера в языке Ардуино имеются две функции: SerialavailableQ — возвращает количество принятых байтов в буфере. Serial.read() — читает один байт из буфера. Возвращает -1 (минус один), если байты закончились. Попробуем создать программу, которая будет получать команды от компьютера по последнему порту и выполнять в зависимости от принятых данных те либо иные действия. Проще всего управлять генерацией звука. И так, ставим задачу! ЗАДАЧА. Создать программу, которая должна получать из компьютера сигналы в виде кодов нот (от О до 9). Получив код ноты, программа должна сфор- мировать звуковой сигнал с высотой тона, соот- ветствующего номеру ноты. При этом длитель- ность каждого звука должна иметь некую фикси- рованную длину. Схема Исходя из условий поставленной задачи, наша схема должна состоять всего лишь из одного звукоизлучателя, подключенного к одному из цифровых контактов модуля Ардуино. Подключим его точно так же, как в схеме музыкальной шкатулки. В результате схема нашего устройства будет выглядеть так, как показано на рис. 15.1.
180 Программирование ARDUINO. Создаем практические устройства VF1 С1 м 4.7мкф Рис. 15.1. Схема устройства, управления звуком с компьютера Алгоритм 1. Проверить, есть ли данные в буфере данных. 2. Если буфер не пуст, извлечь очередной байт данных из буфера. 3. Используя принятый код, вычислить номер ноты. 4. Воспроизвести звуковой сигнал с высотой тона, взятой из таблицы частот, используя номер ноты. При вызове звука задать фиксированную длительность. 5. Одновременно с формированием звука сформировать задержку с длительностью в полтора раза большей, чем длительность ноты (чтобы между соседними звуками была пауза). 6. Продолжить выполнения алгоритма сначала, перейдя к строке 1 данного алгоритма.
Глава 15. Передача данных с компьютера наАрдуино 181 Программа | НР1же приведена программа, реализующая вышеописанный алгоритм (см. листинг 15.1). Как видите, это одна из самых простых программ, приведенных в данной книге. В строке 1 программы определяется константа, указываю- щая на номер контакта звукоизлучателя. В строке 2 описан массив, содержащий таблицу частот десяти основных нот. Далее, в строке 3 начинается описание основного цикла setup(). В теле основного цикла всего две команды. Команда в строке 4 устанавливает режим работы контакта звукоизлучателя. В строке 5 расположена уже знакомая нам команда иници- ализации последовательного канала. В строке 6 начинается описание основного цикла про- граммы — функции 1оор(). Тело функции содержит два вложен- ных оператора if. Первый из них (строка 7) считывает количество приня- тых байтов в буфере обмена и проверяет это количество. Если в буфере есть хотя бы один байт, выполняются операторы в строках 8,9,10,11. В строке 8 находится выражение, которое извлекает оче- редной символ из буфера, вычисляет код ноты и помещает этот код в переменную InpCod. Для извлечения кода используется функция Serial.read(). Для вычисления номера ноты из кода вычитается число 48. ПРИМЕЧАНИЕ. Дело в том, что данные из компьютера в мо- дуль Ардуино, так же, как и данные из Ардуино в компьютер, передаются в виде кодов символов в кодировке ASCII. Если из ASCII кода цифр (см. табл. 14.1) вычесть 48, мы получим число, равное самой этой цифре (0,1,2 и т. д.). Это же значение будет соответствовать и номеру ноты.
182 Программирование ARDUINO. Создаем практические устройства Листинг 15.1. Программа «Звук по команде с компьютера» б 7 8 9 10 11 /* Получение команд с компьютера */ // Определение констант: const int SoundPin =12; // контакт звукоизлучателя //частоты нот const int tabFreq[10] = { 1046, 987, 932, 880, 831, 784, 740, 698, 659, 622 }; void setup () { pinMode(SoundPin, OUTPUT); // режим контакта звука Serial.begin(9600); void loop () { if (Serial.available()>0) { int InpCod = Serial.read()-48; if ((InpCod>=0)&(InpCod<=9)) { tone(SoundPin,tabFreq[InpCod],200); delay(300); В результате, полученный номер ноты окажется записан- ным в переменную InpCod. Далее, в строке 9 проверяется, попадает ли вычисленный код в диапазон от 0 до 9. Это нужно для того, чтобы программа не реагировала на другие, не цифровые символы. В круглых скобках оператора //находится выражение, которое реализует сложное условие. Выражение состоит из двух отдельных выражений, каждое из которых заключено в свои круглые скобки:
Глава 15. Передача данных с компьютера на Ардуино 183 ♦ первое выражение проверяет переменную на соответ- ствие нижней границе диапазона (InpCod должно быть больше или равно нулю); ♦ второе выражение точно так же проверяет верхнюю гра- ницу (InpCod должно быть меньше или равно 9). Оператор & (И) объединяет эти два условия. Благодаря нему все выражение истинно, если истинны оба входящих в него условия в круглых скобках. Если номер ноты удовлетво- ряет всем этим условиям (то есть попадает в нужный диапа- зон), то выполняются строки 10 и 11. В строке 10 находится команда формирования звука. Высота тона берется из таблицы tabFreq[], знакомой нам по программе «Десять нот». В качестве указателя массива исполь- зуется значение переменной InpCod. Длительность звукового сигнала выбрана равной 200 миллисекундам. В строке 11 формируется задержка в 300 миллисекунд. ПРИМЕЧАНИЕ. Дело в том, что в то время, когда Ардуино фор- мирует звук, программа продолжает свою ра- боту. Для того чтобы программа не запустила формирование нового звукового сигнала в то вре- мя, пока еще не закончилось формирование те- кущего, нужно выдержать паузу. Эта пауза в на- шем случае выбрана на 100 миллисекунд больше, чем длительность звука. Это гарантирует паузу между двумя последовательными звуковыми сиг- налами в 100 миллисекунд. Основной цикл программы 1оор() выполняется бес- конечно, при этом каждый раз проверяя содержимое буфера обмена и воспроизводя звуки согласно считанным из буфера кодам. Для того, что бы проверить работу приведенной выше про- граммы нужно:
184 Программирование ARDUINO. Создаем практические устройства Отправить |■ Нет конца строки » 115200 бо,с Рис. 15.2. Передача данных из окна монитора порта ♦ подключить модуль Ардуино к компьютеру; ♦ запустить среду разработки IDE; ♦ открыть окно монитора порта, как описано в предыдущей главе; ♦ набрать в верхней строке окна монитора команду (то есть вписать туда цифру от 0 до 9); ♦ нажать кнопку «Отправить» в верхнем правом углу окна. ПРИМЕЧАНИЕ. Можно также просто после набора цифры на- жать клавишу «Enter». Строка отправки кода очистится, и код отправится по последователь- ному каналу Получив код ноты, модуль Ардуино издает звуковой сиг- нал выбранного музыкального тона. Вы можете написать целую строку цифр (как показано на рис. 15.2) и послать их в модуль за один раз (одно нажатие кнопки «Отправить» или клавиши «Enter»). Ардуино воспроизведет все введенные ноты по порядку, разделяя их паузой в 100 миллисекунд.
ГЛАВА 16 МУЗЫКАЛЬНАЯ ШКАТУЛКА Постановка задачи В главе 11 мы познакомились с оператором toneQ и узнали, что при помощи этого оператора мы легко можем сформиро- вать на любом из выходов звуковой сигнал любой частоты в звуковом диапазоне и с заранее заданной длительностью. Из этого следует, что для того, чтобы Ардуино воспроизвел мелодию, нужно просто сформировать последовательность звуков (нот) нужной частоты (высоты тона) и длительности. Частоты всех звуков, составляющих мелодию можно поместить в массив (что такое массив мы уже знаем). В другой массив можно поместить все длительности звуков. И это будет самый простой способ извлечения мелодий. ПРИМЕЧАНИЕ. И действительно, такой способ довольно часто применяется для простых программ, воспроизво- дящих одну мелодию. Мы же предлагаем вам рас- смотреть более сложную задачу Предложенный выше способ хранения нот мелодии очень расточителен. Для хранения частоты звукового сигнала нужна переменная или даже массив переменных с типом значения
186 Программирование ARDUINO. Создаем практические устройства unsigned int. Этот тип значения занимает в памяти два байта. Кроме того один байт потребуется для хранения значения дли- тельности. ПРИМЕЧАНИЕ. Автор этих строк еще в самом первом издании книги [1] предложил и реализовал способ кодиров- ки нот мелодии, при котором для хранения одной ноты программа использует всего один байт. И сейчас, вашему вниманию предлагается программа, которая реализует тот же самый способ кодирования нот, но уже средствами языка Ардуино. Для начала опишем суть предлагаемого способа коди- рования. Во-первых, вместо частоты звукового сигнала нужно использовать просто номер ноты. Как известно, для большин- ства простых мелодий достаточно двух, в крайнем случае, трех октав. Одна октава содержит семь основных и пять дополнитель- ных нот. То есть, для простых задач будет вполне достаточно, если мы будем использовать чуть больше тридцати нот. ПРИМЕЧАНИЕ. Поэтому предлагается из всего нотного стана выбрать 32 ноты самого популярного диапазона и пронумеровать их по порядку Тогда, для хране- ния в памяти мелодий мы можем использовать лишь номера нот, и нам потребуется значитель- но меньше памяти. На рис. 16.1 изображен фрагмент музыкальной клавиатуры (первая и вторая октава) с указанием кодов нот, которые мы будем использовать далее в нашей музыкальной программе.
Глава 16. Музыкальная шкатулка 187 На рис. 16.2 показано, как выгля- дит та же кодировка на нотном стане. Для простоты на рис. 16.2 показаны только основные ноты (ноты белых клавиш). Дополнительные ноты (черные клавиши) на нотном стане изображаются с использованием диезов или бемолей. Присвоенные им коды вы легко можете увидеть на рис. 16.1, на котором принцип кодировки нот выглядит гораздо нагляднее. Все ноты (основные и дополнительные) нумеруются по порядку. ВНИМАНИЕ! 23 ^■■■В 21 ■МИН 19 ■■■■■ 16 ^ИНН 14 ■■■■■ 11 ■■■■■1 9 ^НИНН 7ЬнВНВ I 4 ванн 2 ^HHHH 24 20 18 17 15 13 1? 10 8 6 5 3 1 Рис. 16.1. Коды нот на клавиатуре Под номером ноты в приведенных выше рассуж- дениях подразумевается номер высоты тона ноты. Такая же ситуация наблюдается в вопросе кодирования длительности звучания ноты. Рис. 16.2. Коды нот на нотном стане
188 Программирование ARDUINO. Создаем практические устройства ПРИМЕЧАНИЕ. В музыке не используется произвольная длитель- ность. Величина длительности музыкальных зву- ков подчиняется строгим законам гармонии. В музыке конкретное значение длительности не так важно. Длительность меняется в зависимо- сти от темпа исполнения. Решающее значение имеет соотношение между величинами разных длительностей. В музыке существуют следующие термины, определяющие длительность звуков (и пауз): ♦ Целая; ♦ Половинная; ♦ Четверть; ♦ Восьмая; ♦ Шестнадцатая и т. д. Достаточно пронумеровать каждую их этих типов длитель- ности и использовать в кодировке мелодий код длительности ноты, а не ее значение. В табл. 16.1 приведены коды всех воз- можных длительностей, которые мы будем использовать далее в программе. ПРИМЕЧАНИЕ. Обратное направление нумерации вызвано жела- нием использовать уже готовые таблицы кодов мелодий из программы-первоисточника, приве- денной в 11]. А там большей длительности соот- ветствовал больший номер. Для того чтобы облегчить задачу кодировки длительностей ваших мелодий, на рис. 16.3 показано, как обозначается дли- тельность ноты в нотной грамоте. Ноты разной длительности отличаются хвостиками.
Глава 16. Музыкальная шкатулка 189 Кодировка длительностей Таблица 16.1 Код 7 6 5 4 3 2 1 0 Длительность 1(целая) 1/2 (половинная) 1/4 (четверть) 1/8 (восьмая) 1/16 (шестнадцатая) 1/32 (тридцать вторая) 1/64 (шестьдесят четвертая) 1/128 (сто двадцать восьмая) Величина в миллисекундах 2560 1280 640 320 160 80 40 20 О 7 JJhh 6 5 Рис. 16.3. Кодировка длительностей Предложенный выше способ коди- ровки высоты тона и длительности ноты позволяют закодировать любую ноту всего одним байтом. Как известно, один байт состоит из восьми единич- ных разрядов (битов). Три старших бита мы будем исполь- зовать для хранения кода длительно- сти ноты, а пять оставшихся битов — для хранения кода высоты тона. Вот как будет выглядеть двоичное число, в котором закодирована нота с кодом длительности 6 (половин- ная длительность) и кодом высоты тона 9 (соль диез первой октавы). 11001001=201 I £- Код ноты (9) ^— Код длительности (6) В десятичном представлении значение вышеприведенного кода равно 201. Используя описанный выше способ, можно закодировать все ноты любой мелодии.
190 Программирование ARDUINO. Создаем практические устройства Однако кроме нот, любая мелодия содержит еще и паузы. Длительность пауз в музыке подчиняется тем же правилам, что и длительность звуков. ПРИМЕЧАНИЕ. Поэтому при кодировке мелодий мы будем пред- ставлять паузу, как ноту без звука. Логично и удоб- но присвоить такой ноте-паузе код, равный нулю. Вот как будет выглядеть полный код паузы с четвертной длительностью (код длительности 5). 10100000=160 / £- Код ноты (0) *-— Код длительности (5) Теперь для того, чтобы закодировать какую-либо мело- дию, нужно составить цепочку кодов. В цепочку должны вхо- дить коды каждой ноты и каждой паузы кодируемой мелодии. Полученные коды нужно записать в массив. ПРИМЕЧАНИЕ. Массив может иметь тип значений - byte. Таким образом, каждый элемент такого массива займет в памяти всего один байт. Теперь, когда мы определились со способом кодировки мелодий, мы можем сформулировать задачу.
Глава 16. Музыкальная шкатулка 191 ЗАДАЧА. Доработать программу «Десять нот», пред- ставленную в главе 11 этой книги (листинг 11.1) таким образом, чтобы при нажатии любой кноп- ки звучала мелодия. Для каждой кнопки мелодия должны быть разная. Назовем такую программу «Музыкальная шкатулка». Схема Как следует из поставленной выше задачи, схема должна оставаться прежняя, предложенная в главе 11 (см. рис. 11.1). Алгоритм 1. Просканировать клавиатуру и определить номер самой первой нажатой кнопки. 2. По номеру нажатой кнопки найти в памяти таблицу закре- пленной за этой кнопкой мелодии. 3. Начать цикл воспроизведения мелодии. Этот цикл дол- жен перебирать по порядку коды нот из таблицы мелодии, выбранной в пункте 2 алгоритма. 4. Каждый извлеченный из таблицы код ноты разложить на код тона и код длительности в соответствии с правилами кодирования, описанными в начале этой главы. 5. Если код тона не равен нулю, сформировать звуковой сиг- нал с высотой тона, соответствующей коду тона и длитель- ностью, соответствующей коду длительности. Код тона и код длительности использовать те, которые были получены в пункте 4 данного алгоритма.
192 Программирование ARDUINO. Создаем практические устройства 6. Если код тона равен нулю, сформировать паузу длительно- стью, соответствующей коду длительности, полученному в пункте 4 данного алгоритма. 7. Повторять пункты 2...6 настоящего алгоритма много- кратно, до тех пор, пока нажата кнопка, выбранная в пункте 1 алгоритма. 8. После того, как перебор кодов нот в таблице мелодии закончен, перейти к началу алгоритма, то есть к пункту 1 и начать новое сканирование кнопок. 9. Если при сканировании кнопок окажется, что ни одна кнопка не нажата, запретить формирование звука и так же перейти к началу алгоритма. Программа Программа, реализующая поставленную задачу и опи- санный выше алгоритм, приведена в листинге 16.1. За основу этой программы взята программа «Десять нот» (см. листинг 11.1). ПРИМЕЧАНИЕ. Та часть программы, которая занималась скани- рованием кнопок, оставлена без изменений. Но теперь, после определения номера нажатой кнопки про- грамма вызывает добавленную в конец программы проце- дуру воспроизведения мелодии. Кроме того, в начало новой программы (в область определения констант и переменных) добавлен набор массивов констант. Эти массивы представляют собой таблицы мелодий (10 таблиц по одной на каждую мело- дию), и несколько вспомогательных таблиц, назначение кото- рых мы узнаем по мере рассмотрения текста программы.
Глава 16. Музыкальная шкатулка 193 В строке 1 программы определяется константа — номер контакта для подключения звукоизлучателя. В строках 2 и 3 определяются две глобальные переменные. И константа и переменные нам уже знакомы по программе «Десять нот». А вот дальше начинаются определения таблиц. В строке 4 определяется таблица длительностей нот. Как видите, таблица выполнена в виде массива, в который помеща- ются значения всех используемых в программе задержек. Это те самые значения, которые мы уже приводили в табл. 16.1, в графе «Величина в миллисекундах». Значение задержки с кодом 0 помещается в элемент массива номер ноль, значение задержки с кодом 1, в элемент номер 1 и так далее. Теперь, для того чтобы, например, получить величину задержки с кодом 4 нужно просто обратиться к элементу массива номер 4 (вот так: tabz[4J). В строке 5 определяется таблица частот. При помощи этой таблицы программа может быстро определить частоту (высоту тона) любой ноты. Например, частота ноты 10 определяется как tabFreq[10]. В строках 6... 15 размещены таблицы мелодий. Таблицы с именами mell[], mel2[] и т. д. содержат коды нот разных мело- дий. Каждый такой код ноты в таблице содержит номер дли- тельности и номер ноты, закодированные способом, описан- ным выше в начале этой главы. В конце каждой таблицы мело- дий стоит код 255. Этот код служит признаком конца мелодии. ПРИМЕЧАНИЕ. Все мелодии имеют разную длину, поэтому без специального маркера, индицирующего окончание каждой конкретной мелодии, не обойтись. В строке 16 мы видим определение еще одной таблицы. Она определена как *tabm[]. Это таблица ссылок на таблицы мелодий. Она помогает программе обратиться к нужной мело- дии по ее номеру. Элементами таблицы *tabm[] имеют новые
194 Программирование ARDUINO. Создаем практические устройства для нас типы значения — значение типа «ссылка». Механизм ссылок — это тоже фирменная особенность языка СИ. До сих пор мы имели дело с переменными и массивами, имеющими стандартные типы значения: ♦ число; ♦ символ; ♦ булево; ♦ логическое и т. п. Ссылочная переменная работает с ссылками на другие переменные, функции, массивы и т. д. ПРИМЕР. Элемент номер 0 массива *tabm[] содержит ссылку на таблицу mell. Элемент номер 1 со- держит ссылку на таблицу те12, элемент номер 2 содержит ссылку на таблицу mell и т. д. Вы легко увидите это, взглянув на строку 16 программы. В каждой таблице, на которую ссылается соответствующая ссылка, содержится набор кодов одной из мелодий. Так, напри- мер, в таблице те13 записаны коды мелодии, которая должна воспроизводиться при нажатии кнопки номер 3. Используя таблицу ссылок, можно обраться к любой таблице мелодий. И даже к любому элементу любой таблицы мелодий. ПРИМЕР. Получить значение любого элемента табли- цы мелодий те13[] можно следующим образом: 1аЬт[2][НомерЭлемента]. Результат выражение tabm[2] — это ссылка на таблицу те14[]. Поэтому вместо те14[НомерЭлемента] можно написать 1аЬт[2][НомерЭлемента].
Глава 16. Музыкальная шкатулка 195 Допустим, мы хотим обратиться к элементу 5 таблицы мелодий те14[]. Как видно из строки 9 программы, значение этого элемента равно 143 (не забывайте, что нумерация эле- ментов начинается с нуля). А, значит, что мы можем приров- нять следующие три выражения: tabm[2][5] = mel4[5] = 143 Существует и другой способ работы со значениями типа «ссылка». Ссылка на массив одновременно является ссылкой на его первый элемент (элемент номер ноль). Значение типа «ссылка» можно присвоить какой-нибудь переменной. Но эта переменная должна и сама должна иметь ссылочный тип. То есть нам нужна переменная типа «ссылка». Например, в нашей программе мы присваиваем значе- ния различных элементов массива tabm[] переменной nota. Предположим, что нам нужно присвоить переменной значение элемента номер 2. Для этого просто запишем nota = tabm[2]. Теперь, при помощи переменной nota, можно обращаться к любому элементу массива, на который указывает эта ссылка (массива mel3[J). ПРИМЕЧАНИЕ. Как говорилось выше, сразу после присвоения зна- чения ссылка, записанная в переменную nota, ука- зывает на элемент массива номер ноль (теЩО]). Для того чтобы получить значение элемента, на который указывает ссылка, используется оператор «*» (звездочка). В нашем случае выражение *nota будет равно 132 (*nota = 132, см. строку 8 программы). Если теперь увеличить значение ссылки на единицу, то ссылка укажет на следующий элемент массива. Увеличить зна- чение ссылки можно, просто прибавив к нему единицу или применив команду инкрементации: nota++.
196 Программирование ARDUINO. Создаем практические устройства Листинг 16.1. Программа «Музыкальная шкатулка» /* Музыкальная шкатулка */ // Определение констант const int SoundPin =12; // номер контакта звукоизлучателя // Определение глобальных переменных int ButtonPin; // указатель текущего входа от кнопки boolean btFree; // флаг «все кнопки отпущены» // Таблица длительностей нот const unsigned int tabz[] = {20,40,80,160,320,640,1280,2560}; // Таблица - частоты нот const unsigned int tabFreq[]={0,587,622,659,698,740,784, 831,880,932,988,1047,1109,1175,1245,1319,1397,1480, 1568,1661,1760,1865,1976,2093,2217,2349,2489,2637, 2794,2960,3136,3322,3520}; // Таблицы мелодий //В траве сидел кузнечик byte mell[] = {109,104,109,104,109,108,108,96,108,104, 108,104,108,109,109,96,109,104,109,104,109,108,108,96, 108, 104,108,104,108,141,96,109,111,79,79,111,111,112, 80,80,112,112,112,111,109,108,109,109,96,109,111,79,79, 111,111,112,80,80,112,112,112,111,109,108,141,128,96,255}; // Песенка крокодила Гены byte mel2[] = {109,110,141,102,104,105,102,109,110,141, 104,105,107,104,109,110,141,104,105,139,109,110,173,96, 114,115,146,109,110,112,109,114,115,146,107,109, 110, 114,112,110,146,109,105,136,107,105,134,128,128,102,105, 137,136,128,104,107,139,137,128,105,109,141,139,128,110, 109,176,112,108,109,112,144,142,128,107,110,142,141,128, 105,109,139,128,173,134,128,128,109,112,144,142,128,107, 110,142,141,128,105,109,139,128,173,146,128,255};
Глава 16. Музыкальная шкатулка 197 10 11 12 //В лесу родилась елочка byte mel3[] = {132,141,141,139,141,137,132,132,132,141, 141,142,139,176,128,144,146,146,154,154,153,151,149,144, 153,153,151,153,181,128,96,255}; // Happy births day to you byte mel4[] = {107,107,141,139,144,143,128,107,107,141, 139,146,144,128,107,107,151,148,146,112,111,149,117,117, 148,144,146,144,128,255}; // С чего начинается родина byte mel5[] = {99,175,109,107,106,102,99,144,111,175,96, 99,107,107,107,107,102,104,170,96,99,109,109,109,109, 107,106,143,109,141,99,109,109,109,109,104,106,171,96, 99,111,109,107,106,102,99,144,111,143,104,114,114,114, 114,109, 111,176, 96,104,116,112,109,107,106,64,73,143, 107,131,99,144,80,80,112,111,64,75,173,128,255}; //Из кинофильма «Веселые ребята» byte mel6[] = {105,109,112,149,116,64,80,148,114,64,78, 146,112,96,105,105,109,144,111,64,80,145,112,64,81,178, 96,117,117,117,149,116,64,82,146,112,64,79,146,144,96, 105,105,107,141,108,109,112,110,102,104,137,128,96,105, 105,105,137,102,64,73,142,105,107,109,64,75,137,96,105, 105,105,137,102,105,142,112,64,82,180,96,116,116,116, 148,114,112,142,109,64,78,146,144,96,105,105,107,141, 108,109,112,110,102,104,169,96,96,255}; // Улыбка bytemel7[] = {136,133,170,168,131,134,133,131,193,160, 133,136,138,138,138,140,143,141,140,138,173,200,138, 140,173,128,140,138,133,136,134,202,160,140,138,141,136, 140,138, 138,136,131,133,163,161,160,129,132,136,136, 136,136,168,141,129,132,131,131,131,163,131,132,136, 134,136,137,136,134,136,137,173,171,160,141,136,139, 137,137,137,169,141,134,137,136,136,136,168,141,132, 131,132,134,137,136,134,132,134,169,168,160,141,136, 139,137,137,137,169,141,134,137,136,136,136,168,141, 132,131,132,134,137,136,134,132,134,168,193,160,255};
198 Программирование ARDUINO. Создаем практические устройства 13 14 15 16 17 18 19 20 21 22 // Гимн России bytemel8[] = {136,173,136,96,106,172,133,96,101,170, 136,96,102,168,129,96,97,163,131,96,101,166,134,96,104, 170,140,141,175,136,177,143,96,109,175,140,136,173,140, 96,106,172,133,96,101,170,136,96,102,168,129,96,97,173, 140,138,168,224,255}; // Спят усталые игрушки byte mel9[] = {168,128,133,168,128,133,168,168,166,165, 163,165,200,143,145,143,145,212,168,128,131,168,128, 131,168,166,165,163,161,165,200,145,148,145,148,214, 168,168,170,168,177,177,170,168,161,161,166,168,170, 170,168,166,165,165,166,165,232,198,195,193,160,224,255}; // Гамма byte mellOU = {129,130,131,132,133,134,135,136,137,138, 139,140,141,142,143,144,145,146,147,148,149,150,151, 152,153, 154,155,156,157,158,159,128,159,158,157,156, 155,154,153,152,150,149,148,147,146,145,144,143,142, 141,140,139,138,137,136,135,134,133,132,131,130,129, 128,255}; // Таблица начал всех мелодий byte *tabm[] = {mell, mel2, mel3, mel4, mel5, mel6, mel7, mel8, mel9, mellO}; void setup () { // Установка режимов контактов: pinMode(SoundPin, OUTPUT); // контакт звука // цикл установки для контактов кнопок: for (ButtonPin=0; ButtonPin<=9; ButtonPin++) { pinMode(ButtonPin, INPUT PULLUP); void loop() { // Сканирование кнопок и воспроизведение звука: btFree = true; // флаг «все кнопки отпущены»
Глава 16. Музыкальная шкатулка 199 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 for (ButtonPin=0; ButtonPin<=9; ButtonPin++) if (digitalRead(ButtonPin)==LOW) { playMelody (ButtonPin); btFree = false; break; if (btFree) noTone(SoundPin); // Функция воспроизведения мелодии void playMelody (int numberMelody) { byte fnota; // код тона ноты byte dnota; // код длительности ноты byte *nota; // ссылка на текущую ноту nota = tabm[numberMelody]; StartMelody: if (digitalRead(ButtonPin)==HIGH) return; // если кнопка отпущена, закончить if (*nota==0xFF) return; // проверка на конец мелодии fnota = (*nota)&0xlF; // определяем код тона dnota = ((*nota)»5)&0x07; // опред. код длительности if (fnota!=0) { tone (SoundPin, tabFreq[fnota],tabz[dnota]); delay (tabz[dnota]+10); else noTone(SoundPin); // если пауза выкл. звук delay (tabz[dnota]); nota++; goto StartMelody;
200 Программирование ARDUINO. Создаем практические устройства Теперь выражение *nota будет возвращать значение 141 (*nota = 141). Так,увеличивая значение ссылочной переменной, можно перебрать все элементы таблицы мелодии. ПРИМЕЧАНИЕ. Далее в программе мы будем использовать имен- но этот, последний, метод работы со ссылками. Функция setup() занимает строки 17...20 и не отличается от аналогичной функции из программы «Десять нот». Функция 1оор() (строки 21...28) также является копией аналогичной функции из программы «Десять нот», но с одним небольшим отличием. ПРИМЕЧАНИЕ. В строке 25 вместо вызова команды toneQ теперь производится вызов функции воспроизведения ме- лодии playMelodyf). В качестве параметра в эту функцию передается номер нажатой кнопки. Функция playMelody() расположена в строках 29...46 про- граммы. Функция начинается с определения локальных пере- менных (строки 30...32). Переменная fnota предназначена для временного хранения кода высоты тона ноты, переменная dnota будет хранить код длительности ноты. Обратите внимание на описание переменной в строке 32. В этой строке описывается переменная nota. Она будет исполь- зоваться для хранения ссылки на текущий элемент таблицы, выбранной для воспроизведения мелодии.
Глава 16. Музыкальная шкатулка 201 ПРИМЕЧАНИЕ. По этой причине переменная имеет тип «ссыл- ка», на что указывает символ звездочки перед именем переменной в строке определения. В строке 33 переменной nota как раз и присваивается зна- чение ссылки. Ссылка извлекается из таблицы tabm[]. В строке 34 находится метка. ЧТО ЕСТЬ ЧТО. Метка - это символьное имя, идентификатор, отмечающий место в программе, куда программа может осуществлять переход по команде goto. Метка состоит из имени метки, которое заканчивается дво- еточием. Имя метки также должно соответствовать стандарт- ным правилам компьютерных имен. В строке 35 происходит считывание и проверка состояния текущей кнопки. Номер этой кнопки был передан в функцию playMelody() при ее вызове через параметр numberMelody. СОВЕТ. В процессе воспроизведения мелодии нужно по- стоянно контролировать, нажата ли еще эта кнопка, для того, что бы немедленно прекратить воспроизведение мелодии при ее отпускании. Оператор if в строке 35 проверяет значение, определяющее состояния кнопки, и, если, это значение равно HIGH (кнопка отпущена), выполняется команда return. В данном случае команда return приводит к немедленному завершению функ- ции playMelody(), а, значит, к прекращению процесса воспро- изведения мелодии.
202 Программирование ARDUINO. Создаем практические устройства Если в результате проверки выяснится, что кнопка еще нажата, то программа начинает процесс воспроизведения мело- дии. И начинается этот процесс проверкой конца мелодии. ПРИМЕЧАНИЕ. Дело в том, что при воспроизведении мелодии программа будет постоянно возвращаться к метке StartMelody, считывая ноту за нотой. И в определенный момент мелодия закончится (оче- редной код ноты окажется равным 255). Прежде, чем начинать расшифровывать код ноты, нужно проверить, не конец ли это мелодии. Поэтому в строке 36 происходит проверка на конец мело- дии. Если код ноты равен OxFF (а это шестнадцатеричный ана- лог десятичного числа 255), то это означает, что мелодия закон- чилась. Если это так, выполняется оператор return в строке 36, и процедура playMelody() завершается. Если мелодия не окончена, то программа приступает к выделению из кода ноты: ♦ кода высоты тона (строка 37); ♦ кода длительности (строка 38). Схематически процесс разделения показан на рис. 16.4. Код тона получается путем наложения на код ноты маски OxlF (см. выражение в строке 37). Число OxlF имеет нули в трех старших разрядах и единицы во всех остальных (00011111). Оператор «&» означает логическую операцию побитового «И». ЧТО ЕСТЬ ЧТО. Операция «И» называется логическим умноже- нием.
Глава 16. Музыкальная шкатулка 203 Код тона I О I О I О I d4 I d3 I d2 I d1 I dO I Код ноты I d7 I d6 Код длительности V5 d3 I d2 d1 I dO I Ш. d2 I dt I dO I Pmc. 16.4. Процесс преобразования кодов ноты Если логически перемножить два бита, то результат умно- жения будет равен единице только в том случае, когда оба перемножаемых бита равны единице. Если хотя бы один из перемножаемых битов равен нулю, то и результат равен нулю. Если код ноты побитно перемножить с кодом маски, то в результате такого умножения те биты, которые в коде маски равны нулю, обнулятся и в байте результата. Те биты, которые в маске равны единице, в байте результата останутся такими же, как в исходном коде ноты (см. рис. 16.4). Примерно так же вычисляется код задержки (строка 38). Только предварительно биты кода ноты сдвигаются вправо на 5 шагов, как показано на рис. 16.4. Сдвиг производится при помощи уже знакомого нам оператора «»». После сдвига на код накладывается маска 0x07. Шестнадцатеричное число 0x07 содержит нули в пяти стар- ших двоичных разрядах и единицы в трех младших (00000111). Операция логического умножения «&» сдвинутого кода ноты и кода маски сбрасывает старшие биты, и, таким образом, результатом этих преобразований является код длительности ноты (см. рис. 16.4). Теперь нужно воспроизвести звук с полученными выше параметрами. Но сначала нужно определить — это звук или пауза.
204 Программирование ARDUINO. Создаем практические устройства Для этого в строке 39 проверяется значение перемен- ной fnota. Если оно не равно нулю, то программа формирует звуковой сигнал с высотой тона fnota и длительностью dnota (строка 40 программы). ПРИМЕЧАНИЕ. Конкретное значение частоты сигнала берется из таблицы tabFreq[], куда подставляется код высоты тона. Конкретное значение длительно- сти звука берется из таблицы tabz[], куда под- ставляется код задержки. Далее нужно приостановить работу программы на время, пока звучит звуковой сигнал. Вы, наверное, помните, что команда tone() работает автономно, параллельно с выполне- нием основной программы. ВНИМАНИЕ! Если не предусмотреть задержку, то программа запустит формирование следующего звука тог- да, когда еще не закончил звучать предыдущий звук. Поэтому в строке 41 программы предусмотрена команда задержки. Эта команда формирует задержку на 10 миллисе- кунд большую, чем заданная в функции tone() длительность звукового сигнала. Запас по длительности введен для того, чтобы между отдельными звуками всегда была небольшая микропауза и звуки не сливались в процессе воспроизведения мелодии. Если код высоты тона (fnota) оказался равен нулю, то про- грамма в строках 43,44 выполняет формирование музыкаль- ной паузы с кодом длительности dnota. Для этого в строке 43
Глава 16. Музыкальная шкатулка 205 выполняется команда принудительного выключения звука поТопе()9 а в строке 44 формируется пауза. Команда поТопе() применяется в этом месте программы на всякий случай, для надежности. Вообще-то все звуки к этому моменту и так должны прекратиться. В строке 45 производится инкремент значения перемен- ной nota. В результате ссылка, хранящаяся в этой переменной, теперь будет указывать на следующую ноту мелодии. Затем, при помощи оператора goto, расположенного в строке 46, управление передается по метке StartMelody. Это означает, что выполнение программы продолжится со строки 35. То есть с первой из команд, расположенных после метки StartMelody. Таким образом, образуется цикл чтения и воспроизведения кодов нот мелодии, который прекратится: ♦ либо в строке 36, если мелодия закончится (встретится код HOTbiOxFF); ♦ либо в строке 35, если обнаружится, что кнопка с номером ButtonPin отпущена.
ГЛАВА 17 КОДОВЫЙ ЗАМОК 1 Постановка задачи Наша книга подходит к концу, поэтому новый пример должен быть достаточно сложным с точки зрения алгоритма. Вашему вниманию предлагается кодовый дверной замок с необычной логикой работы. Данный пример не только поможет разобраться в некоторых новых приемах програм- мирования для Ардуино, но может также найти практическое применение в вашем хозяйстве. Этот кодовый замок был раз- работан как пример еще для самого первого издания книги [1]. В замке применен необычный способ набора кодовой ком- бинации. На первый взгляд замок устроен так же, как обычно выглядят подобные устройства. Замок имеет клавиатуру, состо- ящую из десяти кнопок, предназначенных для ввода цифр от О до 9. Также имеется переключатель режимов: «Запись кода — Работа». К выходу электронной части замка подключен элек- тронный ключ, управляющий механизмом открывания замка (реле или соленоид). ПРИМЕЧАНИЕ. Необычность замка кроется в алгоритме набора кодовой комбинации. Кодовая комбинация может состоять не только из нажатий той или иной кнопки, но и из сочетаний двух или нескольких одновременно нажатых кнопок.
Глава 17. Кодовый замок 207 При вводе кода программа просто запоминает любые изменения состояния клавиатуры. Затем, в режиме «Работа», вы должны повторить все манипуляции, которые вы выпол- няли при вводе кодовой последовательности в режиме «Запись кода» Для того чтобы было понятнее, приведем пример кодовой комбинации. Допустим, сначала вы нажимаете кнопку «1». Это первое изменение состояния клавиатуры (было ничего не нажато, стало — нажата одна кнопка). Это состояние запоми- нается в памяти. Далее, к примеру, не отпуская кнопки «1» вы можете, нажать кнопку «3». Это следующее изменение состояния клавиатуры (нажаты одновременно кнопки «1» и «3»). Оно так же запо- минается в памяти. Далее, вы можете, например, отпустить кнопку «1». Или наоборот, не отпуская кнопки «1» и «3» нажать, к примеру, еще и кнопку «8». И все эти изменения последова- тельно запоминаются в памяти. ПРИМЕЧАНИЕ. Не важно, быстро или медленно вы набираете коды. Запоминается лишь последовательность изменения состояний. Окончанием процесса набора кодовой комбинации является ситуация, когда все кнопки окажутся отпущенными, и в течение одной секунды не будет нажата ни одна кнопка. Выше описанным способом набирается кодовая комби- нация как в режиме «Запись кода», так и в режиме «Работа». Но в режиме «Записи кода» все эти манипуляции с клавиа- турой запоминаются в памяти модуля Ардуино, а в режиме «Работа» правильно повторенная кодовая комбинация откры- вает замок. Такой необычный алгоритм ввода кодовой комбинации существенно усложняет задачу подбора кода для злоумышлен- ников.
208 Программирование ARDUINO. Создаем практические устройства ПРИМЕЧАНИЕ. При желании, вы всегда можете использовать электронный замок и привычным способом. Просто при наборе кодовой последовательности не нажимайте несколько кнопок одновременно. Теперь разберемся, куда же мы будем записывать набран- ную в режиме записи кодовую последовательность. До сих пор мы использовали два вида памяти. Во-первых, оперативное запоминающее устройство (ОЗУ). В нем хранятся значения всех переменных программы. Во-вторых — программная память (здесь хранится наша исполняемая программа, а также постоянные константы). ОЗУ отличается высоким быстродействием, но информация в ОЗУ хранится лишь до тех пор, пока на модуль подано напряжение питания. При отключении питания все содержимое ОЗУ теряется. ЧТО ЕСТЬ ЧТО. Программная память (Flesh) - это электриче- ски стираемое ПЗУ (постоянное запоминающее устройство). Программа и константы записыва- ются в эту память в процессе заливки програм- мы и не изменяются в процессе ее работы. Для долговременного хранения данных, вводимых пользо- вателем, в современных микроконтроллерах имеется специ- альный вид памяти, которая называется EEPROM. ЧТО ЕСТЬ ЧТО. EEPROM (Electrically Erasable Programmable Read- Only Memory) - электрически стираемое пере- программируемое постоянное запоминающее устройство).
Глава 17. Кодовый замок 209 В модуле Ардуино (а, точнее, в микроконтроллере ATmega323) такая память также имеется. Для того чтобы ваша программа могла работать с этим видом памяти необходимо использовать специальную библиотеку. Эта библиотека, в отличие от использованных нами до сих пор библиотек, входит в стандартный пакет среды разработки IDE. Библиотека так и называется «EEPROM». Как добавить библиотеку к программе мы рассматривали в главе 6 дан- ной книги. После подключения библиотеки в распоряже- нии программиста появляются следующие дополнительные функции: ♦ EEPROM.write(HoMepH4euKU9 Значение); — запись байта данных в ячейку EEPROM памяти. Возвращаемого значе- ния нет; ♦ ЕЕР11ОМ.геас1(НомерЯчейки); — чтение байта данных из ячейки памяти EEPROM. Возвращает значение считанного байта. Как видите, запись в энергонезависимую память EEPROM производится побайтно. Поэтому для записи значений пере- менных тех типов, которые занимают в памяти более одного байта (см. Приложение 2), вы должны самостоятельно вычис- лить значение каждого байта и записать каждое такое значе- ние в отдельную ячейку EEPROM. Чаще всего приходится иметь дело с переменными, кото- рые занимают в памяти два байта. Для этого случая в языке Ардуино существуют специальные функции, позволяющие разбивать значение на байты, а затем объединять их обратно, в одно значение. Так, функция highByte() получает из значения переменной значение ее старшего байта. Функция lowByte() получает значе- ние младшего байта. А функция word() объединяет старший и младший байты в одно значение. Как это делается, мы увидим ниже при разборе уже готовой программы. А пока поставим задачу.
210 Программирование ARDUINO. Создаем практические устройства ЗАДАЧА. Создать схему и программу на основе модуля Ардуино, реализующую вышеописанную идею электронного замка. Устройство должно иметь десять кнопок для ввода кода обозначенных цифрами от «0» до «9», переключатель режима «Запись кода - Работа», а также электронный ключ для управления исполнительным механиз- мом замка. Схема Схема электронного замка приведена на рис. 17.1. Внешний вид электронного замка, выполненного с использованием универ- сальной монтажной платы, приведен на рис. 17.2. +12 В Рис. 17.1. Схема электронного замка
Глава 17. Кодовый замок 211 Рис. 17.2. Внешний вид электронного замка Как видно из схемы, цифровые кнопки замка подключены к цифровым контактам 0...9 модуля. Переключатель режима представляет собой тумблер на два фиксированных положе- ния, имеющий одну группу из двух контактов: ♦ в режиме «Запись кода» контакты тумблера должны быть замкнуты; ♦ в режиме «Работа» контакты тумблера должны быть разом- кнуты. Ключевой каскад для управления исполнительным меха- низмом замка подключается к контакту 13. К этому же кон- такту на плате Ардуино уже подключен светодиод, специально предназначенный для нужд программиста. Как только на контакт 12 поступит напряжение, открываю- щее замок, светодиод загорится. И мы будем видеть, что замок открыт.
212 Программирование ARDUINO. Создаем практические устройства ПРИМЕЧАНИЕ. В процессе испытания схемы и программы элек- тронный ключ и исполнительный механизм мож- но не подключать. Для проверки и отладки про- граммы достаточно светодиода. II Алгоритм Как в режиме «Записи кода», так и в режиме «Работа» функ- ционирование программы начинается с процедуры ввода кодовой комбинации с клавиатуры. Процедура в цикле мно- гократно считывает состояние клавиатуры и ждет изменения этого состояния. ЧТО ЕСТЬ ЧТО. Состояние клавиатуры - это число, состоящее из 10 двоичных битов информации. Каждый бит этого числа отображает состояние соответству- ющей кнопки клавиатуры. Младший бит (бит номер 0) отра- жает состояние кнопки «0». Следующий бит (бит номер 1) ото- бражает состояние кнопки «1» и так далее. Если какая либо кнопка нажата, то соответствующий ей бит равен нулю, если отпущена — единице. Код состояния клави- атуры записывается в переменную, имеющую тип int, то есть занимающую в памяти два байта. Итак, процедура ввода кода с клавиатуры многократно счи- тывает код состояния клавиатуры и ждет, когда при очередном считывании этот код изменится. Каждое новое значение кода записывается в буфер, который расположен в ОЗУ. В результате в буфере постепенно накапливается последовательность кодов, играющая роль ключа для открывания замка.
Глава 17. Кодовый замок 213 ПРИМЕЧАНИЕ. В процессе ввода кодовой последовательности программа постоянно контролирует время меж- ду двумя последовательными нажатиями кнопок. Если это время превысит контрольное время (в 1 секунду), программа считает, что ввод кодо- вой последовательности завершен и выходит из цикла сканирования кнопок. После того как ввод кодовой последовательности в буфер завершен, дальнейшие действия зависят от режима работы. Если переключатель режимов находится в положении «Запись кода», программа переходит к процедуре записи кодовой последовательности из буфера (в ОЗУ) в долговременную память (EEPROM). При этом каждый код из буфера разделяется на старший и младший байты. И все эти байты записываются по очереди в ячейки EEPROM. Затем туда же записывается и длина кодовой после- довательности. ПРИМЕЧАНИЕ. Это необходимо, потому что длина последова- тельности - величина не фиксированная. В на- шем кодовом замке мы можем использовать ко- довую последовательность произвольной длины. Она ограничивается лишь объемом буфера. Если же переключатель режима находится в положении «Работа», то после завершения процедуры ввода кодовой последовательности с клавиатуры программа переходит к про- цедуре чтения и сравнения кодов. Коды, записанные ранее, считываются по очереди из ячеек EEPROM и сравниваются со значениями кодов, только что введенных с клавиатуры, и нахо- дящихся в буфере в ОЗУ.
214 Программирование ARDUINO. Создаем практические устройства Если все коды обеих последовательностей (в EEPROM и в буфере ОЗУ) окажутся одинаковыми, программа дает команду на открывание замка. Программа Программа электронного замка приведена в листинге 17.1. При создании программы была подтверждена высокая степень совместимости языка Ардуина с другими СИ-подобными язы- ками программирования. Программа, которую мы видим в листинге 17.1, создана путем копирования программы электронного замка, приве- денной в книге [1], написанной на языке СИ среды програм- мирования Code Vision. ПРИМЕЧАНИЕ. Для адаптации программы с языка СИ Code Vision для работы в среде Ардуино пришлось сделать совсем немного изменений. Но мы не будем в дан- ной книге заниматься сравнением двух программ. Кто хочет, может сравнить их самостоятельно. Мы просто разберем подробно работу програм- мы, приведенной на листинге 17.1. Как уже говорилось выше, программа начинается с присо- единения библиотеки EEPROM.h (строка 1). Далее, в строках 2...8 производится определение различ- ных констант, используемых в дальнейшем в этой программе. И тут используется другой, пока еще нам не знакомый опера- тор присвоения. Это универсальный оператор присвоения #define.
Глава 17. Кодовый замок 215 При помощи этого оператора можно присвоить имя любому значению, выражению или строковой константе. Оператор #define не требует указания типа значения, так как он опреде- ляет не значение, а только строковый литерал. ПРИМЕЧАНИЕ. Такой способ определение констант использо- вался в исходном варианте программы, взятом из книги [1]. Оттуда он перешел в программу на Ардуино. Рассмотрим подробнее назначение определяемых кон- стант. В строке 2 программы определяется константа klfree. Константе присваивается литерал 0x3FF. Это число в шестнад- цатеричном формате, соответствующее коду состояния кла- виатуры в случае, если ни одна кнопка не нажата. Это значе- ние понадобится нам в процедуре «ожидания нажатия хотя бы одной из клавиш» и в процедуре «ожидания отпускания всех клавиш». В строке 3 определяется длительность контрольного про- межутка времени kontrTime. Его мы будем использовать в про- цедуре проверки факта, окончился ли набор кодовой последо- вательности. В строке 4 определяется константа антидребезга. В строке 5 определен размер буфера, куда будет помещена набираемая кодовая последовательность. Эта же величина будет использоваться для определения количества ячеек в EEPROM, в которые мы будем помещать кодовую последова- тельность для длительного хранения. В строке 6 определяется константа klen, которая будет содержать адрес ячейки в EEPROM, в которой мы будем хра- нить длину нашей кодовой последовательности. Для этой цели мы выберем ячейку, расположенную сразу после блока ячеек,
216 Программирование ARDUINO. Создаем практические устройства зарезервированных для хранения всех байтов самой кодовой последовательности. ПРИМЕЧАНИЕ. Адрес не указывается напрямую, а вычисляется при помощи простого математического выраже- ния. Чтобы узнать, сколько байтов займет кодо- вая последовательность, берется размер буфера в ОЗУ (константа bsize) и умножается на два. Это делается потому, что для каждого элемен- та буфера в памяти EEPROM нужно оставить две ячейки памяти. Адрес ячейки, находящейся сразу после блока ячеек кодо- вой последовательности, будет на единицу больше, чем размер блока (то есть bsize*2+l). Определение адреса ячейки методом вычисления удобно тем, что при изменении размера буфера автоматически изменяется и адрес ячейки для записи длины последовательности. Это гарантирует от ошибок при модифи- кации параметров. В строках 7 и 8 определяются номера контактов для под- ключения переключателя режимов (modePiri) и ключа управле- ния исполнительным механизмом (relayPin). В строках 9 и 10 определяются две переменные (и и i), которые будут нам служить указателями при работе с буфером и ячейками в EEPROM. В строке 11 определяется переменная для временного хра- нения кода состояния клавиатуры (codS). И, наконец, в строке 12 определяется буфер для хранения кодовой последовательности. Буфер представляет собой мас- сив с именем bufr и размером bsize. В строке 13 начинается описание функции setup(). В теле функции производится настройка режимов всех используемых контактов модуля Ардуино.
Глава 17. Кодовый замок 217 Листинг 17.1. Программа электронного замка 9 10 11 12 13 14 15 16 17 18 19 20 7* Кодовый замок */ #include <EEPROM.h> ♦define klfree 0x3FF // код «Все кнопки отпущены» ♦define kontrTime 1000 // контрольн. промежуток времени ♦define kandr 30 // Константа антидребезга ♦define bsize 30 // Размер буфера для хранения кода ♦define klen bsize*2+l // Ячейка EEPROM для длинны // кодовой последовательности ♦define modePin 11 // Пин переключателя режима ♦define relayPin 13 // Номер контакта привода замка unsigned char ii; // Указатель массива unsigned char i; // Вспомогательный указатель unsigned int codS; // Старый код unsigned int bufr[bsize]; // Буфер в ОЗУ для хранения кода void setup () { // Определение режимов контактов: pinMode(relayPin, OUTPUT); // выход на исп. механизм pinMode(modePin, INPUT_PULLUP); // перекл. режимов // Цикл настройки режимов контактов кнопок: for (int ButtonPin=0; ButtonPin<=9; ButtonPin++) { pinMode(ButtonPin, INPUT_PULLUP); void loop() { // Определение локальный переменных: unsigned long itime; // хранения текущего времени unsigned int CodC; // код состояния клавиатуры
218 Программирование ARDUINO. Создаем практические устройства 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 ml: while (incod() != klfree); // ожидание отпускания кнопок while (incod() == klfree); // ожидание нажатия кнопок ii=0; m2: delay(50); // задержка 50 мс codS=incod(); // ввод кода и запись, как старого bufr[ii++]=codS; // запись очередного кода в буфер if (ii>=bsize) goto m4; // проверка конца буфера itime = millis(); // запись текущего времени тЗ: if (incod() != codS) goto m2; // изменилось ли состояние if (millis()-itime< kontrTime) goto m3; // пров.контр.времени т4: // Проверка перекл. режимов if (digitalRead(modePin)==HIGH) goto comp; // Запись кода в EEPROM EEPROM.write(klen,ii); // Запись длины кода // Запись всех байтов кода for (i=0; i<ii; i++) { EEPROM.write(i*2,highByte(bufr[i])); EEPROM.write(i*2+lflowByte(bufr[i])); } goto zamok; // Проверка кода comp: // Проверка длины кода: if (EEPROM.read(klen)!=ii) goto ml; for (i=0; i<ii; i CodC = EEPROM.read(i*2)*256 + EEPROM.read(i*2+l); if (CodC!=bufr[i]) goto ml;// Проверка самого кода // Открывание замка zamok: digitalWrite(relayPin,HIGH); // Открываем замок delay(lOOO); // Задержка на 1 сек digitalWrite(relayPin,LOW); // Закрываем замок
Глава 17. Кодовый замок 219 44 45 46 47 48 49 50 51 52 53 54 // Функция опроса клавиатуры и антидребезга unsigned int incod (void) { unsigned int codO=0; // Локальные переменные unsigned int codl; byte k; // Цикл антидребезга for (k=0; k<kandr; k++) { codl=PINB&0x3; // Формируем первый байт кода codl=(codl«8)+PIND; // Формируем полный // код состояния клавиатуры if (codO!=codl) // Сравниваем со старым кодом { к=0; // Если не равны, сбрасываем счетчик codO=codl; // И присваиваем новое значение // старому коду return codl; В строке 14 контакт relayPin (управление исполнительным механизмом) переводится в режим вывода информации. В строке 15 определяется режим работы контакта пере- ключателя режимов (modePiri). В строках 16, 17 находится цикл, который определяет режимы работы всех десяти контактов, предназначенных для подключения цифровых кнопок. Основной цикл программы loopQ начинается в строке 18. В теле функции 1оор() выполняются практически все основные действия программы. Отдельно выполняется лишь задача счи- тывания кода состояния кнопок. Для считывания кода состояния используется функция incod(). Функция считывает информацию с кнопок и возвра- щает код состояния, в котором, как уже говорилось, каждый из десяти младших битов отвечает за состояние своей конкрет- ной кнопки. Функция incod() так же выполняет чистовой этап борьбы с дребезгом контактов.
220 Программирование ARDUINO. Создаем практические устройства Основная функция 1оор() начинается с определения двух локальных переменных. Переменная Ште (см. строку 19) вве- дена для того, чтобы можно было запоминать текущее значе- ние системных часов. Это значение затем используется для определения временных интервалов. Переменная CodC (см. строку 20) используется как вспомо- гательная в операции сравнения кодов. В программе широко используются метки. При помощи меток решена вся логика работы основной задачи. Надеюсь, что вы еще не забыли: метка используется для того, чтобы переходить на строку, следующую непосредственно за меткой из других мест программы. Так, строка 21 помечена меткой «ml:». Кроме метки, в строке 21 находится цикл ожидания отпускания всех кнопок. Если в момент запуска программы одна или несколько кнопок окажутся нажатыми, то это может означать, что набор кодов начался преждевременно и часть нажатий уже пропущено. ПРИМЕЧАНИЕ Чтобы предотвратить некорректный набор, работа программы приостанавливается до тех пор, пока все кнопки не будут отпущены. Цикл ожидания отпускания кнопок многократно обраща- ется к функции incod(), которая возвращает код состояния кно- пок. Полученный код сравнивается с константой klfree, значе- ние которой равно коду полностью отпущенных кнопок. Цикл длится до тех пор, пока эти два кода отличаются друг от друга. Когда коды окажутся одинаковыми, цикл закончится. Далее, в строке 22 находится другой цикл — цикл ожида- ния нажатия хотя бы одной из кнопок. Он очень похож на цикл из строки 21. Отличается только условием. Цикл длится до тех пор, пока код состояния кнопок равен значению klfree. После обнаружения факта нажатия хотя бы одной кнопки программа входит в цикл записи кодовой после-
Глава 17. Кодовый замок 221 довательности в буфер. Для начала обнуляется указатель буфера (строка 23). В строке 24 выполняется небольшая задержка на 50 милли- секунд. Это первый уровень антидребезга. В строке 24 еще раз считывается код состояния кнопок (уже чистый от дребезга). Считанный код записывается в переменную codS. Эта перемен- ная предназначена для хранения старого значения кода. ПРИМЕЧАНИЕ. Далее мы будем многократно перечитывать код, и сравнивать с этим старым значением. Считанное значение кода состояния записывается также и в первую ячейку буфера (строка 26). Одновременно значение указателя буфера увеличивается на единицу (благодаря опера- тору ++). Двойной плюс, как вы можете видеть, стоить после имени переменной. Это значит, что сначала происходит запись значения codS в элемент номер и, и только затем происходит инкрементация и. Новое значение кода состояния будет запи- сываться уже в следующую ячейку буфера. В строке 27 производится проверка конца буфера. Если пользователь попытается ввести слишком длинную кодовую последовательность, при достижении конца буфера ввод кода прекращается. Оператор if в строке 27 сравнивает значение указателя и с заданным размером буфера. И если конец достиг- нут, передает управление по метке т49 в конец операции ввода кодовой последовательности. Если указатель и не достиг конца буфера, то программа переходит к строке 28. В этой строке считывается и запомина- ется в переменную itime текущее системное время. Далее, в строке 29 считывается новое значение кода состо- яния кнопок и сравнивается со старым. Смысл этой операции — ожидание изменения кода состояния. Пока код состояния при каждом считывании будет неизменным, условие оператора if в
222 Программирование ARDUINO. Создаем практические устройства строке 29 будет иметь значение «Ложь» и программа каждый раз будет переходить к строке 30. В этой строке происходит проверка контрольного времени. Для этого считывается новое значение системного времени, из него вычитается время, сохраненное в строке 28, и полученная разность сравнивается с величиной контрольного промежутка времени. Если контрольное время не достигнуто, то управление передается к метке тЗ> и снова выполняется строка 29. В ней снова считывается код состояния кнопок и сравнивается со старым значением. И снова, если значение не изменилось, в строке 30 производится проверка на истечение контрольного промежутка времени. Из этого цикла два выхода: ♦ во-первых, если истечет контрольный промежуток време- ни, программа перестанет переходить к тЗ, и перейдет к строке 31 (то есть закончит процедуру ввода кодовой по- следовательности) ; ♦ во-вторых, если в строке 29 программа обнаружит факт изменения кода состояния. Тогда произойдет переход по метке т2. Там новый код запишется как старый (в переменную codS)> затем он запи- шется в очередную ячейку буфера, далее произойдет проверка буфера на исчерпание его объема (строка 27) и, наконец, в строке 28 будет считано и записано в переменную новое теку- щее значение системного времени. Далее программа войдет в новый цикл ожидания измене- ния состояния кнопок с проверкой исчерпания контрольного периода времени. Как бы ни завершился процесс ввода кодовой последова- тельности, в любом случае управление переходит к строке 31. В этой строке находится команда, которая проверяет состоя- ние переключателя режимов. Если контакты переключателя не замкнуты (значение, считанное с контакта modePin равно HIGH), это означает, что включен режим «Работа». Управление передается по метке сотр, к процедуре срав- нения кодовых последовательностей. Эта процедура зани-
Глава 17. Кодовый замок 223 мает строки 37...40. Если контакты переключателя режимов замкнуты, то управление переходит к строке 32, где начи- нается процедура записи кодовой последовательности в EEPROM. Выполнив одну из этих процедур (записи или сравнения кодовых последовательностей) программа переходит к неболь- шой процедуре, завершающей выполнение основного цикла, которая начинается в строке 41. Эта маленькая процедура, отмеченная меткой zamok:, вызывает срабатывание меха- низма замка, и удержание его в открытом состоянии в течение 1 секунды. Рассмотрим все три описанные выше процедуры по порядку. Процедура записи в EEPROM начинается в строке 32. В этой строке в ячейку klen долговременной памяти EEPROM записывается значение длины ключевой кодовой последо- вательности. После завершения процедуры ввода кода длина последовательности содержится в переменной п. ПРИМЕЧАНИЕ. Это происходит потому, что указатель буфера (И) в процессе ввода кодов всегда указывает на ячейку буфера, куда должен записываться очеред- ной код. Учитывая, что нумерация ячеек начинается с нуля, значе- ние этой ячейки всегда равно текущей длине кодовой последо- вательности. В строке 33 начинается цикл записи кодов из буфера в ячейки EEPROM. В качестве указателя цикла используется переменная /. При помощи этого указателя мы будем считывать коды из буфера, начиная с элемента 0 и заканчивая последним элементом введенной кодовой последовательности. Каждый считанный код мы разобьем на два байта (стар- ший и младший) и запишем каждый из них в долговремен-
224 Программирование ARDUINO. Создаем практические устройства ную память в две последовательные ячейки. А начнем запись с ячейки номер 0. Эта операция выполняется в два этапа. В строке 34 в ячейку памяти с адресом /*2 записывается старший байт очередного элемента буфера. Для извлечения старшего байта из очередного элемента буфера используется стандартная функция языка Ардуино highByte(). В строке 34 в ячейку долговременной памяти с адресом /*2+1 записывается младший байт значения текущего элемента буфера. Для получения младшего байта используется функция lowByte(). Таким образом, нулевой элемент массива (/=0) будет разделен на два байта. Старший байт будет записан в ячейку с номером 0*2=0. Младший в ячейку с номером 0*2+1=1. Следующий элемент буфера (i=l) будет разделен и записан так: старший байт в ячейку с номером 1 *2=2> младший байт в ячейку с номером 1 *2+1=3. И так далее, для всех кодов после- довательности. После записи всей кодовой последовательности программа переходит к процедуре открывания замка по метке «zamok:». Замок на 1 секунду открывается и закрывается. Это служит индикатором того, что ввод кодовой последовательности успешно завершен. ПРИМЕЧАНИЕ. Такой способ индикации выбран для того, чтобы ни вводить в схему лишних светодиодов. Процедура проверки кодов начинается в строке 37. В этой строке из ячейки klen долговременной памяти (EEPROM) считывается значение длины кодовой последовательности, записанной туда ранее в режиме «Запись кода». Оно сравни- вается с только что полученным значением длины уже новой кодовой последовательности, хранящейся в буфере. Если два этих значения не равны, то это значит, что код в буфере набран неправильно (не та длина). Управление пере- дается по метке ml в самое начало программы. Открывание замка не происходит.
Глава 17. Кодовый замок 225 В случае если первая проверка прошла успешно (длина кода совпадает), программа переходит к сравнению самих кодовых последовательностей. Цикл сравнения формируется в строке 38. В качестве указателя цикла так же используется переменная и В строке 39 считываются оба байта очередного кода после- довательности. Значение старшего байта умножается на 256 и к нему прибавляется значение младшего байта. В результате мы получаем искомый код из очередной позиции кодовой после- довательности. Этот код записывается в переменную CodC. ПРИМЕЧАНИЕ. Нужно отметить, что язык Ардуино предлага- ет специальную функцию объединения стар- шего и младшего байтов word(CmapiuuuBaum, МладшийБайт). Если использовать эту функцию, то строка 39 программы может выглядеть следующим образом: CodC = word(EEPROM.read(i*2), EEPROM.read(i*2+l)); В строке 40 значение CodC сравнивается со значением текущего элемента буфера. Они должны совпадать. Если зна- чения не совпадают, управление тут же передается по метке ml и открывание замка не происходит. И только в том случае, когда цикл полностью переберет и сравнит все коды последовательности, все они окажутся попарно равными, цикл завершится нормальным образом и управление плавно перейдет к строке 41, то есть к процедуре открывания замка. Процедура открывания замка выполняется как в режиме «Запись кода», так и в режиме «Работа». Только при «Записи кода» процедура выполняется всегда по завершении набора кодовой последовательности. И просто индицирует то, что набранная кодовая последовательность благополучно записана в память.
226 Программирование ARDUINO. Создаем практические устройства В режиме «Работа» открывание замка происходит только в случае полного совпадения введенной кодовой комбинации с комбинацией, записанной ранее в EEPROM. Процедура откры- вания замка очень проста. В строке 41 на выход исполнительного механизма (relayPin) подается сигнал высокого логического уровня. При этом заго- рается светодиод на плате Ардуино, помеченный литерой «L». Одновременно срабатывает электронный ключ (если, конечно, вы его подключили). Через ключ подается питание на исполни- тельный механизм и открывается замок. В строке 42 формируется задержка длительностью в 1 секунду. За это время вы должны успеть открыть (или хотя бы приоткрыть) дверь так, чтобы замок уже не мог защелкнуться. В строке 43 на выход relayPin подается низкий логический уровень. Исполнительный механизм отпускает щеколду замка. СОВЕТ. Если одной секунды вам не достаточно, чтобы успеть открыть дверь, вы можете увеличить значение задержки в строке 42. Функция опроса клавиатуры и антидребезга выполняет две задачи: ♦ считывает состояние всех кнопок клавиатуры и формирует код состояния; ♦ реализует второй уровень антидребезговой защиты. Для формирования кода состояния программа исполь- зует прямое чтение из регистров. Из главы 2 нашей книги, а также из схемы распиновки модуля Ардуино (см. рис. 2.3), мы знаем, что цифровые контакты с 0 по 7 подключены к разрядам PD0...PD7 порта PD основного микроконтроллера ATmega323. Именно к этим контактам в нашей схеме подключены кнопки с «О» по «7». Еще две кнопки подключены к контактам 8 и 9 модуля. Эти два контакта подключены уже к другому порту
Глава 17. Кодовый замок 227 микроконтроллера. Контакт 8 подключен к разряду РВО, а кон- такт 9 к разряду РВ1 порта РВ. ПРИМЕЧАНИЕ. Для того чтобы получить состояние сразу всех кнопок с 0 по 7, можно просто прочитать содер- жимое порта PD. Прочитав восьмиразрядное чис- ло из этого порта, мы получим код, у которого каждый бит будет отражать состояние своей кнопки. Еще два бита, соответствующих остав- шимся двум кнопкам («8» и «9»), можно получить, прочитав содержимое порта РВ. Два младших бита полученного числа будут соответство- вать оставшимся двум кнопкам. При этом нам придется обну- лить шесть старших битов прочитанного числа для того, чтобы случайные сигналы не влияли на результат. Для этого можно наложить на число соответствующую маску. Затем два кода (считанный из порта PD и считанный из порта РВ) объединяются в одно двухбайтовое число и записы- ваются в переменную типа int. Такой способ работы с входной информацией называется «работа на уровне портов». ПРИМЕЧАНИЕ. Этот способ не характерен для программ, напи- санных в программной среде Ардуино, но поддер- живается в ней. Работа на уровне портов часто используется в других системах программирова- ния для микроконтроллеров. Например, в среде Code Vision, в программе, описанной в книге [1]. В нашу программу такой метод перекочевал из книги [1], программу из которой мы взяли за основу. Таким образом, мы
228 Программирование ARDUINO. Создаем практические устройства хотели показать, что язык Ардуино достаточно гибок и поддер- живает самые разные приемы программирования, что гаран- тирует высокую переносимость программ. Функция incod() занимает в программе строки 44...54. Начинается функция с определения локальных переменных. Переменные codO и codl (см. строки 45 и 46) предназначены для временного хранения старого и нового значения кода состояния кнопок. Переменная к (строка 47) — это счетчик цикла антидре- безга. В строке 48 как раз и объявляется цикл антидребезга. Внутри цикла происходит многократное чтение и формирова- ние кода состояния. В нем реализуется алгоритм антидребезга, который состоит в том, что определенное число раз считанный код состояния должен быть неизменным. Число таких считыва- ний определено константой антидребезга kandr. Если при очередном считывании код изменился, то новое значение кода запоминается, а программа заново начинает отсчет последовательности, в которой коды должны быть оди- наковыми. Функция завершается и возвращает значение кода состояния кнопок только в том случае, когда этот код не изме- нялся при считывании его kandr число раз подряд. Собственно считывание и формирование кода происходит в строках 49 и 50. Сначала считывается значение из порта РВ (строка 49). На считанное значение накладывается маска 0x3 (00000011), которая обнуляет все старшие разряды, оставляя без изменения только два младших. Полученное число запи- сывается в переменную codl. Затем в строке 50 программа считывает значение из порта PD, и складывает его со значением codl сдвинутым на 8 разря- дов влево. Результат всех этих действий снова записывается в codl. После всех этих преобразований два разряда, считанные из порта РВ, окажутся в разрядах 8 и 9 полученного кода. А раз- ряды с 0 по 7 займет код, считанный из порта PD. Это и будет искомый код состояния кнопок, и храниться он будет в пере- менной codl.
Глава 17. Кодовый замок 229 Далее, в строке 51 производится сравнение только что считанного кода состояния из codl и старого значения кода, который должен храниться в переменной codO. При первом считывании переменная codO содержит нулевое значение, записанное туда при создании этой переменной в строке 45 программы. Это равносильно тому, что код состояния изме- нился. Поэтому счетчик цикла сразу же будет обнулен, и цикл начнется сначала. Это произойдет следующим образом: в строке 51 срав- ниваются значения переменных codO и codl. Если они не равны, программа выполняет команды в строках 52 и 53. В строке 52 счетчику цикла антидребезга к присваивается нуле- вое значение. Таким образом, цикл начнется сначала. В строке 53 в переменную codO записывается значение переменной codl. Теперь новое значение кода будет использо- ваться в качестве старого. Цикл в строке 48 будет повторяться kandr раз в том случае, если все эти разы codl будет равен codO. Как только новый код окажется не равным старому, счетчик к опять обнулится и цикл начнется сначала. Если код ни разу не изменялся, цикл завершается, и про- грамма переходит к строке 54. В этой строке находится опера- тор return (возврат). Параметром оператора является возвра- щаемое значение. Оператор return завершает работу функции incod(), и возвращает значение codl. ПРИМЕЧАНИЕ. При желании функцию incodQ можно написать, не используя прямой доступ к регистрам. В листин- ге 17.2 приведен вариант той же функции, в ко- тором для считывания состояния кнопок исполь- зуется стандартный оператор языка Ардуино digitalReadf).
230 Программирование ARDUINO. Создаем практические устройства Новая редакция функции incod() отличается от старого только в той части, где происходит считывание и формирова- ние кода состояния клавиатуры. Вся операция формирования кода занимает строки 6,7 и 8 (см. листинг 17.2). Код состояния формируется в переменной codl. Перед нача- лом операции формирования кода эта переменная обнуляется (см. строку 6 программы). Затем, в строке 7, начинается цикл опроса кнопок. В качестве указателя цикла используется пере- менная ButtonPin. Переменная в цикле изменяется от 0 до 9 и поочередно указывает на контакты, к которым подключены кнопки. В строке 8 значение, записанное в переменной codl, сдвигается на один бит влево. При этом к полученному в результате сдвига значению прибавляется бит состояния текущей кнопки, счи- танный с входа, на который указывает переменная ButtonPin. В результате за десять шагов цикла все десять битов состоя- ния кнопок выстроятся в единое число — код состояния кно- пок. В остальном новая редакция функции incod() ничем не отличается от старой. ВНИМАНИЕ! В листинге 17.2 используется своя локальная ну- мерация строк, не связанная с нумерацией в ли- стинге 17.1. Прежде, чем закончить данную главу, несколько слов о таком важном вопросе, как отладка программ. ПРИМЕЧАНИЕ. К сожалению, среда разработки Ардуино не име- ет практически никаких средств для отладки программ. Поэтому программистам приходится выкручиваться.
Глава 17. Кодовый замок 231 Листинг 17.2. Альтернативный вариант функции опроса клавиатуры и антидребезга 5 б 7 8 9 10 11 12 // Функция опроса клавиатуры и антидребезга unsigned int incod (void) { unsigned int codO=0; // локальные переменные unsigned int codl; byte k; // Цикл антидребезга for (k=0; k<kandr; k++) { codl=0; for (int ButtonPin=0; ButtonPin<=9; ButtonPin++) { codl= (codl«l) + digitalRead(ButtonPin) ; } if (codO!=codl) { // сравниваем со старым кодом k=0; // если не равны, сбрасываем счетчик codO=codl; // новое значение старому коду return codl; Простые программы можно отлаживать, применяя следую- щие приемы: ♦ написание программы отдельными модулями с опробова- нием каждого модуля по отдельности; ♦ применение метода пробных изменений программы, про- веряющих разные гипотезы возникновения ошибок; ♦ внимательный просмотр текста программы с целью по- нять причину ошибки чисто логически, В число таких приемов также можно включить временное внедрение в программу специальных отладочных программ- ных блоков, выводящих любым доступным способом промежу- точные результаты работы программы.
232 Программирование ARDUINO. Создаем практические устройства ПРИМЕР. В процессе отладки программы электронного замка был применен следующий прием. В програм- му в отладочных целях был временно введен блок операторов, который каждый раз после набора кодовой последовательности выводил в компью- тер через последовательный канал содержимое буфера, а также кодовую комбинацию, считанную из БЕРНОМ. Эту информацию можно увидеть на компьютере при помощи монитора порта, как мы это делали в главе 14. В про- цессе отладки блок команд перемещался в разные места отла- живаемой программы. Это позволяло наблюдать формирова- ние кодовых последовательностей на разных этапах работы программы. Для того чтобы кнопки «О» и «1» не мешали при передаче данных по последовательному порту, они были временно отключены на период отладки. После завершения процесса отладки все отладочные изменения из программы были уда- лены. В набор электронных версий программных примеров, нахо- дящийся на виртуальном диске, включена версия программы электронного замка, в которую уже включены отладочные блоки. Скачав пакет электронных версий программных при- меров, вы можете, при желании, самостоятельно запустить и попробовать поработать с этим вариантом программы. То есть освоить работу в режиме отладки. ВНИМАНИЕ. Версии программ, снабженные отладочными мо- дулями, помечены словом debug.
ГЛАВА 18 КОДОВЫЙ ЗАМОК С МУЗЫКАЛЬНЫМ звонком Постановка задачи В качестве последнего примера покажем, как можно объ- единить две самые сложные схемы и программы, описанные в этой книге в одну. А именно, описанный в предыдущей главе кодовый замок, мы объединим с программой музыкальной шкатулки, превратив «Музыкальную шкатулку» в музыкаль- ный дверной звонок. Не смотря на то, что указанные две про- граммы являются самыми сложными из всех, приведенных в книге программ, их совсем не трудно объединить в одну. ЗАДАЧА. Доработать схему и программу кодового замка, внедрив в него алгоритм воспроизведения мело- дий, который должен служить в качестве му- зыкального дверного звонка. Для этого в схему необходимо добавить кнопку звонка и звукоиз- лучающее устройство. При этом кодовый замок должен работать так же, как он работал при
234 Программирование ARDUINO. Создаем практические устройства Схема отсутствии функции звонка. Однако при нажа- тии кнопки звонка устройство должно воспро- изводить при помощи звукового излучателя одну из десяти заложенных в память мелодий. При каждом нажатии кнопки звонка должна звучать новая мелодия. Схема доработанного электронного замка приведена на рис. 18.1. В схему, в соответствии с поставленной задачей, добавлены: ♦ кнопка звонка, которая подключена к цифровому контак- ту 10; ♦ звукоизлучатель (динамик), который подключен так же, как это было сделано в схеме музыкальной шкатулки, к контакту 12. Рис. 18.1. Схема электронного замка со звонком
Глава 18. Кодовый замок с музыкальным звонком 235 Рис. 18.2. Внешний вид электронного замка со звонком Внешний вид собранного устройства показан на рис. 18.2. Алгоритм I Алгоритм работы электронного замка и алгоритм воспро- изведения музыки в новой программе будут взяты из про- грамм, описанных в главе 16 и главе 17. Они будут использо- ваться без изменений. Нам нужно только объединить два алго- ритма и заставить их работать совместно. Как мы знаем из главы 17, пока не нажата не одна из цифро- вых кнопок, программа электронного замка постоянно находится в режиме опроса состояния клавиатуры и ожидает нажатия.
236 Программирование ARDUINO. Создаем практические устройства ПРИМЕЧАНИЕ. Именно в этот цикл опроса нужно включить еще одну проверку: проверку состояния кнопки звонка. В результате цикл по очереди будет проверять то факт нажатия любой из цифровых кнопок замка, то факт нажатия кнопки звонка. При обнаружении нажатия дальнейшие действия про- граммы зависят от того, что именно было нажато. Если нажата одна из кнопок замка, то выполняется алго- ритм ввода кодовой последовательности и далее весь алгоритм замка. Если окажется нажатой кнопка звонка, то выполняется алгоритм воспроизведения мелодии. ПРИМЕЧАНИЕ. От музыкальной шкатулки этот алгоритм от- личается тем, что мелодии воспроизводятся по очереди. Номер воспроизводимой мелодии за- писывается в специально выделенную ячейку в EEPROM. При очередном нажатии кнопки звонка, воспроизводится следующая по порядку мелодия. Программа Объединенная программа электронного замка и музы- кального звонка приведена в листинге 18.1. За основу взята программа электронного замка. Программа воспроизведения мелодии оформлена в виде отдельной функции playMelody(), которая находится в конце листинга 18.1 и занимает строки 78...96.
Глава 18. Кодовый замок с музыкальным звонком 237 В начале программы в блоке определений всех перемен- ных, констант и массивов присутствуют определения как для одной, так и для второй задачи. ПРИМЕЧАНИЕ. Так как большинство строк в блоке определений повторяют аналогичные строки в программах- источниках: программы из листинга 17.1 (элек- тронного замка), и программы из листинга 16.1 (музыкальной шкатулки), мы рассмотрим только те строки описания, которые требуют особых пояснений. И первое отличие мы видим в строке 7 листинга 18Л. Здесь вводится новая константа numuz. Этой константой опре- деляется адрес ячейки в EEPROM для хранения номера теку- щей мелодии. Хранение номера текущей мелодии в энергоне- зависимой памяти позволит программе даже после выключе- ния питания хранить этот номер. При очередном нажатии кнопки звонка номер мелодии увеличивается на единицу, и звонок каждый раз воспроизво- дит новую мелодию. Мелодии нумеруются числами от 0 до 9. Когда, при очередном нажатии кнопки звонка номер мело- дии окажется больше девяти, программа сбрасывает его в ноль. В результате, после девятой мелодии воспроизводится мело- дия номер ноль. Номер ячейка numuz выбирается на единицу большим, чем номер для хранения длины кодовой последова- тельности (вычисляется по формуле fc/en+i), так как все преды- дущие ячейки в EEPROM уже заняты. В строке 8 определяется еще одна новая константа ringPin. Это номер контакта для подключения кнопки звонка. Еще одна новая константа SoundPin определяется в строке 10. Она пред- ставляет собой номер контакта для подключения динамика. В строках 16...28 мы видим знакомые нам таблицу высоты тона, таблицу длительностей нот и таблицы мелодий.
238 Программирование ARDUINO. Создаем практические устройства Листинг 18.1. Программа электронного замка 9 10 11 12 13 14 15 16 17 /* Кодовый замок плюс музыкальный звонок */ ♦include <EEPROM.h> ♦define klfree 0x3FF // код «Все кнопки отпущены» ♦define kzad 3000 // код задержки при сканировании ♦define kandr 30 // константа антидребезга ♦define bsize 30 // размер буфера // Два адреса ячеек в EEPROM: ♦define klen bsize*2+l // длинна кодовой последовательности ♦define numuz klen+1 // номер текущей мелодии // Номера контактов: ♦define ringPin 10 // конт. кнопки звонка ♦define modePin 11 // конт. переключателя режима ♦define SoundPin 12 // конт. звукоизлучателя ♦define relayPin 13 // конт. исполнит, механизма замка // Определение переменных: unsigned char ii; // Указатель массива unsigned char i; // Вспомогательный указатель unsigned int codS; // Старый код unsigned int bufr[bsize]; // Буфер в ОЗУ для хранения кода // Таблица длительностей нот const unsigned int tabz[] = {20,40,80,160,320,640,1280,2560}; // Таблица - частоты нот const unsigned int tabFreq[]={0,587,622,659,698,740,784, 831,880,932,988,1047,1109,1175,1245,1319,1397,1480, 1568,1661,1760,1865,1976,2093,2217,2349,2489,2637, 2794,2960,3136,3322,3520};
Глава 18. Кодовый замок с музыкальным звонком 239 18 19 20 21 22 // Таблицы мелодий //В траве сидел кузнечик byte mellU = (109,104,109,104,109,108,108,96,108,104, 108,104,108,109,109,96,109,104,109,104,109,108,108,96, 108,104,108,104,108,141,96,109,111,79,79,111,111,112,80, 80,112,112,112,111,109,108,109,109,96,109,111,79,79,111, 111,112,80,80,112,112,112,111,109,108,141,128,96,255}; // Песенка крокодила Гены byte mel2[] = (109,110,141,102,104,105,102,109,110,141, 104,105,107,104,109,110,141,104,105,139,109,110,173,96, 114,115,146,109,110,112,109,114,115,146,107,109,110,114, 112,110,146,109,105,136,107,105,134,128,128,102,105,137, 136,128,104,107,139,137,128,105,109,141,139,128,110,109, 176,112,108,109,112,144,142,128,107,110,142,141,128,105, 109,139,128,173,134,128,128,109,112,144,142,128,107,110, 142,141,128,105,109,139,128,173,146,128,255}; //В лесу родилась елочка byte mel3[] = (132,141,141,139,141,137,132,132,132,141, 141,142,139,176,128,144,146,146,154,154,153,151,149,144, 153,153,151,153,181,128,96,255}; // Happy births day to you byte mel4[] = (107,107,141,139,144,143,128,107,107,141, 139,146,144,128,107,107,151,148,146,112,111,149,117, 117,148,144,146,144,128,255}; // С чего начинается родина byte mel5[] = (99,175,109,107,106,102,99,144,111,175,96, 99,107,107,107,107,102,104,170,96,99,109,109,109,109, 107,106,143,109,141,99,109,109,109,109,104,106,171,96, 99,111,109,107,106,102,99,144,111,143,104,114,114,114, 114,109,111,176, 96,104,116,112,109,107,106,64,73,143, 107,131,99,144,80,80,112,111,64,75,173,128,255};
240 Программирование ARDUINO. Создаем практические устройства 23 24 25 26 27 //Из кинофильма «Веселые ребята» byte mel6[] = {105,109,112,149,116,64,80,148,114,64,78, 146,112,96,105,105,109,144,111,64,80,145,112,64,81,178, 96,117,117,117,149,116,64,82,146,112,64,79,146,144,96, 105,105,107,141,108,109,112,110,102,104,137,128,96,105, 105,105,137,102,64,73,142,105,107,109,64,75,137,96,105, 105,105,137,102,105,142,112,64,82,180,96,116,116,116, 148,114,112,142,109,64,78,146,144,96,105,105,107,141, 108,109,112,110,102,104,169,96,96,255}; // Улыбка byte mel7[] = {136,133,170,168,131,134,133,131,193,160, 133,136,138,138,138,140,143,141,140,138,173,200,138,140, 173,128,140,138,133,136,134,202,160,140,138,141,136,140, 138,138,136,131,133,163,161,160,129,132,136,136,136,136, 168,141,129,132,131,131,131,163,131,132,136,134,136,137, 136,134,136,137,173,171,160,141,136,139,137,137,137,169, 141,134,137,136,136,136,168,141,132,131,132,134,137,136, 134,132,134,169,168,160,141,136,139,137,137,137,169,141, 134,137,136,136,136,168,141,132,131,132,134,137,136,134, 132,134,168,193,160,255}; // Гимн России byte mel8[] = {136,173,136,96,106,172,133,96,101,170, 136,96,102,168,129,96,97,163,131,96,101,166,134,96,104, 170,140,141,175,136,177,143,96,109,175,140,136,173,140, 96,106,172,133,96,101,170,136,96,102,168,129,96,97,173, 140,138,168,224,255}; // Спят усталые игрушки byte mel9[] = {168,128,133,168,128,133,168,168,166,165, 163,165,200,143,145,143,145,212,168,125,131,168,128,131, 168,166,165,163,161,165,200,145,148,145,148,214,168,168, 170,168,177,177,170,168,161,161,166,168,170,170,168,166, 165,165,166,165,232,198,195,193,160,224,255}; // Гамма byte mellOU = {129,130,131,132,133,134,135,136,137, 138,139,140,141,142,143,144,145,146,147,148,149,150,
Глава 18. Кодовый замок с музыкальным звонком 241 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 151,152,153,154,155,156,157,158,159,128,159,158, 157,156,155,154,153,152,150,149,148,147,146,145,144, 143,142,141,140,139,138,137,136,135,134,133,132,131, 130,129,128,255}; // Таблица начал всех мелодий byte *tabm[] = {mell, mel2, rael3, rael4, mel5, mel6, mel7, mel8, mel9, raellO}; // ===== ======= void setup() { // Инициализация контактов: pinMode(relayPin, OUTPUT); // конт. механизма замка pinMode(SoundPin, OUTPUT); // конт. звукоизлучателя pinMode(modePin, INPUT_PULLUP); // перекл. режимов pinMode(ringPin, INPUT_PULLUP); // кнопка звонка // Инициализация пинов цифровых кнопок: for (int ButtonPin=0; ButtonPin<=9; ButtonPin++) { pinMode(ButtonPin, INPUT_PULLUP); // ============== void loop() { unsigned long itime; unsigned int CodC; ml: while (incod() != klfree); // ожидание отпускания кнопок // Ожидание нажатия кнопок: while (incod() == klfree) { if (digitalRead(ringPin)==LOW) { byte imel = ЕЕPROM.read(numuz); // зап.номер мелодии if (imel>9) imel=0; playMelody (imel++); EEPROM.write(numuz,imel);
242 Программирование ARDUINO. Создаем практические уаройства 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 m2 m3: m4 II- II- } ii=0; : delay(50); // задержка 50 мс codS=incod(); // ввод кода и запись, как старого bufr[ii++]=codS; // запись очередного кода в буфер if (ii>=bsize) goto m4; // проверка конца буфера // сканируем состояние кнопок в течении 1 секунды: itime = millis(); if (incod() != codS) goto m2; // изменилось ли состояние? // Проверка контрольн. промежутка времени: if (millis()-itime<1000) goto m3; // Проверка переключателя режимов: if (digitalRead(modePin)==HIGH) goto comp; — — ОсшИСЬ КОДа В JjILrKUlYl EEPROM.write(klen,ii); // запись длины кода // Запись всех байтов кода for (i=0; i<ii; i++) { EEPROM.write(i*2,highByte(bufr[i])); EEPROM.write(i*2+l,lowByte(bufr[i])); goto zamok; ТТч*"Ч Л. V^ Л. Ч*-Ч ¥Л *t *A Л. W <-4 - -- — — проверка кода comp: if (EEPROM.read(klen)!=ii) goto ml; // //■ Проверка длины кода for (i=0; i<ii; i++) { CodC = EEPROM.read(i*2)*256 + EEPROM.read(i*2+l); if (CodC!=bufr[i]) goto ml; // проверка кода } иткрывание замка zamok: digitalWrite(relayPin,HIGH); // открываем замок // } delay(lOOO); задержка на 1 сек digitalWrite(relayPin,LOW); // закрываем замок
Глава 18. Кодовый замок с музыкальным звонком 243 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 // Функция опроса клавиатуры и антидребезга unsigned int incod (void) { unsigned int codO=0; // локальные переменные unsigned int codl; byte k; // Цикл антидребезга for (k=0; k<kandr; k++) { codl=PINB&0x3; // формируем первый байт кода // Формируем полный код состояния клавиатуры: codl=(codl«8)+PIND; // Сравниваем со старым кодом if (codO!=codl) { k=0; // если не равны, сбрасываем счетчик codO=codl; // новое значение старому коду return codl; // ========================= II Функция воспроизведения мелодии void playMelody (int numberMelody) { byte count; // определяем переменную (счетчик) byte fnota; // код тона ноты byte dnota; // код длительности ноты byte *nota; // ссылка на текущую ноту nota = tabm[numberMelody]; StartMelody: if (digitalRead(ringPin)==HIGH) return; // Если кнопка // звонка не нажата, закончить if (*nota==0xFF) return; // проверка на конец мелодии fnota = (*nota)&0xlF; // определяем код тона dnota = ((*nota)»5) &0x07; // определяем код длительности if (fnota!=0) { tone (SoundPin, tabFreq[fnota],tabz[dnota]); delay (tabz[dnota]+10);
244 Программирование ARDUINO. Создаем практические устройства 92 93 94 95 96 else { noTone(SoundPin); // если пауза выкл. звук delay (tabz[dnota]); } nota++; goto StartMelody; В функцию setup()y которая занимает строки 29...35, вклю- чены команды, определяющие режимы всех используемых в программе контактов модуля Ардуино. Строки 36...66 занимает основной цикл программы (т. е. функция 1оор()). Она практически полностью повторяет основ- ной цикл программы кодового замка (см. листинг 17.1). ПРИМЕЧАНИЕ. Отличие только в том, что в цикл ожидания на- жатия кнопок (который начинается в строке 40) включены пять новых команд (строки 41...45), ко- торых не было в оригинальной программе замка. В строке 41 находится команда чтения и контроля состоя- ния кнопки звонка. Если кнопка не нажата (считанное значение равно HIGH), то команды в фигурных скобках оператора if не выполняются, и цикл ожидания нажатия кнопок продолжается. Если нажать одну из цифровых кнопок, то условие в кру- глых скобках оператора while в строке 40 перестанет выпол- няться, цикл прервется, а управление перейдет к строке 46, то есть к реализации алгоритма кодового замка. Если же вместо цифровых кнопок нажать кнопку звонка, то в строке 41 считанное с входа ringPin состояние кнопки звонка окажется равным LOW. To есть условие оператора if (в строке 41) окажется выполнено.
Глава 18. Кодовый замок с музыкальным звонком 245 Поэтому управление перейдет к операторам тела функции //(строки 42....45). Сначала в строке 42 из ячейки EEPROM с адресом numuz считывается номер текущей мелодии и записывается в пере- менную imel. Этот номер по условиям задачи должен прини- мать значения от 0 до 9, так как в таблицах мелодий у нас зало- жено десять мелодий, пронумерованных именно таким обра- зом (начиная с нуля). Однако при первом запуске программы либо в процессе предыдущей работы переменная может иметь и другие значе- ния. Нам нужно проверить, попадает ли значение в заданный диапазон и исправить его, если не попадает. Переменная imel имеет тип byte, а значение этого типа никогда не бывает отрицательным. Поэтому достаточно одной проверки: в строке 43 проверяется, не больше ли imel девяти. Если imel больше девяти, то ему присваивается нулевое значение. В строке 44 вызывается функция воспроизведения мело- дии playMelody(). В качестве параметра в функцию передается только что полученный номер мелодии imel. Одновременно производится инкрементация номера мелодии при помощи оператора «++». В строке 45 новый, увеличенный на единицу, номер мело- дии записывается в ячейку numuz долговременной памяти EEPROM. При следующем нажатии кнопки звонка будет вос- производиться уже новая мелодия. ПРИМЕЧАНИЕ. Никаких других отличий от оригинальной вер- сии в той части программы, которая реализует функцию электронного замка, нет. Функция воспроизведения мелодии playMelody() начина- ется в строке 78. Она представляет собой копию основного цикла программы музыкальной шкатулки (см. листинг 16.1), из которой исключены команды, которые опрашивают кнопки.
246 Программирование ARDUINO. Создаем практические устройства Номер мелодии функция получает через свой единственный входной параметр numberMelody. ПРИМЕЧАНИЕ. Полученная выше объединенная программа рас- считана на то, что пользователь будет в каж- дый момент времени либо набирать кодовую последовательность, либо звонить в звонок. Если попытаться два этих действия выполнить одновременно, то одно действие будет мешать другому. Нажатие кнопки звонка помешает набо- ру кода. Мелодия звонка будет звучать до тех пор, пока нажата кнопка звонка, и нажатие цифровых кнопок при этом будет просто игнорироваться.
ГЛАВА 19 ПЛАТЫ ARDUINO: ОСОБЕННОСТИ И ВОЗМОЖНОСТИ Arduino Due Arduino Due — плата микроконтроллера на базе процес- сора Atmel SAM3X8E ARM Cortex-МЗ. Это первая плата Arduino на основе 32-битного микроконтроллера с ARM ядром. На ней имеется 54 цифровых вход/выхода (из них 12 можно задействовать под выходы ШИМ), 12 аналоговых входов, 4 последовательных порта UART, генератор тактовой частоты 84 МГц, связь по USB с поддержкой OTG, 2 ЦАП, 2 TWI, разъем SPI, разъем JTAG, кнопка сброса и кнопка стирания. ВНИМАНИЕ! В отличие от других плат Arduino, Arduino Due работает от 3,3 В. Т. е. максимальное напряже- ние, которое выдерживают вход/выходы, состав- ляет 3,3 В. Подав более высокое напряжение, на- пример, 5 В на выводы Arduino Due, можно повре- дить плату.
248 Программирование ARDUINO. Создаем практические устройства IArduino Leonardo Arduino Leonardo — контроллер на базе ATmega32u4. Платформа имеет 20 цифровых вход/выходов, из них могут использоваться: ♦ 7 — как выходы ШИМ; ♦ 12 — как аналоговые входы. Частота кварцевого генератора 16 МГц. Плата имеет: разъем микро-USB, силовой разъем, разъем ICSP и кнопку переза- грузки. ПРИМЕЧАНИЕ. В отличие от всех предыдущих плат, АТтеда32и4 имеет встроенную поддержку для USB соеди- нения. Такая поддержка позволяет задать, как Leonardo будет виден при подключении к компью- теру (это может быть клавиатура, мышь, вир- туальный СОМ порт). Arduino Yun Arduino Yun — контроллер на базе ATmega32u4 и AR9331 от фирмы Atheros. Процессор от Atheros поддерживает ОС Linino (версия операционной системы Linux). Плата имеет встроенные модули Ethernet и WiFi, а также USB-порт, слот для карты микро-SD, 20 цифровых вход/выхо- дов (7 из которых могут использоваться как выходы ШИМ и 12 как аналоговые входы), работает на частоте 16 МГц, разъем микро USB, разъем ICSP и 3 кнопки сброса.
Глава 19. Платы Arduino: особенности и возможности 249 Arduino Micro Arduino Micro — плата микроконтроллера на базе ATmega32u4, была разработана совместно с фирмой Adafruit. Плата имеет 20 цифровых вход/выходов (из них 7 могут использоваться в качестве выходов ШИМ и 12 — как аналого- вые входы). Частота кварцевого генератора 16 МГц. Плата имеет: гнездо микро-USB, разъем ICSP и кнопку перезагрузки. Малый размер контроллера позволяет легко поместить его на макетной плате. Arduino UNO Arduino UNO — самая популярная версия базовой плат- формы Arduino USB. Плата Uno имеет стандартный порт USB. ПРИМЕЧАНИЕ. Arduino Uno во многом схожа с Duemilanove, но имеет новый чип ATMego8U2 для последователь- ного подключения по USB и новую, более удобную маркировку вход/выходов. Платформа может быть дополнена платами расширения, например, пользовательскими платами с различными функциями. Arduino Ethernet Arduino Ethernet — это плата микроконтроллера на базе ATmega328. Она имеет 14 цифровых входов/выходов, 6 анало- говых входов. Частота кварцевого генератора 16 МГц. Имеет
250 Программирование ARDUINO. Создаем практические устройства возможность подключить: RJ45 разъем, разъем питания, сое- динитель ICSP и кнопку «Reset». ПРИМЕЧАНИЕ. Выводы 10,11,12 и 13 зарезервированы для со- пряжения с модулем Ethernet и не могут исполь- зоваться никак иначе. Таким образом, число до- ступных выводов уменьшается до 9, 4 из кото- рых могут использоваться как выходы ШИМ. На плату может быть добавлен дополнительный модуль питания через Ethernet (РоЕ). I Arduino Duemilanove Arduino Duemilanove («2009») построена на одном из микроконтроллеров: ATmegal68 или ATmega328. Платформа содержит 14 цифровых входов/выходов (6 из которых могут использоваться как выходы ШИМ), 6 аналого- вых входов, кварцевый генератор 16 МГц, разъем USB, силовой разъем, разъем ICSP и кнопку перезагрузки. ПРИМЕЧАНИЕ Duemilanove (в переводе с итальянского - 2009) была названа в честь года своего выпуска - 2009 год. Является предпоследней версией базовой платформы Arduino USB.
Глава 19. Платы Arduino: особенности и возможности 251 Arduino II Diecimila I Arduino Diecimila построена на микроконтроллере ATmegal68. Платформа содержит 14 цифровых входов/выходов (6 из которых могут использоваться как выходы ШИМ), 6 ана- логовых входов, частота кварцевого генератора 16 МГц. На плате имеются: разъем USB, силовой разъем, разъем ICSP и кнопка перезагрузки. ПРИМЕЧАНИЕ. Diecimila (в переводе с итальянского - 10000) была названа в честь выпуска 10000-ой платы Arduino. Arduino Nano Arduino Nano построенна на микроконтроллере ATmega328 (Arduino Nano 3.0) или ATmegal68 (Arduino Nano 2.x), имеет небольшие размеры и может использоваться в лабораторных работах. ПРИМЕЧАНИЕ. Arduino Nano имеет схожую с Arduino Duemilanove функциональность, однако отличается сборкой. Отличие заключается в отсутствии силового разъема и работе через кабель Mini-B USB. Плата Nano разработана и производится компанией Gravitech.
252 Программирование ARDUINO. Создаем практические устройства Arduino Mega Arduino Mega построена на микроконтроллере ATmegal280. Платформа содержит 54 цифровых входов/выхо- дов (14 из которых могут использоваться как выходы ШИМ), 16 аналоговых входов, 4 последовательных порта UART. Частота кварцевого генератора 16 МГц. Плата имеет: разъем USB, силовой разъем, разъем ICSP и кнопку перезагрузки. Arduino Mega совместима со всеми устройствами расширения, разработанными для модулей Duemilanove или Diecimila. Arduino Mega 2560 Arduino Mega 2560 построена на микроконтроллере ATmega2560. Плата имеет: 54 цифровых входа/выхода (14 из которых могут использоваться как выходы ШИМ), 16 аналого- вых входов, 4 последовательных порта UART. Частота кварце- вого генератора 16 МГц. Плата содержит: USB коннектор, разъем питания, разъем ICSP и кнопку перезагрузки. ПРИМЕЧАНИЕ. Плата Arduino Меда 2560 совместима со все- ми платами расширения, разработанными для платформ Uno или Duemilanove.
Глава 19. Платы Arduino: особенности и возможности 253 Arduino ADK Arduino ADK во многом повторяет Arduino Mega 2560 и построена на микросхеме ATmega2560. ПРИМЕЧАНИЕ. Ключевое отличие заключается в наличие USB Host интерфейса, который позволяет подклю- чать контроллер к различным устройствам с интерфейсом USB, включая телефоны и другие устройства на базе Android. USB Host интерфейс реализован на микросхеме МАХ3421е. Также, как Mega 2560, плата имеет 54 цифровых входов/выхо- дов (14 из которых могут использоваться как выходы ШИМ), 16 аналоговых входов, 4 последовательных порта UART. Частота кварцевого генератора 16 МГц. Плата имеет: USB коннектор, разъем питания, разъем ICSP и кнопка перезагрузки. Последовательное подключение через USB реализовано на микросхеме Atmega8U2, также как в платах UNO и Mega. Arduino LllyPad Arduino LilyPad разработана с целью использования как часть одежды. Она может быть зашита в ткань со встроенными источниками питания, датчиками и приводами с проводкой. Плата построена на микроконтроллере ATmegal68V (мало- мощная версия с ATmegal68) или ATmega328V. Arduino LilyPad была создана компаниями Leah Buechley и SparkFun Electronics.
254 Программирование ARDUINO. Создаем практические устройства Arduino Fio Arduino Fio, построенная на микроконтроллере ATmega328P, работает при напряжении 3,3 В, с тактовой часто- той 8 МГц. Плата содержит: 14 цифровых входов и выходов (6 из кото- рых могут использоваться как выходы ШИМ), 8 аналоговых входов, резонатор, кнопку перезагрузки и отверстия для мон- тажа выводов. Плата Fio также содержит схему зарядки через разъем USB и позволяет подключить литий-полимерную батарею. На лицевой поверхности платформы установлен разъем ХВее. Arduino Fio может применяться в беспроводных сетях. Загрузка скетчей может производиться через кабель FTDI или плату-конвертер Sparkfun. Дополнительно имеется возможность загружать скетчи по беспроводной связи при использовании адаптера USB-to-XBee, например, ХВее Explorer USB. Arduino Mini Arduino Mini построена на микроконтроллере ATmegal68 и предназначена для использования в тех случаях, когда требу- ются минимальные размеры. Платформа содержит 14 цифровых входов и выходов (6 из которых могут использоваться как выходы ШИМ), 8 аналого- вых входов. Частота кварцевого генератора 16 МГц. ВНИМАНИЕ! Недопустимы как превышение питающего на- пряжения, так и переполюсовка выводов питания.
Глава 19. Платы Arduino: особенности и возможности 255 Программируется при помощи адаптера Mini USB или любого преобразователя USB или RS232 в TTL Arduino Pro Arduino Pro построена на одном из микроконтроллеров: ATmegal68 или ATmega328. Плата Pro производится в обоих исполнениях 3,3 В / 8 МГц и 5 В /16 МГц. Плата содержит: 14 цифровых входов и выходов (6 из кото- рых могут использоваться как выходы ШИМ), 6 аналоговых входов, силовой разъем батареи, силовой выключатель, кнопку перезагрузки, отверстия для монтажа силового разъема, блок ICSP и блоки выводов. Шестипиновый блок может подключаться к кабелю FTDI или плате-конвертеру Sparkfun для обеспечения питания и связи через USB. Arduino Pro предназначена для непостоянной установки в объекты или экспонаты. Расположение выводов совместимо с платами расширения Arduino. Arduino Pro Mini Arduino Pro Mini построена на микроконтроллере ATmegal68. Платформа содержит 14 цифровых входов и выхо- дов (6 из которых могут использоваться как выходы ШИМ), 6 аналоговых входов, резонатор, кнопку перезагрузки и отвер- стия для монтажа выводов. Блок из шести выводов может подключаться к кабелю FTDI или плате-конвертеру Sparkfun для обеспечения питания и связи через USB.
256 Программирование ARDUINO. Создаем практические устройства ПРИМЕЧАНИЕ. Плата предназначена для непостоянной уста- новки в объекты или экспонаты. Расположение выводов совместимо с платформой Arduino Mini. Существует две версии платформы Pro Mini. Одна версия работает при напряжении 3,3 В и частоте 8 МГц, другая — при напряжения 5 В и частоте 16 МГц. II USB Serial Light II Адаптер USB Serial Light Адаптер преобразует USB-канал в после- довательный RS-232 канал TTL уровня, который можно под- ключить непосредственно к Arduino Mini, Arduino Ethernet или другим платам Arduino, не имеющим своего USB адап- тера. Обеспечивает указанным платам связь с компьютером и загрузку скетчи. Логика конвертора реализована на базе чипа Atmega8U2, запрограммированном как конвертер из USB в последователь- ный сигнал, такой же как на Arduino Uno. Для Windows требу- ется файл .inf с драйверами. Плата имеет встроенный разъем мини-USB. Вы также можете использовать 5 выводов: RX (для приема данных с ком- пьютера), ТХ (для передачи данных), 5V, Ground (общий про- вод) и Reset (Сброс). Имеются светодиодные индикаторы питания и активности на линиях RX и ТХ.
ГЛАВА 20 ARDUINO SHIELDS ИЛИ ПЛАТЫ РАСШИРЕНИЯ Для чего нужны | платы расширения? II Платы расширения представляют собой закончен- ные устройства, предназначенные для установки на модули Arduino, которые расширяют их функциональность. Плата рас- ширения Ардуино имеет еще англоязычное название Arduino shield или просто шилд. ПРИМЕЧАНИЕ. Англоязычное слово Shield переводится как щит, экран, ширма. Это устройство, которое как бы покрывает плату контроллера, создает допол- нительный слой устройства, ширму, за которой скрываются различные элементы. Подключаются платы расширения к основному контрол- леру с помощью стандартных разъемов. На плате расшире- ния установлены все необходимые электронные компоненты, а взаимодействие с микроконтроллером и другими элементами основной платы происходят через стандартные пины Arduino.
258 Программирование ARDUINO. Создаем практические устройства ПРИМЕЧАНИЕ. Чаще всего питание на плату расширения тоже подается с основной платы Arduino, хотя во мно- гих случаях есть возможность запитать плату и от других источников. В любой плате расшире- ния остаются несколько свободных пинов, кото- рые можно использовать по своему усмотрению, подключив к ним любые другие компоненты. I Плата расширения Arduino WIFi Плата расширения Arduino WiFi позволяет контролле- рам Arduino осуществлять сетевое соединение, используя бес- проводную сеть формата 802.11. Плата построена на базе чипа HDG104 Wireless LAN 802.11b/g System in-Package. Микроконтроллер Atmega 32UC3 обеспечивает поддержку сетевого стэка (IP) как для TCP, так и для UDP протокола. Плата WiFi, как и большинство плат расширения, соединяется с пла- той контроллера Arduino посредством контактных колодок, расположенных по краям платы. Размеры соответствуют контактам на контроллерах Arduino UNO и Arduino Mega2560. На плате WiFi имеется слот для micro-SD карт, которые могут быть использованы для хранения и передачи файлов по сети. I Плата расширения Xbee Shield Плата расширения Xbee Shield при помощи модуля Maxstream Xbee Zigbee обеспечивает беспроводную связь нескольким устройствам Arduino в радиусе до 35 метров (в помещении) и до 90 метров (вне помещения).
Глава 20. Arduino shields или платы расширения 259 Плата расширения | Arduino Motor II Плата расширения Arduino Motor (управление моторами) изготовлена на основе микросхемы L298. Данная микросхема являющейся двойным полномостовым драйвером. Он разра- ботан специально для управления индуктивными нагрузками, такими как реле, соленоиды, двигатели постоянного тока и шаговые двигатели. Она позволяет управлять двумя двигателями постоянного тока с помощью вашей платы Arduino, независимо регулируя скорость и направление каждого из них. Кроме всего прочего, плата позволяет измерять ток, потре- бляемый каждым двигателем. Плата совместима с модулями TinkerKit. Плата расширения | Ethernet Shield II Плата расширения Ethernet Shield обеспечивает подклю- чение к локальной сети посредством сетевого кабеля (витая пара), а через локальную сеть обеспечивается связь с Интернет. Для подключения достаточно всего лишь подключить плату расширения к Ардуино, подсоединить ее к сети кабелем RJ-45 и выполнить несколько простых действий.
ГЛАВА 21 ПОДВОДЯ ИТОГИ Подводя итоги, мы смеем надеяться, что данная книга дала вам достаточно полное представление о возможностях такого замечательного устройства, как модуль Ардуино. Надеемся, что вы освоили основные приемы схемотехники и принципы составления программ. Мы постарались осветить как можно полнее все возможно- сти модуля. При этом число примеров оказалось даже больше, чем число примеров использованных в книге [1]. Разобравшись и попробовав самостоятельно собрать и испытать каждую из схем, предложенных в книге вы, как мы надеемся, уже стали достаточным специалистом в этой отдельной, но очень пер- спективной области микроэлектроники. И теперь уже самосто- ятельно сможете осуществлять свои собственные разработки и реализовать свои идеи. Большим подспорьем вам будет виртуальный диск, спе- циально разработанный для данной книги. Диск находится в свободном доступе в Интернете на сайте автора книги по адресу book.mirmk.ru/wdiskl и на сайте Издательства www.nit.com.ru. Кроме текстов всех программных примеров из книги в электронном виде, инсталляционного пакета среды разра- ботки IDE, архивов всех используемых в книге программных библиотек, на диске вы найдете видеоролики с авторскими обзорами различных вопросов, помогающими полнее освоить материал из книги, а также набор вспомогательной справоч- ной информации. Автор данной книги будет благодарен за любые отзывы и замечания. Отзывы присылайте по E-mail: belov@mirmk.ru или на сайт http://book.mirmk.ru.
Приложение 1 Основные операторы языка Ардуино Главные функции Оператор setup() loop() Синтаксис void setup() { Строки программы; } void loop() { Строки программы; } Описание Функция используется для инициализации переменных, определения режимов работы линий ввода/вывода и т. п. Функция запускается однократно, либо после нажатия кнопки «Сброс», либо сразу после подачи питания, а также сразу после загрузки в модуль новой версии программы Функция 1оор() многократно выполняется в цикле. В теле этого цикла и выполняются все основные операции программы. Функции setup() и 1оор() должны присутствовать в каждом скетче, даже если эти функции не используются
Управляющие операторы Оператор if if ...else switch... case Синтаксис if (Условие) { Оператор; } if (Условие) Оператор; else Оператор; switch (Переменная) { case Значение1: Оператор; case Значение2: Оператор; case ЗначениеЗ: Оператор; default: Оператор); } Описание Оператор if позволяет выполнять ряд операторов в зависимости от Условия. Условие - это математическое выражение, возвращающее значение Ложь (False) или Истина (True). Для этого используется один из операторов сравнения (<, >, ==, !=). Оператор else дополняет оператор if и позволяет выполнить альтернативный набор операторов в случае не выполнения Условия в соответствующем ему операторе if. Программный переключатель. Оператор позволяет задавать действия, которые будут выполняться при разных условиях. Если значение переменной в скобках у слова swich равно значению у одного из слов case, то оператор(ы) напротив этого слова выполняется(ются). В противном случае соответствующий оператор игнорируется. Команда break, поставленная в любом месте переключателя, досрочно прекращает перебор case и приводит к выходу из swich. Оператор(ы) после управляющего слова default выполняется(ются), если не выбрана ни одна из опций case < q "О О х< q 2
Оператор for while do ...while break continue Синтаксис for (i=0; i <= Значение; i++){ Оператор; Оператор; } while (Условие) { Оператор; Оператор; } do { Оператор; Оператор; } while (Условие); Описание Оператор цикла. В i - переменная цикла. Оператор имеет три параметра. В первом параметре должно быть указано выражение, которое присваивает переменной цикла начальное значение. Второй параметр - это логическое выражение, при выполнении которого цикл продолжается. В третьем параметре размещается выражение, которое выполняет итерацию цикла. В приведенном примере после каждой итерации переменная i увеличивается на единицу Оператор while (Пока) используется, как цикл, который будет выполняться, пока Условие в круглых скобках - истина. Операторы в теле цикла должны обязательно в какой-то момент так изменять условие, чтобы оно приняло значение Ложь. Иначе программа войдет в бесконечный цикл и зависнет Оператор цикла do ...while работает так же, как и цикл while, только проверка условия происходит не в начале, а в конце цикла. Такой цикл всегда выполняется хотя бы один раз Оператор Break используется для принудительного выхода из циклов switch, do, for и while. Оператор continue пропускает оставшиеся операторы в текущей итерации цикла и передает управление в его начало
Операторы цифрового ввода/вывода Оператор pi n Mode () digitalRead() digitalWrite() Синтаксис pinMode(pin, mode) digitalRead(pin) digitalWrite(pin, value) Описание Установка режима работы цифрового входа/выхода. pin - номер контакта. Параметр может принимать значения 1,2,3, 4,5,6,7,8,9,10,11,12,13 (а также 14,15,16,17,18,19); mode - режим работы. Параметр mode может принимать значения: OUTPUT - режим вывода информации (устанавливается по умолчанию); INPUT- режим ввода информации; INPUT_PULLAP- режим ввода с включением внутреннего подтягивающего резистора Функция чтения из цифрового входа. pin - номер контакта (принимает те же значение, что и в двух предыдущих операторах). Функция возвращает значение сигнала на выбранном входе. Возвращаемые значения HIGH или LOW (или, что тоже самое, 1 или 0) Оператор вывода информации на цифровой контакт Ардуино. Параметр pin - номер контакта. pin может принимать те же значения, что и в предыдущем операторе; value - выводимое значение. Value может принимать значение HIGH или LOW (или, что тоже самое, 1 или 0) ■3 ш z "О О го ш I п> > то о с Z о Л) Z ■g л> о 1
1 Операторы аналогового ввода/вывода Оператор analogRead() analogWrite() Синтаксис analogRead(An) analogWrite(pin, value) Описание Функция аналогового чтения с аналогового входа An. Параметр может принимать значения: А0,А1,А2,АЗ,А4,А5,А6 Процедура аналогового вывода. pin - номер контакта. value - выводимое значение. Вывод производится на цифровые выходы модуля в формате широтно-импульсной модуляции (PWM). Номер контакта (pin) может принимать значение (для Arduino UNO) - 3,5, 6,9,10,11. Значение параметра value может принимать значения от 0 до 255 ю VI
Операторы времени Оператор millisO micros() delay(tz) delayMicroseconds(tz) Синтаксис Описание Функция возвращает количество миллисекунд, прошедшее с запуска программы Функция возвращает количество микросекунд, прошедшее с запуска программы Функция программной задержки. Параметр tz определяет время задержки в миллисекундах Функция программной задержки. Параметр tz определяет время задержки в микросекундах
Расширенный ввод/вывод Оператор tone() noTone() shiftOutQ shiftln() Синтаксис tone(pin,fq) tone(pin,fq,tz) noTone(pin) shiftOut(dataPin, clockPin, bitOrder, value) shiftln(dataPin, clockPin, bitOrder) Описание Процедура генерации звукового сигнала на цифровом выходе. pin - вывод, на котором будет генерироваться сигнал. fq - частота сигнала в герцах (unsigned int). tz - длительность сигнала в миллисекундах (unsigned long). Если не указан, звук генерируется, пока не поступит команда noToneQ Прекращение генерации звука на указанном контакте Функция последовательного вывода байта данных. Программно реализует SPI канал, используя любые два цифровых контакта. dataPin - контакт, через который побитно выводятся передаваемые данные. clockPin - контакт, который используется для вывода сигнала синхронизации. clockPin - порядок передачи битов. Принимает значения: MSBFIRST (старший бит вперед), LSBFIRST (младший бит вперед). value - передаваемый байт данных. Возвращаемого значения нет Функция последовательного ввода байта данных. Работает в паре с функцией shiftOut(). Параметры dataPin, clockPin и bitOrder аналогичны соответствующим параметрам shiftOut. Функция возвращает считанный байт о го X 20 О
Оператор pulseln() Синтаксис pulseln(pin, value) pulseln(pin, value, timeout) Описание Функция измерения длительности входного импульса. pin - контакт, на котором происходит измерение. value — полярность измеряемого импульса: Если value = HIGH — измеряется длительность положительного импульса. Если value = LOW, измеряется длительность отрицательного импульса. timeout - время ожидания начала импульса (по умолчанию 1 с). Функция возвращает длительность импульса. Функция работает с импульсами длительностью от 10 микросекунд до 3 минут О 00 "О о •5 QJ 2 О S О 9 "О Ш I q о q S
Работа с последовательным портом =1 ■о Оператор Serial.begin() SeriaLprint() Serial.println() Serial.available() Serial.read() Serial.write() Serial.flushO Синтаксис Serial.begin(rate) SeriaLprint(data) SeriaLprintln(data) Serial.available() Serial.read() SeriaLwrite(val) Serial.write(str) Serial.write(buf, len) Serial.flushO Описание Инициализация последовательного порта. rate - скорость передачи информации. Может принимать значения: 300,1200,2400,4800,9600,14400, 19200,38400,57600 или 115200 Передача строки данных в последовательный порт (печать текста в последовательный порт) Передача строки данных в последовательный порт, завершаемый символом перевода строки Функция проверки количества считанных по последовательному порту байт в буфере. Байты помещаются в буфер автоматически по поступлению Считывание очередного байта из буфера последовательного порта. Если данные отсутствуют, функция возвращает-1 (минус один) Запись данных в последовательный порт. val - число от 0 до 255 (байт). str - текстовая строка в виде набора байтов. buf - массив данных в виде набора байтов. len - длина массива данных Ожидание окончания процесса передачи данных О
270 Программирование ARDUINO. Создаем практические устройства Приложение 2. Типы данных в Arduino IDE Тип boolean byte char unsigned char int unsigned int word long unsigned long float double Кол-во байт 1 байт 1 байт 1 байт 1 байт 2 байта 2 байта 2 байта 4 байта 4 байта 4 байта 4 байта Диапазон значений 0 или 1 0...255 -128 ...127 0...255 -32768 ...32767 0 до 65535 0 до 65535 -2 147 483 648... 2 147 483 647 0... 4 294 967 295 -3.4028235Е38... 3.4028235Е38 Примечание Логическое значение TRUE или FALSE (правда/ложь) число в диапазоне от 0...255 Хранит код символа в кодировке ASCII Хранит код символа в кодировке ASCII Целое число со знаком Целое число без знака То же самое, что unsigned int Числа с плавающей точкой. Точность 6...7 знаков То же самое, что float Примечания: unsigned char — то же что и byte. Для лучшей читаемости удобнее использовать byte. unsigned int — то же самое, что и word. Для лучшей читаемости используйте word. int — один из самых распространенных типов данных, который очень часто используется для объявления переменных в скетчах для Arduino. unsigned long — чаще всего этот тип данных используется для хра- нения результатов функции millis(), которая возвращает количество миллисекунд, прошедших с момента начала работы программы. float — числа с плавающей запятой не характерны для Arduino. Компилятор обрабатывает программы, использующие этот тип данных работают очень долго. Рекомендуется по возможности избегать приме- нения этого типа данных.
Список литературы 1. Белов А.В. Микроконтроллеры AVR от азов программирова- ния до создания практических устройств. — СПб.: Наука и Техника, 2016.- ISBN: 978-5-94387-854-1 2. Белов А.В. Программирование микроконтроллеров для начи- нающих и не только... — СПб.: Наука и Техника. — ISBN: 978-5-94387- 867-1 3. Белов А.В. Разработка устройств на микроконтроллерах AVR, шаг за шагом от «чайника» до профи. — СПб.: Наука и Техника, 2013. - ISBN: 978-5-94387-825-1 4. Белов А.В. Самоучитель разработчика устройств на микрокон- троллерах AVR, второе издание. — СПб.: Наука и Техника, 2010 г. — ISBN: 978-5-94387-808-4 5. Белов А.В. Самоучитель разработчика устройств на микрокон- троллерах AVR.. - СПб.: Наука и Техника, 2008. - ISBN: 978-5-94387- 363-8 6. Белов А.В. Самоучитель по микропроцессорной технике. Изд. 2. - СПб.: Наука и Техника, 2007. - ISBN: 978-5-94387-190-0 7. Белов А.В. Создаем устройства на микроконтроллерах. — СПб.: Наука и Техника, 2007. - ISBN: 978-5-94387-364-3 8. Белое А.В. Микроконтроллеры AVR в радиолюбительской прак- тике. - СПб.: Наука и Техника, 2007. - ISBN: 978-5-94387-365-2 Список ссылок на ресурсы в интернет 1. http://book.mirmk.ru Сайт поддержки всех книг автора (Белова Александра). Здесь вы найдете дополнительные материалы к книге, тексты всех программ- ных примеров в электронном виде, описание других книг на ту же тему. 2. https://www.arduino.cc/ Сайт разработчика проекта Ардуино (англоязычный). На этом сайте вы можете подробно прочитать обо всех вариантах модуля Ардуино, а также скачать пакет для установки интегрированной среды разработчика (IDE) и любую его Бетта версию.
HJ Издательство «Наука и Техника» КНИГИ ПО КОМПЬЮТЕРНЫМ ТЕХНОЛОГИЯМ, МЕДИЦИНЕ, РАДИОЭЛЕКТРОНИКЕ Уважаемые читатели! Книги издательства «Наука и Техника» вы можете: > заказать в нашем интернет-магазине WWW.nit.COm.ru (более 100 пунктов выдачи на территории РФ) «Новый книжный» Сеть магазинов ТД«БИБЛИО-ГЛОБУС» Московский Дом Книги, «ДК на Новом Арбате» Московский Дом Книги, «Дом технической книги» Московский Дом Книги, «Дом медицинской книги» Дом книги «Молодая гвардия» > приобрести в Москве: тел. (495) 937-85-81, (499) 177-22-11 ул. Мясницкая, д. б/З, стр. 1, ст. М «Лубянка» тел. (495) 781 -19-00,624-46-80 ул.Новый Арбат, 8, ст. М «Арбатская», тел. (495) 789-35-91 Ленинский пр., д.40, ст. М «Ленинский пр.», тел. (499) 137-60-19 Комсомольский пр., д. 25, ст. М «Фрунзенская», тел. (499) 245-39-27 ул. Б. Полянка, д. 28, стр. 1, ст. М «Полянка» тел. (499) 238-50-01 > приобрести в Санкт-Петербурге: Санкт-Петербургский Дом Книги Невский пр. 28, тел. (812) 448-23-57 Буквоед. Сеть магазинов тел. (812) 601 -0-601 > приобрести в регионах России: г. Воронеж, «Амиталь» Сеть магазинов г. Екатеринбург, «Дом книги» Сеть магазинов г. Нижний Новгород, «Дом книги» Сеть магазинов г. Владивосток, «Дом книги» Сеть магазинов г.Иркутск, «Продалить» Сеть магазинов г. Омск, «Техническая книга» ул. Пушкина, д.101 тел. (473) 224-24-90 тел. (343) 289-40-45 тел. (831) 246-22-92 тел. (423) 263-10-54 тел. (395) 298-88-82 тел. (381) 230-13-64 Мы рады сотрудничеству с Вами!