Почему не стоит всегда использовать функцию delay() в Arduino

Когда вы впервые воспользовались платой Arduino, вы, вероятно, сделали что-то вроде этого:

  • Подключили светодиод к Arduino

  • Загрузили стандартный скетч мигания (blink), который включал и выключал светодиод каждую секунду

Это называется программа «Hello World» для Arduino, и она показывает, что всего несколькими строками кода можно создать нечто, имеющее реальное практическое применение.

Скетч мигания светодиодом Arduino (blink sketch)

В приведённом выше примере вы используете функцию delay() для определения интервалов между включением и выключением светодиода.

Вот в чём дело: хотя delay() удобна и работает для простых примеров, в реальных проектах её использовать не стоит… Читайте дальше, чтобы узнать почему.

Как работает функция delay()

Функция Arduino delay() работает довольно просто.

Она принимает одно целое число в качестве аргумента. Это число представляет время в миллисекундах, которое программа должна подождать, прежде чем перейти к следующей строке кода.

Когда вы выполняете delay(1000), ваш Arduino останавливается на этой строке на 1 секунду.

delay() — это блокирующая функция. Блокирующие функции не позволяют программе делать что-либо ещё, пока конкретная задача не будет завершена. Если вам нужно, чтобы несколько задач выполнялись одновременно, вы просто не можете использовать delay().

Если ваше приложение требует постоянного чтения/сохранения данных с входов, следует избегать использования функции delay(). К счастью, есть решение.

Функция millis() спешит на помощь

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

Чем это полезно?

Потому что с помощью простой математики можно легко проверить, сколько времени прошло, не блокируя ваш код.

Скетч ниже показывает, как можно использовать функцию millis() для создания проекта мигания. Он включает светодиод на 1000 миллисекунд, а затем выключает его. Но делает это неблокирующим способом.

Давайте подробнее рассмотрим скетч мигания, который работает без функции delay:

/*
    Blink without Delay, example here: arduino.cc/en/Tutorial/BlinkWithoutDelay
*/

// constants won't change. Used here to set a pin number :
const int ledPin =  13;      // the number of the LED pin

// Variables will change :
int ledState = LOW;             // ledState used to set the LED

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated

// constants won't change :
const long interval = 1000;           // interval at which to blink (milliseconds)

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the
  // difference between the current time and last time you blinked
  // the LED is bigger than the interval at which you want to
  // blink the LED.
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
  }
}

Посмотреть исходный код

Этот скетч можно найти здесь, и он работает, вычитая предыдущее записанное время (previousMillis) из текущего времени (currentMillis). Если разность больше интервала (в данном случае 1000 миллисекунд), программа обновляет переменную previousMillis текущим временем и либо включает, либо выключает светодиод.

И поскольку это неблокирующий код, любой код, расположенный за пределами первого оператора if, будет работать нормально.

Теперь вы можете понять, что можно добавить другие задачи в функцию loop(), и ваш код по-прежнему будет мигать светодиодом каждую секунду.

Какую функцию использовать?

Мы изучили два различных способа работы со временем в Arduino. Использование функции millis() требует немного больше усилий по сравнению с использованием delay(). Но без неё ваши программы не смогут выполнять многозадачность на Arduino.