Мультизадачность с Ардуино: несколько процессов одновременно

Этот материал описывает метод работы с Ардуино при котором создается иллюзия, что плата выполняет одновременно несколько задач.

Используя этот метод, Ардуино сможет одновременно делать ряд процессов:

  • переключение светодиода каждые 300 мс (миллисекунды);
  • прокрутка строки текста каждые 250 мс;
  • изменение настраиваемой гистограммы каждые 100 мс.

Вы сможете использовать данный материал как шаблон для своих собственных проектов.

Комплектующие

Для этого проекта требуется очень немного деталей:

  • 1x - микроконтроллер Arduino Uno R3
  • 1x - I2C ЖК-дисплей 16x2
  • 1x - USB-кабель arduino-to-PC
  • 4x - провода для перемычек с разъемами «папа-мама»

Все детали можно заказать в любом интернет-магазине.

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

Изображение выше показывает как нам нужно всё соединить.

Код для Ардуино

Код вы найдете ниже. Вы можете скопировать его, либо скачать файл .ino и вставить его содержимое в новый эскиз arduino.

Вам также потребуется установить библиотеку I2C для вашего ЖК-дисплея. Можно посмотреть в наших библиотеках на сайтеили использовать библиотеку скачанную с http://arduino-info.wikispaces.com/LCD-Blue-I2C#v1. Если у вас есть другой чип, а не тот, что отмечен на фото выше - вам нужно будет скачать другую библиотеку.

После того как вы найдете подходящую библиотеку, загрузите zip-файл и установите её, используя нашу инструкцию.

Теория

Пустой цикл loop(){} в коде arduino заставит ардуино «вращаться» (прим.ред. - цикличность) несколько тысяч раз в секунду. Давайте посмотрим, что произойдет, когда мы добавим эту задачу

Одна задача.

Следующий код будет отображать «Hello World!» на вашем последовательном мониторе один раз в секунду:

Каждому циклуloop(){} теперь нужно более 1 секунды для завершения из-за команды задержки delay(1000).

Предполагая, что arduino имеет 16-мегагерцовый кристалл, команда delay(1000) тратит 16 000 000 тактов. Другие задачи возможны, если мы устраним эту задержку.

Многозадачность.

Давайте перепишем вышеприведенный фрагмент кода:

"Привет мир!" выведется после чего цикл loop(){} возвращается к цикличности с полной скоростью до тех пор, пока флажок 1 = истина.

Поскольку loop(){} нечего делать, а процесс работает на полной скорости, добавим еще одну задачу.

Эта часть кода заставляет светодиод «переключаться», после чего loop(){} снова возвращается в процесс, пока флаг Flag2 = true (флажок 2 = ложь).

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

Длительные задачи требуют специальных методов кода.

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

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

Это достигается за счет использования «статических» переменных* для отслеживания того, как далеко продвинулась задача. В данном случае это показывают функции «scrollMessage ()» и «bargraph()».


* Когда подпрограмма вызывается, все «переменные» забывают свои предыдущие значения, если они не были объявлены «статическими», и в этом случае предыдущее значение доступно при вызове следующей подпрограммы.


Планировщик заданий

Секрет установки флагов - создать цикл в 1 миллисекунду (мс) с использованием одного из таймеров Arduino Uno R3. Можно использовать Timer/Counter 2 (8 бит), который оставляет Timer/Counter 1 (16-бит) свободным для других задач.

1 мс достигается путем деления частоты 16 000 000 Гц на 128 для получения интервала времени 8 микросекунд. Если теперь считать эти импульсы 8 мкс и генерировать прерывание, когда счетчик достигнет 125, то 125 x 8 или 1 мс истечет**.


** Фактически мы загружаем 125-1 = 124 в «сравнительный регистр соответствия», потому что прерывание не происходит до следующего (125-го) тактового импульса.


Флаги устанавливаются путем помещения следующего кода в ISR (процедуру обслуживания прерываний):

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

Основной цикл loop(){} видит каждый флаг, который задает планировщик задач и выполняет задачу. Добавление дополнительных задач достаточно просто. Создайте дополнительный счетчик и флаг, а затем имитируйте приведенный выше код. На графике выше показана взаимосвязь между тремя задачами, которые выполняет ардуино в данном примере.

Вы можете заметить, что:

  • иногда loop(){} не имеет задач для выполнения;
  • иногда loop(){} выполняет только одну или две задачи;
  • каждая из задач вызывается разное количество раз;
  • гистограмма продвигается каждые 100 мс;
  • текст прокручивается каждые 250 мс;
  • светодиод переключается каждые 300 мс.

Ключевые моменты

Для успешной многозадачности:

  • Избегайте функций delay() или delayMicroseconds(). Вместо этого
  • создавайте циклы и
  • используйте планировщик задач,
  • а длительные задачи следует рассматривать как серию небольших «кусков»
  • со «статическими» переменными, отслеживающими прогресс.

На этом пока всё про мультизадачность в Ардуино. Хороших вам проектов.