PDE. Урок 2. Небольшие нюансы при программировании — Arduino и Processing — Arduino с нуля — Каталог статей — GM-project.com

image Всем привет. Меня зовут Никита. Я один из участников проекта «Философия Звука». Это моя первая статья на блоге — строго не судите. В комментариях можете поделиться своими размышлениями по теме. Это будет полезно как мне, так и всем остальным. Начнём). Arduino Только «ленивый» не знает про такую разработку как «Arduino». Хотя как раз и «ленивые», в том числе, используют её в своих задачах. Существует много разных мнений по поводу «Arduino». Есть как сторонники, так и противники – классика жанра. К какому лагерю отношусь я? Сложно ответить однозначно. Могу лишь сказать, что «Arduino» мне нравится в том варианте, который использую именно я). Вкратце расскажу из чего состоит платформа: image Имеется ПО Arduino, в котором пользователь пишет специальный текст — код. Далее этот код преобразуется встроенным в ПО компилятором AVR GCC в другой специальный текст (машинный язык). Также в ПО Arduino интегрирована консольная программка AVRDude для прошивки микроконтроллеров AVR. С её помощью написанный нами программный код отправляется в память микроконтроллера через USB порт ПК. Со стороны «железа» Arduino есть, или приобретается отдельно, преобразователь USB-USART. Он служит мостом между программной и аппаратной частью Arduino. Вообще, «чистый» контроллер AVR прошить по интерфейсу USART нельзя. Поэтому изготовитель Arduino зашивает в микроконтроллер программу — «загрузчик» (пользователь тоже может это проделать). Вот теперь контроллер принимает наш «машинный код» и сохраняет его у себя в памяти. Ну и потом мы проверяем, что всё правильно работает или опять не работает). Недостатки программирования в Arduino Добрались до самого интересного — программирования). Эта статья не посвящена какому-то отдельному проекту. Она только подготовит тех, кто выберет тот же путь программирования в Arduino, что и я. Казалось бы, что не нравится? Многие пишут в Arduino так, как задумали разработчики, и даже проекты рабочие и симпатичные выходят. Есть куча библиотек для различных подключаемых электронных устройств: датчики температуры, дисплеи, сервоприводы, GSM модули и многое другое. Но у этих библиотек есть недостатки, связанные со стилем программирования в Arduino:

  • Из-за универсальности код в Arduino IDE выполняется медленно и занимает много места в памяти микроконтроллера — критично для «насыщенных» проектов.
  • Даже в библиотеках используются задержки, во время которых контроллер ничего не делает — неэффективное использование ресурсов.
  • Весь код программы приходится писать в одной странице Arduino IDE, что затрудняет написание, чтение и редактирование большого проекта.
  • Нет «прозрачности» написания программ. Мы не видим, что скрывается за скетчем — не знаем, какие регистры используются, какие прерывания выполняются без нашего ведома. Поэтому не можем напрямую работать с регистрами и прерываниями.

