Найти тему
Электроника, ESP32, Arduino

Почему delay тормозит Arduino и что с этим делать

Разберем на практическом примере почему использование оператора delay() в своих программах ну.... такое себе удовольствие....

Возьмем любую Arduino-подобную платформу, кнопку и светодиод с токоограничивающим резистором.

Схема макета на базе Arduino Nano
Схема макета на базе Arduino Nano

Попробуем решить простую параллельную задачу:

  • мигаем встроенным светодиодом 1 раз в секунду
  • при нажатой кнопке BT1 светодиод L1 светится, при отпущенной гаснет
Vfrtn
Vfrtn

Для мигания встроенным светодиодом возьмем код из примера, с которого обычно начинается обучение Arduino и добавим строчку которая будет зажигать светодиод L1 при нажатии на кнопку.

Код который не подходит для решения данной задачи
Код который не подходит для решения данной задачи

Операторы в цикле loop () выполняются последовательно, а поскольку оператор delay() останавливает выполнение программы на заданное в параметре функции количество миллисекунд строка 23 в которой происходит копирование состояния кнопки фактически будет опрашиваться один раз в две секунды. (нажав/отпустив кнопку придется ждать до 2-х секунд прежде чем программа сможет зажечь/погасить светодиод). Даже при сборке простейших часов такой подход будет не приемлем - пока на дисплее мигают точки поуправлять таким с помощью кнопок (например чтобы установить время) будет проблематично.

Классический пример решения данной задачи предлагает ресурс с официальной документацией по Arduino в статье Blink Without Delay и его всегда можно использовать его в качестве шаблона.

Один из классических вариантов решения задачи.
Один из классических вариантов решения задачи.

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

Мне столько кода для решения задачи моргания двоеточием в часах писать лень - попробуем воспользоваться фишками языка С и затолкнуть все в пару строк кода ;-)

Все действо уместилось в строчке
Все действо уместилось в строчке

Код который переключает светодиод 1 раз в секунду получился даже короче чем при использовании оператора delay() и не блокирует программу.

digitalWrite(ledBlinking, (millis() / 1000) % 2);

Как это работает?

Встроенная функция millis() возвращает значение равное количеству миллисекунд, прошедших с момента включения или перезагрузки Arduino.

Это число мы делим на 1000 (получается время в секундах)

Оператор % берёт остаток от деления на 2 этого промежуточного результата.

Четные числа на 2 делятся без остатка - то есть получается 0

Нечетные числа делятся на 2 с остатком 1

Таким образом, итоговое значение будет то 0, то 1, переключаясь каждую секунду. Это значение мы и записываем с помощью функции digitalWrite в цифровой выход 13 на котором находится встроенный светодиод.

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

Самодельный delay() издание улучшенное и дополненное
Самодельный delay() издание улучшенное и дополненное

Данный код переключает светодиод, и печатает в COM порт "1 SEС" каждую секунду при этом не блокируя опрос кнопки.

Пример работы программы
Пример работы программы

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

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

Arduino - прерывайся по таймеру правильно

Всем удачи!