Уроки технологии
Технология Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Управление задержкой

В этом уроке вы узнаете, как правильно использовать функцию Delay(), чтобы добавить некоторую задержку между двумя действиями в ваших программах Arduino. Затем вы поймете, почему использование delay() часто не является хорошей идеей, когда вы хотите масштабировать свои программы, и как это исправить. Хорошо, начнем!

Оглавление

Зачем вам нужны задержки в ваших программах Arduino?

Что ж, работа программы Arduino во многом зависит от внешних входов/выходов. Возьмем простой пример: вы можете контролировать состояние кнопки 100 раз в секунду или заставить светодиод мигать каждые 0,5 секунды. Как же этого добиться? Программы Arduino работают следующим образом:

  • Во-первых, функция setup() выполняется один раз.
  • Затем функция loop() выполняется снова и снова, пока вы не выключите Arduino.

Любая программа, которую вы напишете, будет выполняться на полной скорости. Итак, если вы заставляете светодиод мигать в своем коде, произойдет следующее: светодиод будет мигать с очень высокой частотой (по крайней мере, несколько тысяч раз в секунду). Вы учитесь использовать Arduino для создания собственных проектов? *Ознакомьтесь с * Arduino для начинающих и учитесь шаг за шагом. Если вы хотите контролировать время, то есть следить за тем, чтобы светодиод мигал только каждые 0,5 секунды, а не на полной скорости, вам нужно добавить в код некоторую задержку.

Реализация задержки Arduino в вашем коде

Функция задержки() Arduino

Вот код, который заставляет светодиод мигать (здесь мы выбираем встроенный светодиод, нет необходимости добавлять внешний светодиод) каждые 0,5 секунды — это один из наиболее распространенных примеров, которые вы увидите, если начнете работать с Arduino.

#define LED_PIN 13 

void setup() 
    { 
    pinMode(LED_PIN, OUTPUT); 
    }

void loop()                                  
    {                                            
    digitalWrite(LED_PIN, HIGH);             
    delay(500);                              
    digitalWrite(LED_PIN, LOW);              
    delay(500);                              
    }

Итак, как работает этот код? Сначала вы используете «define», чтобы вам не приходилось несколько раз жестко кодировать число «13» в вашем коде, и это делает вашу программу более читабельной. Вы устанавливаете режим вывода на ВЫХОД с помощью функции pinMode().

Затем в функции loop() вы:

  • Включите светодиод.
  • Используйте delay(500), чтобы программа заснула на 500 миллисекунд или 0,5 секунды.
  • Выключите светодиод.
  • Используйте delay(500), чтобы программа снова заснула на 0,5 секунды.
  • И вернитесь к шагу 1, потому что функция цикла() продолжает выполняться снова и снова.

Функция delay() ожидает, что вы дадите несколько миллисекунд, а не секунд, на сон. Итак, если вы думаете секундами, то умножьте число на 1000 и вы получите нужное вам значение.

Сделать задержку Arduino на 1 минуту

Если вы хотите перевести Arduino в спящий режим на 1 минуту или на несколько минут, это довольно просто. Возьмите количество минут, умножьте его на 60, чтобы получить количество секунд, а затем умножьте его на 1000, чтобы получить количество миллисекунд. Пример: задержка (3 * 60 * 1000); заставит программу спать на 3 минуты.

Функция Arduino DelayMicroсекунды()

Если вам нужно быть более точным, возможно, вам придется использовать функцию delayMicroсекунды(). С delayMicroсекунды() вы можете указать количество микросекунд для сна. Минимальная продолжительность для delay() составляет 1 миллисекунду, поэтому, если вам нужно спать всего 400 микросекунд, у вас есть новое решение.

Эта функция может быть очень полезна при обмене данными с некоторыми аппаратными компонентами. Например, Arduino необходимо отправить некоторые данные компоненту, а затем прочитать ответ. Допустим, компоненту требуется 6 микросекунд для обработки запроса и доставки точных данных.

Тогда в вашей программе у вас может быть что-то вроде этого:

... 
void loop() 
{ 
    // send data to component 
    delayMicroseconds(6); 
    // reach out to component again to read data 
    }

Неблокирующая задержка – почему следует избегать использования задержки()