Хочется устранить недостатки и расширить возможности платформы Arduino. К лёгкой прошивке по USB кабелю добавить возможность написания программы с разбиением на файлы. Использовать полноценное программирование микроконтроллера, с применением регистров и векторов прерываний. Если коротко, то мне хотелось бы совместить лёгкую прошивку микроконтроллера AVR, как это сделано в Arduino IDE, и возможность написания «быстрого»«прозрачного», разбитого на файлы и занимающего мало места кода на C/C++ как, например, в IAR Embedded Workbench. «Исправление» Arduino IDE Мы используем 8-битные микроконтроллеры от AVR, поэтому будем работать с последней классической  версией Arduino 1.0.6.  Программисты на C/C++ привыкли, что главной функцией является функция main(), но здесь её нет… или может быть она скрыта?  Копнём в папку установки Arduino. Вот здесь можно много чего интересного найти:  C:Program Files (x86)Arduino-1.0.6hardwarearduinocoresarduino. Начнём, конечно же, с найденного там файла main.cpp. О чудо, вот она main() … нет, так и должно было быть 🙂 Вот и нашлись наши знакомые void setup() и void loop(), которые нужно определять в каждом скетче. Эти функции мы перенесём в свой файл, например, main_my.cpp и там определим. Это позволит разбить программу на файлы.  Из интересного тут ещё функция init(). Её определение можно найти в файле wiring.c. Функция длинная, поэтому покажу только её начало: Здесь глобально разрешаются прерывания. Происходит настройка и запуск всех таймеров, которые имеются в микроконтроллере. Да-да, всё, что связано с функциями задержек, ШИМ, отсчётов временных интервалов от старта контроллера, предварительно настраивается тут. Выбирается частота работы АЦП, и сразу же АЦП включается. И в самом конце сбрасываются настройки USART, который использовался для «загрузчика». Получается, что все предварительные настройки, которые используют библиотеки Arduino, описаны в init(). Если мы уберём эту функцию, то сможем работать с регистрами и векторами прерываний микроконтроллера напрямую, не боясь, что они уже используются. И что теперь будем делать? Править функцию main(), чтобы отключать ненужную нам работу таймеров или АЦП? А если хочется совмещать свой код, например, с уже готовой удобной библиотечной отладкой по USART? Да и не все пользователи Arduino оценят такой подход. Нужно такое решение, которое бы устроило всех, и оно есть:  Оставим всё как есть. И в нашем проекте, если это нужно, остановим всю периферию, которой не будем пользоваться с помощью библиотек Arduino или которая нам не нужна совсем.  Атрибуты Кто-то спросит, а что там с прерываниями? Например, если остановим Timer 0, то как мы сможем использовать прерывание TIM0_OVF_vect, ведь этот вектор уже используется средой для формирования задержек, а компилятор обязательно будет ругаться на повторное применение? Выход такой: в компиляторе GCC существуют особые способы объявления функций, называются они атрибуты. Ключевое слово для использования __attribute__. В частности есть такой атрибут weak. Он делает функцию «слабой». Это значит, что если функция определена и объявлена как weak в одном месте, то в другом — мы можем написать её второе определение, и вызываться будет именно оно. А если не напишем, тогда будет использоваться определение с атрибутом weak TIM0_OVF_vect, но чтобы была возможность в другом проекте использовать функции Arduino? Нужно в исходниках Arduino в файле wiring.c (именно тут используется этот вектор) написать такую строчку:  ISR(TIMER0_OVF_vect) __attribute__((weak));  Выглядеть эта доработка будет так: Аналогично можно дообъявить другие уже использующиеся вектора. Да, нам придётся исправить исходные файлы среды Arduino. Но зато всего один раз, и потом мы «забудем » об этом :). Универсальный скетч Больше не буду забивать Вам голову умными мыслями), а покажу как теперь будет выглядеть скетч для всех проектов. Он поможет отвязаться от написания всей программы в одном окне Arduino IDE: Файл main_my.h пустой и находится вместе со всеми файлами нашей программы. В файле main_my.cpp определены функции void setup() и void loop(). Собственно, дальше можно уже писать свою программу в любом удобном для Вас текстовом редакторе, например Notepad++. Добавлять столько *.cpp и *.h файлов, сколько нужно для проекта. Если необходимо, делать код ёмким и быстрым — без использования функций Arduino. Или, для быстроты реализации программы, совмещать свой код с кодом Arduino. На этом всё :). Ну а в следующий раз напишем какую-нибудь очень полезную программу). С уважением, Никита О. в Arduino & Pi 4 года назад

Привет начинающим ардуинщикам! 🙂

После небольшого перерыва, наконец-то готова 8-я видео-статья из цикла В«Основы Arduino для начинающихВ» и сегодня мы поговорим о таком важном инструменте в арсенале программиста, как функции. Научимся с ними работать и использовать их в своем коде.

Предыдущие выпуски вы найдете здесь: ,1,2,3,4,5,6,7

Видеоверсия поста:

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

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

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

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

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

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

Для начала возьмем очень простую задачу. Допустим, что в вашей программе часто (а говоря часто, я имею в виду 2 и более раза), приходится использовать один и тот же фрагмент кода — например, зажигать несколько светодиодов и подавать сигнал в зависимости от состояния какого-либо пина. Можно пойти по сложному пути и всякий раз писать лишние строки в те места кода, где это понадобится, а можно всего один раз написать функцию и затем вызывать ее в нужных местах – что мы сейчас и сделаем.

Простейшая функция выглядит следующим образом:

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

Итак, заготовка простейшей функции готова и в ней уже написан необходимый нам код.

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

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

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

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

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

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

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

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

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

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

Тогда программа, дойдя до этого момента, будет поступать так – она переходит в указанную нами функцию, выполняется там, доходит до оператора return и запоминает значение, которое мы указали. Это значение она записывает по адресу переменной result. Теперь result равен тому числу, которое возвращает наша функция.

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

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

