Переключение задач
Важнейшим инструментом по организации логики работы программы является так называемый конечный автомат, он же машина состояний (англ. State Machine) - значение, которое имеет заранее известный набор состояний и программа работает по-разному на каждом из них. Звучит сложно, но на самом деле речь идёт об операторе switch
(урок про оператор выбора) и переменной, которая переключается кнопкой или по таймеру. Значение этой переменной - код или номер состояния, задачи, режима, эффекта - в данном случае это всё синонимы. Например:
if (клик по кнопке 1) mode++; if (клик по кнопке 2) mode--; switch (mode) { case 0: // задача 0 break; case 1: // задача 1 break; case 2: // задача 2 break; // ......... }
Таким образом организуется выбор и выполнение выбранных участков кода, переключение режимов работы, эффектов и так далее.
Переключение режима
Переключение переменной mode
может быть сделано не просто так, как в примере выше, тут есть варианты:
- Ограничить диапазон переменной
mode
по минимальному номеру задачи (обычно0
) и максимальному (количество задач - 1
). - Сделать переключение с последней задачи на первую и наоборот, т.е. "закольцевать" изменение.
В рассмотренных ниже примерах MODES
- количество задач, то есть при 10 задачах переменная будет принимать значения от 0 до 9.
Ограничение
if (mode < MODES - 1) mode++; // на увеличение if (mode > 0) mode--; // на уменьшение
Переключение
if (++mode >= MODES) mode = 0; // на увеличение if (--mode < 0) mode = MODES - 1; // на уменьшение
Рассмотрим несколько готовых примеров на базе библиотеки EncButton:
Массив функций
Выше мы рассмотрели переключение задач при помощи оператора switch
. Это классическая, громоздкая и всем понятная конструкция. Но что если у нас будет 10 задач, или 50? Например эффекты для светодиодной ленты. Можно вообще избавиться от switch
и реализовать переключение задач гораздо более компактно - при помощи массива указателей на функции (урок про указатели). Рассмотрим пример, в котором задачи переключаются по таймеру с периодом 1 секунда. Функция текущей активной задачи просто вызывается в основном цикле программы.
// задачи, которые будут выполняться void task1() { } void task2() { } void task3() { } // количество задач #define T_AMOUNT 3 // массив функций, передаём имена функций задач void (*func[])() = {task1, task2, task3}; void setup() { } uint32_t tmrSwitch; // таймер byte task; // номер текущей задачи void loop() { // таймер if (millis() - tmrSwitch >= 1000) { tmrSwitch = millis(); if (++task >= T_AMOUNT) task = 0; // переключаем по кругу } func[task](); // вызов текущей функции-задачи }
И всё! Пример очень легко масштабируется для любого количества задач, достаточно создать дополнительные функции и поместить их в массив, а также изменить количество задач для переключения.
Флаги
Логические переменные, или флаги, являются очень важным инструментом организации логики работы программы. В глобальном флаге можно хранить "состояние" составляющих программы, и они будут известны во всей программе и во всей же программе могут быть изменены. Немного утрированный пример:
bool flag = false; void loop() { // если был клик по кнопке, поднять флаг if (buttonClick()) flag = true; if (flag) { // какой-то код } }
Состояние глобального флага может быть прочитано в любых других функциях и местах программы, таким образом можно сильно упростить код и избавиться от лишних вызовов. При помощи флага можно организовать однократное выполнение блока кода по какому-то событию:
bool flag = false; void loop() { // если был клик по кнопке, поднять флаг if (buttonClick()) flag = true; if (flag) { flag = false; // выполнится один раз после клика по кнопке } }
Также флаг можно инвертировать, что позволяет генерировать последовательность 10101010 для переключения каких-то двух состояний:
bool flag = false; void loop() { // допустим, условие выполняется периодчисеки по таймеру if (timerElapsed()) { flag = !flag; // инвертировать флаг // например, нужно передавать в функцию два значения, // чередуя их по таймеру setSomeValue(flag ? 10 : 200); } }
Флаги - очень мощный инструмент, не забывайте о них!
Видео
Полезные страницы
- Набор GyverKIT – большой стартовый набор Arduino моей разработки, продаётся в России
- Каталог ссылок на дешёвые Ардуины, датчики, модули и прочие железки с AliExpress у проверенных продавцов
- Подборка библиотек для Arduino, самых интересных и полезных, официальных и не очень
- Полная документация по языку Ардуино, все встроенные функции и макросы, все доступные типы данных
- Сборник полезных алгоритмов для написания скетчей: структура кода, таймеры, фильтры, парсинг данных
- Видео уроки по программированию Arduino с канала “Заметки Ардуинщика” – одни из самых подробных в рунете
- Поддержать автора за работу над уроками
- Обратная связь – сообщить об ошибке в уроке или предложить дополнение по тексту ([email protected])