Итак, теперь, когда вы понимаете, что такое задержка в Arduino и как ее реализовать с помощью Delay() и delayMicroseconds(), давайте посмотрим, как использовать функцию задержки – но без этих функций. Почему? Когда вы используете функцию задержки(), выполнение вашей программы остановится и возобновится только после завершения задержки. Это нормально, если вам нужно выполнить только одно действие – например: мигнуть одним светодиодом – но что, если вам нужно выполнить уход за несколькими аппаратными компонентами или взаимодействие с другими устройствами, такими как платы Arduino или ваш собственный компьютер? Используя предыдущий пример, как можно заставить один светодиод мигать каждые 0,5 секунды, а другой — каждые 0,8 секунды? В этом случае вы довольно быстро застрянете. Если вы все еще хотите использовать delay()``, вы можете найти решение, в котором вы будете использовать более короткие задержки между несколькими действиями, но это будет каждый раз усложнять вашу программу. вы добавляете новое действие. Решение этой проблемы состоит в том, чтобы просто отслеживать время без использования функции delay()```, по сути, чтобы найти способ воспроизвести поведение задержки() без ее использования.** > > Посмотрите это видео в качестве дополнительного ресурса к этой статье:**

Пример кода — задержка Arduino без задержки()

Давайте перепишем наш пример мигания светодиода без использования задержки().

#define LED_PIN 13 
unsigned long lastTimeLedBlinked = millis();          
unsigned long delayBetweenBlink = 500;                   
byte ledState = LOW;                                     

void setup()
{ 
    pinMode(LED_PIN, OUTPUT); 
}

void loop()
{                                                        
    unsigned long timeNow = millis();                    
    if (timeNow - lastTimeLedBlinked > delayBetweenBlink)
        {                                                    
            if (ledState == LOW)                             
            {                                                
                ledState = HIGH;                             
            }                                                
            else                                             
            {                                                
                ledState = LOW;                              
            }                                                
            digitalWrite(LED_PIN, ledState);                 
            lastTimeLedBlinked = timeNow;                    
        }                                                    
}
#define LED_PIN 13 
unsigned long lastTimeLedBlinked = millis();
unsigned long delayBetweenBlink = 500;         
byte ledState = LOW;

Давайте проанализируем этот код построчно.

Инициализация

Сначала вы инициализируете 3 переменные в глобальной области видимости:

  • LastTimeLedBlinked: эта переменная будет использоваться для хранения времени последнего мигания светодиода. По сути, каждый раз, когда мы заставляем светодиод мигать, мы обновляем эту переменную текущим временем – используя функция millis().
  • DelayBetweenBlink: это время, в течение которого вы хотите подождать между двумя действиями – здесь действие заключается в мигании светодиода. -ledState: нам нужно будет сохранить текущее состояние светодиода (ВЫСОКИЙ или НИЗКИЙ), чтобы мы могли знать, каким было предыдущее состояние, и предпринять соответствующие действия.

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

Реализация функции задержки Arduino в функции цикла

Теперь давайте посмотрим, что происходит в функции цикла().

void loop() {
        unsigned long timeNow = millis();
        First, you read the current time with the
               millis() function.if (timeNow - lastTimeLedBlinked > delayBetweenBlink)
        {

И теперь вы сравниваете текущее время, которое вы только что прочитали, с предыдущим миганием светодиода. Если прошло достаточно времени (больше, чем значение, хранящееся в задержкеBetweenBlink), это означает, что вы можете ввести if(). Эта структура кода – считывание времени и сравнение его с предыдущим временем выполнения действия – позволяет заменить функцию delay(). По сути, ты просто вычисление продолжительности здесь. И, как вы можете видеть, это означает, что ваша программа содержит больше строк для простого приложения, но дает вам гораздо больше контроля и гораздо более масштабируемую. Итак, что же произойдет? Что ж, loop() будет продолжать выполняться на полной скорости. Каждый раз ваша программа будет проверять, прошло ли достаточно времени. Если нет, то loop()на этом заканчивается, потому что он не входит в структуру if. И когда пройдет достаточно времени, мы введем if().

// Executing the action

if (ledState == LOW) {
    ledState = HIGH; 
    } else {
        ledState = LOW; 
        } 
    digitalWrite(LED_PIN, ledState);

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

То, что мы делаем с этой глобальной переменной, просто: если она была LOW, мы устанавливаем ее в HIGH, а если она была HIGH, мы устанавливаем ее в LOW. И затем, конечно же, мы соответствующим образом обновляем физическое состояние светодиода с помощью digitalWrite().

LastTimeLedBlinked = timeNow;
     }
}

Наконец, и это очень важно, мы сохраняем текущее время, когда мы в последний раз мигали светодиодом. Если мы этого не сделаем, программа будет мигать светодиодом на полной скорости, потому что условие внутри if() всегда будет истинным. Установив предыдущее время на текущее, мы «сбрасываем таймер». Итак, в этом примере if() будет вводиться только каждые 500 миллисекунд или 0,5 секунды. И теперь это здорово, потому что ваша программа не останавливается, поэтому вы можете продолжать выполнять разные независимые действия, все еще «ожидая» мигания светодиода.

2 действия «одновременно»

Например, предположим, что вы хотите мигать одним светодиодом каждые 0,5 секунды, а другим — каждые 0,8 секунды.

#define LED_1_PIN 13
#define LED_2_PIN 10
unsigned long lastTimeLed1Blinked = millis();
unsigned long delayBetweenBlink1 = 500;
byte led1State = LOW;
unsigned long lastTimeLed2Blinked = millis();
unsigned long delayBetweenBlink2 = 800;
byte led2State = LOW;

void setup()
{
    pinMode(LED_1_PIN, OUTPUT);
    pinMode(LED_2_PIN, OUTPUT);
}
void loop()
{
    unsigned long timeNow = millis();
    // Action 1 - Blink LED 1
    if (timeNow - lastTimeLed1Blinked > delayBetweenBlink1)
    {
        if (led1State == LOW)
        {
            led1State = HIGH;
        }
        else
        {
            led1State = LOW;
        }
        digitalWrite(LED_1_PIN, led1State);
        lastTimeLed1Blinked = timeNow;
    }
    // Action 2 - Blink LED 2
    if (timeNow - lastTimeLed2Blinked > delayBetweenBlink2)
    {
        if (led2State == LOW)
        {
            led2State = HIGH;
        }
        else
        {
            led2State = LOW;
        }
        digitalWrite(LED_2_PIN, led2State);
        lastTimeLed2Blinked = timeNow;
    }
}

Как видите, мы повторяем структуру кода для второго добавленного нами действия. И оба действия не будут мешать друг другу!

Резюме

Если вы хотите сделать задержку Arduino без использования delay():

  • Создайте глобальную переменную для хранения времени, когда вы в последний раз выполняли определенное действие.
  • Создайте еще одну глобальную переменную для хранения желаемой продолжительности между двумя действиями.
  • В функции loop() прочтите текущее время с помощью millis(). — Сразу после этого используйте структуру if и условие (timeNow — LastTimeActionWasExecuted > DelayBetweenActions).
  • После того, как вы ввели if(), выполните действие.
  • И все же в if() сохраните текущее время как предыдущее. Вы можете повторить эти шаги для каждого действия, для которого вам нужна задержка Arduino.

Когда можно использовать задержку() и задержкумикросекунды()

Есть определенные случаи, когда использование delay() в вашем коде все еще допустимо. Вот 2 из них:

  • Вам необходимо инициализировать компонент во время установки вашей программы, и для инициализации этого компонента требуется некоторое время – например, 1,5 секунды. В этом случае, используя delay(1500) в вашем Функция setup() прекрасно работает. По сути, любая delay() в функции setup() вашей программы Arduino не будет проблемой.

  • Как объяснено в части delayMicroсекунды() , вам нужно подождать несколько микросекунд (не миллисекунд!) при обмене данными с внешним компонентом. Если вы обнаружите, что использование delayMicro Seconds() с небольшим числом (например, 10) не нарушает остальную часть вашей программы, вы все равно можете использовать его, не беспокоясь слишком сильно. Но считайте это исключением, а не общим правилом.

Вывод: используйте Arduino Delay() с осторожностью.

Из-за особенностей программ Arduino вам часто придется добавлять задержки в свой код, чтобы вы могли выбирать, когда выполнять те или иные действия и как часто вы хотите их выполнять. Функции delay() и delayMicroсекунды() являются очень простыми в использовании и были добавлены в язык Arduino, чтобы новички могли начать с чего-то простого. Однако, как вы видели в этом уроке, использование delay() может привести к тому, что вы очень быстро застрянете. Итак, лучше сначала понять, зачем это вам нужно, как это использовать, а затем как добиться того же поведения без прямого использования функции Arduino delay().

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