Обязательно поделитесь своими успехами или вопросами в комментариях, ну а на этом про функции все, спасибо за то, что дочитали до конца и до встречи в 9-м выпуске! 😉

Сегодня мы познакомимся с интересным протоколом 1-Wire и даже сразу попробуем поработать с устройством, использующим этот протокол. «Как?» — спросите Вы – «мне не нужно будет покупать детали в радиомагазине?» «Нет» — отвечу я 🙂 С большой долей вероятности, Вы пользуетесь устройством 1-Wire и, возможно, даже не догадываетесь об этом 🙂 Это всего-навсего – ключ-таблетка от домофона (iButton)! Неужели? Достанем ключи из кармана и примемся их пристально разглядывать 🙂 У меня контактная площадка сильно затёрта, но какие-то надписи проглядывают. Потёр площадку фломастером и становится видна заветная надпись: iButton.com 00000F67CE41 1-Wire 0744 UB1 DS1990A#F5 Ага! 1-Wire! Так что же это за интерфейс? Это однопроводной интерфейс, разработан фирмой Dallas Semiconductor (ныне MAXIM) в конце 90-х годов. Этот интерфейс интересен тем, что для двустороннего обмена требуется всего одна линия! (отсюда и название 🙂 Подробнее об интерфейсе здесь. Возвращаемся к нашему ключу 🙂 На самой таблетке указан тип устройства и, похоже, уникальный номер микросхемы 😉 DS1990A – это и есть название нашего устройства. Смотрим варианты iButton-ов на сайте производителя Наш ключ — это самая первая модель в списке 🙂 DS1990A — 64 Bit ID — уникальный серийный номер-ключ Страничка этого ключика здесь, а здесь можно скачать датащит 😉 Число после # определяет форм-фактор ключа – у меня F5. Итак, наш ключ хранит 64 бита уникальной информации Питается от 2.8V до 6.0V К центральной контактной площадке подключается линия данных, а к боковой каёмке – землю. Ключ может работать в диапазоне температур от -40°C до +85°C – впрочем, это нам не нужно 🙂 Читаем датащит далее 🙂 Величина подтягивающего резистора рекомендуется в 2.2k Так… запомним 🙂 Каждый DS1990A прошивается уникальным 64-битным номером первые 8 бит — номер серии устройства (01h)следующие 48 бит — уникальный серийный номер последние 8 бит — CRC-код предыдущих 56 бит информации 🙂 (Cyclic redundancy code, CRC — циклический избыточный код) — способ цифровой идентификации некоторой последовательности данных, который заключается в вычислении контрольного значения её циклического избыточного кода. Далее в датащите перечислены команды, которые понимает ключ: 33h — считать ROM — команда позволяет считать заветные 64-бита данных ключа F0h — поиск ROM — т.к. к сети 1-wire может быть подключено несколько устройств данная команда используется для идентификации подключённых устройств. 55h — соответствие ROM / CCh — пропуск ROM — данные команды входят в минимальный набор необходимых команд 1-wire-устройств. Т.к. в ключе DS1990A эти команды не требуются — он на них не отвечает. Arduino может работать с 1-Wire, используя библиотеку Jim Studt-а – OneWire. Настало время пообщаться с нашим ключиком! 🙂 Схема подключения – крайне проста (это ведь 1-Wire! 🙂 Далее нужно скачать библиотеку OneWire и поместить её в свою директорию libraries. Код скетча:

  1. #include
  2. /*
  3. * тестируем работу по 1-Wire с ключём-таблеткой DS1990A
  4. */
  5. OneWire ds(10); // на digital pin 10
  6. void setup(void) {
  7. Serial.begin(9600);
  8. }
  9. void loop(void) {
  10. byte i;
  11. byte present = 0;
  12. byte data[12];
  13. byte addr[8];
  14. if ( !ds.search(addr)) {
  15. Serial.print(«No more addresses.n«);
  16. ds.reset_search();
  17. return;
  18. }
  19. Serial.print(«R=»);
  20. for( i = 0; i <</span> 8; i++) {
  21. Serial.print(addr[i], HEX);
  22. Serial.print(» «);
  23. }
  24. if ( OneWire::crc8( addr, 7) != addr[7]) {
  25. Serial.print(«CRC is not valid!n«);
  26. return;
  27. }
  28. if ( addr[0] != 0x01) {
  29. Serial.print(«Device is not a DS1990A family device.n«);
  30. return;
  31. }
  32. Serial.println();
  33. ds.reset();
  34. delay(1000);
  35. }

Происходит постоянный поиск устройств на линии 1-Wire. Пока ключ не подключён – выдаётся: No more addresses. При подключении ключа-таблетки – считывается её номер и выдаётся в COM-порт. Например, вот что выдаётся при подключении ключа-таблетки, с которого начиналась эта статья: Первая цифра – номер серии – 01h Далее, как видим, действительно ключ хранит номер, нанесённый на своём корпусе 😉 Только этот номер выведен наоборот – как и положено по протоколу 1-Wire – начиная с младшего байта 😉 Последний байт –B6h — CRC-код предыдущих байт.  Ура! Вот мы и считали наш ключ 🙂 Теперь можно самостоятельно сделать замок для комнаты или разблокировать свой компьютер по ключу 🙂 Фантазируем 🙂

Общие сведения

В этом проекте мы соединим две платы Arduino ULTRA/Piranha ULTRA через интернет. Сделаем мы это при помощи сервиса ioControl.

Видео

Нам понадобится

  • 2x Ethernet Shield W5500
  • 2x Piranha ULTRA
  • 2x Trema Shield
  • 2x Battery Shield
  • 1x Модуль кнопка
  • 1x Модуль светодиод

Создание и настройка панели на iocontrol.ru

  1. Создаём или входим в учётную запись на сайте iocontrol
  2. нажимаем «Создать панель». Придумываем название панели, например «myNewPanel» (название панели должно быть уникальным на сайте. Если панель с таким именем уже есть, сайт уведомит об этом). Нажимаем Создать.
  3. Создаём переменную целочисленного типа и назывем её myButton.
  4. Настраиваем вид переменной как кнопки(Нажимаем на пиктограмму шестерёнки в правом верхнем углу карточки переменной и в открывшейся странице выбираем вид «Кнопка».

Подключение

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

Устанавливаем модуль Ethernet Shield W5500 на Piranha ULTRA

Устанавливаем Battery Shield

Устанавливаем сверху Trema Shield

Подключаем Trema-модуль Кнопка к одному устройству.

Подключаем Trema-модуль Светодиод к другому устройству.

Скетч проекта

Для скетча мы воспользуемся генератором скетча для IDE на сайте ioControl.

Нажмём на кнопку «Генератор скетча для IDE».

Выберем необходимые параметры. В нашем случае это:

Параметр Устройство с кнопкой Устройство со светодиодом
Контроллер Arduino/Piranha ULTRA Arduino/Piranha ULTRA
Shield Ethernet Shield w5500 Ethernet Shield w5500
Вывод SS D10 D10
IP адрес DHCP DHCP
Mac адрес 0xCE 0xCC
Панель панель с переменной myButton панель с переменной myButton
Переменные на чтение myButton
Переменные на запись myButton

После генерации нам остаётся добавить код только для наших модулей в кадый из скетчей соответственно.

Так же необходимо установить библиотеку iocontrol. Если Вы не знаете как устанавливать библиотеки в Arduino IDE — Вы можете узнать по этой ссылке

Скетч для кнопки

Скетч для светодиода

Ссылки

  • Ethernet Shield W5500
  • Бесплатный сервис ioControl
  • Создание учётной записи в сервисе ioControl
  • Создание панели в ioControl
  • Создание переменных в ioControl
  • Подготовка Arduino к работе с ioControl
  • Библиотека iocontrol
  • Wiki — Установка библиотек в Arduino IDE.
image Arduino и Threads

Arduino

В микропроцессорной технике параллельно выполняющиеся задачи называются тредами (Threads) – потоками. Это очень удобно, ведь часто бывает необходимо выполнять несколько операций одновременно. А можно ли заставить микроконтроллер Arduino выполнять сразу несколько задач, как настоящий процессор? Сейчас посмотрим.

Нам понадобится:

  • Arduino UNO или иная совместимая плата;
  • 1 светодиод (вот из такого набора, например);
  • 1 пьезопищалка (вроде этой);
  • соединительные провода (рекомендую вот такой набор);
  • макетная плата (breadboard);
  • персональный компьютер со средой разработки Arduino IDE.

Инструкция по созданию параллельных потоков в программе для Arduino 1Схема подключения для демонстрации потоков в работе с Arduino Вообще говоря, Arduino не поддерживает настоящее распараллеливание задач, или мультипоточность. Но можно при каждом повторении цикла loop() указать микроконтроллеру проверять, не наступило ли время выполнить некую дополнительную, фоновую задачу. При этом пользователю будет казаться, что несколько задач выполняются одновременно. Например, давайте будем мигать светодиодом с заданной частотой и параллельно этому издавать нарастающие и затихающие подобно сирене звуки из пьезоизлучателя. И светодиод, и пьезоизлучатель мы уже не раз подключали к Arduino. Соберём схему, как показано на рисунке. Если вы подключаете светодиод к цифровому выводу, отличному от «13», не забывайте о токоограничивающем резисторе примерно на 220 Ом. image
Схема подключения к Arduino для демонстрации параллельных потоков
2 Управление светодиодом и пьезоизлучателемс помощью оператора delay() Напишем вот такой скетч и загрузим его в Ардуино. После включения видно, что скетч выполняется не совсем так как нам нужно: пока полностью не отработает сирена, светодиод не мигнёт, а мы бы хотели, чтобы светодиод мигал во время звучания сирены. В чём же здесь проблема? Дело в том, что обычным образом эту задачу не решить. Задачи выполняются микроконтроллером строго последовательно. Оператор delay() задерживает выполнение программы на указанный промежуток времени, и пока это время не истечёт, следующие команды программы не будут выполняться. Из-за этого мы не можем задать разную длительность выполнения для каждой задачи в цикле loop() программы. Поэтому нужно как-то сымитировать многозадачность. 3Параллельные процессы без оператора «delay()» Вариант, при котором Arduino будет выполнять задачи псевдо-параллельно, предложен разработчиками Ардуино. Суть метода в том, что при каждом повторении цикла loop() мы проверяем, настало ли время мигать светодиодом (выполнять фоновую задачу) или нет. И если настало, то инвертируем состояние светодиода. Это своеобразный вариант обхода оператора delay(). Существенным недостатком данного метода является то, что участок кода перед блоком управления светодиодом должен выполняться быстрее, чем интервал времени мигания светодиода «ledInterval». В противном случае мигание будет происходить реже, чем нужно, и эффекта параллельного выполнения задач мы не получим. В частности, в нашем скетче длительность изменения звука сирены составляет 200+200+200+200 = 800 мсек, а интервал мигания светодиодом мы задали 200 мсек. Но светодиод будет мигать с периодом 800 мсек, что в 4 раза больше того, что мы задали. Вообще, если в коде используется оператор delay(), в таком случае трудно сымитировать псевдо-параллельность, поэтому желательно его избегать. В данном случае нужно было бы для блока управления звуком сирены также проверять, пришло время или нет, а не использовать delay(). Но это бы увеличило количество кода и ухудшило читаемость программы. 4Использование библиотеки ArduinoThreadдля создания параллельных потоков Чтобы решить поставленную задачу, воспользуемся замечательной библиотекой ArduinoThread, которая позволяет с лёгкостью создавать псевдо-параллельные процессы. Она работает похожим образом, но позволяет не писать код по проверке времени – нужно выполнять задачу в этом цикле или не нужно. Благодаря этому сокращается объём кода и улучшается читаемость скетча. Давайте проверим библиотеку в действии. image
Библиотека ArduinoThread
Первым делом скачаем с официального сайта архив библиотеки и разархивируем его в директорию libraries/ среды разработки Arduino IDE. Затем переименуем папку ArduinoThread-master в ArduinoThread. Схема подключений останется прежней. Изменится лишь код программы. В программе мы создаём два потока – ledThread и soundThread, каждый выполняет свою операцию: один мигает светодиодом, второй управляет звуком сирены. В каждой итерации цикла для каждого потока проверяем, пришло ли время его выполнения или нет. Если пришло – он запускается на исполнение с помощью метода run(). Главное – не использовать оператор delay(). В коде даны более подробные пояснения. image
Параллельное выполнение потоков на Arduino
Загрузим код в память Ардуино, запустим. Теперь всё работает в точности так, как надо!

Последнее изменениеПятница, 01 Ноябрь 2019 19:29 Прочитано 68588 раз

Оцените статью
Рейтинг автора
5
Материал подготовил
Илья Коршунов
Наш эксперт
Написано статей
134
А как считаете Вы?
Напишите в комментариях, что вы думаете – согласны
ли со статьей или есть что добавить?
Добавить комментарий