задержки в ардуино без delay

Работаем с задержками без delay() на arduino.

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

Мы рисовали общую схему нашего устройства, но сейчас посмотрим на элементы, с которыми мы будем работать в данном уроке. Собственно – их 5. – это зуммер, реле с 2 типами контактов, светодиод, показывающий состояние прохода и кнопка мгновенного действия. Подготовим для всех них понятные имена

#define ledAccess 8                      // Светодиод - разрешающий
#define ledDenied 9                      // Светодиод - запрещающий    
#define buttonOpen 10                    // Кнопка разблокировки
#define lock 11                          // Контакт замка
#define buzzer 12                        // Контакт динамика

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

bool lockType = 1;   // Тип замка - электромеханический = 1. Электромагнитный = 0

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

pinMode(buttonOpen, INPUT);   // контакт кнопки на вход с подтяжкой
digitalWrite(buttonOpen, HIGH);
pinMode(lock, OUTPUT);       // контакт замка на выход
pinMode(ledAccess, OUTPUT);  // разрешающий светодиод
pinMode(ledDenied, OUTPUT);  // запрещающий светодиод
pinMode(buzzer, OUTPUT);     // зуммер

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

Смотрите также:  Таймер на avr

К счастью, в ардуино уже есть для этого инструменты. Попытаюсь дать короткое объяснение, почему delay это плохо. Для этого отвлечёмся и представим абстрактную задачу, где один светодиод у нас моргает каждую 1 секунду, а другой, каждые 0,2 секунды. Возьмём обычный пример мигания через delay

void setup() 
{
  pinMode(8, OUTPUT);     // Пины 8 и 9 конфигурируем на выход
  pinMode(9, OUTPUT);
}

void loop()
{
  digitalWrite(8, HIGH);  // В этом блоке подаём 5в на pin8, ждём 1 секунду, снимаем напряжение, опять ждём 1 секунду
  delay(1000);                      
  digitalWrite(8, LOW);    
  delay(1000);                       
  digitalWrite(9, HIGH);   // В этом блоке подаём 5в на pin9, ждём 200мс, снимаем напряжение, опять ждём 200мс
  delay(200);                     
  digitalWrite(9, LOW);   
  delay(200);                      
}

Естественно, в данном примере, они мигают по очереди, потому-что пока выполняется delay, контроллер делает бесполезные операции в цикле. Можно конечно сделать мигание второго диода в прерывании, но опять же, использовать там delay не получится, чтобы выдержать 200мс.

Можно извратиться, и рассчитать задержки свечения светодиода, который мигает чаще, так, чтобы создать второму диоду нужные 1 секунду простоя и свечения.

void setup() 
{
  pinMode(8, OUTPUT);     //Пины 8 и 9 конфигурируем на выход
  pinMode(9, OUTPUT);
}

void loop() 
{
  digitalWrite(8, HIGH);
  digitalWrite(9, HIGH);  // набираем задержку 1000мс блоками из 200мс
  delay(200);             // первые 200мс
  digitalWrite(9, LOW);
  delay(200);             // уже 400мс
  digitalWrite(9, HIGH);
  delay(200);             // 600мс
  digitalWrite(9, LOW);
  delay(200);             // теперь 800мс
  digitalWrite(9, HIGH);
  delay(200);             // набрали 1000мс, можно погасить led8
  digitalWrite(8, LOW);
  digitalWrite(9, LOW);   //опять набираем задержку 1000мс блоками из 200мс
  delay(200);
  digitalWrite(9, HIGH);
  delay(200);             
  digitalWrite(9, LOW);
  delay(200);
  digitalWrite(9, HIGH);
  delay(200);
  digitalWrite(9, LOW);
  delay(200);
}

Всё работает как надо, но согласитесь, это конкретное извращение. Также, в такой код будет невероятно трудно вставить другие задачи, требующие многозадачности.

Смотрите также:  Переход от прототипа Arduino к устройству на примере игрального кубика

Как ещё можно реализовать задержку, чтобы в это время, можно было делать другой код? Самое лучшее решение – запустить условие проверки с некой переменной, которая постоянно увеличивает значение. В ардуино уже реализована такая переменная millis() , которая увеличивается каждую миллисекунду на 1. Её максимальное значение равно 4294967295мс, что приблизительно составляет почти 50 суток. Пока не будем думать, о том, что она может переполнится (в этом ничего страшного нет), и посмотрим, как мы можем использовать эту переменную

unsigned long currentTime;         // Переменная текущего времени
if (millis() - currentTime > 1000) // Если время контроллера millis, больше переменной на 1000, то запускаем условие if
{ 
    currentTime = millis();        // Приравниваем переменную текущего времени к времени контроллера, чтобы через 1000мс опять сработал наш цикл.
    //здесь любое действие.
}

Всё просто! Мы используем вспомогательную переменную текущего времени currentTime, На старте её значение равно = 0, как и millis() = 0. Но millis() у нас постоянно увеличивается, а currentTime – нет. Поэтому через 1000мс, сработает условие if (millis() – currentTime > 1000), т.е. мы создали задержку в 1с, но код при этом выполнялся циклично в loop, и мы могли успеть обработать много периферии. Потом мы просто обновляем нашу переменную currentTime, и она снова ждёт сравнения с millis(), когда она вырастет на 1000мс. Попробуем сделать вышестоящую задачу, мигание двумя светодиодами с разной задержкой. но при этом вставим в loop какой нибудь код, пусть будет отправка в терминал, и проверим, как это работает. Я сделал для наглядности вторую переменную currentTime2, для второго светодиода.

unsigned long currentTime;   // Переменная времени диода 8
unsigned long currentTime2;  // Переменная времени диода 9
bool ledState=0;             // Переменная состояния диода 8
bool ledState2=0;            // Переменная состояния диода 9
unsigned long a;             // Переменная для проверки работоспособности кода в условиях задержки

void setup() 
{
  Serial.begin(9600);        // Конфигурируем серийный порт
  pinMode(8, OUTPUT);        //Пины 8 и 9 конфигурируем на выход
  pinMode(9, OUTPUT);
}

void loop() 
{
  if (millis() - currentTime > 1000)   // Проверяем время для первого диода (1000мс)
  {
    currentTime = millis();
    ledState=!ledState;                // Меняем состояние первого диода на противоположное
    digitalWrite(8, ledState);
  }
  if (millis() - currentTime2 > 200)  // Проверяем время для второго диода (200мс)
  {
    currentTime2 = millis();
    ledState2=!ledState2;    // Меняем состояние второго диода на противоположное
    digitalWrite(9, ledState2);
  }   
  a++;
  Serial.println (a);    
}

Для состояний светодиодов, возьмём переменную bool ledState и ledState2. Мы задали её тип как boolean, а это значит что она может принимать два значения – true и false, что в принципе равно 0 и 1. Чтобы не писать по несколько раз HIGH, LOW, мы подставляем туда состояние светодиода ledState. А меняем его каждый раз логическим отрицанием (!) на противоположное значение.

ledState=!ledState;

В конце цикла loop, впишем команду инкрементирования переменной a++, и отправки значения в серийный порт, чтобы отследить, будет ли выполняться этот код в момент ожидания задержек.

  a++;
  Serial.println (a);

Смотрим, что получилось

Смотрите также:  Дисплеи ST7735 и ST7789. Подключаем к ESP8266, выводим изображение без SD карты.

Видно, что теперь у нас и светодиоды моргают с заданными задержками (1000мс и 200мс) и в серийный порт с постоянством отправляется новая переменная. Отлично – теперь мы можем получить какие-то задатки многозадачности, и продолжить писать код для нашего устройства в следующей статье.

4 комментария к “Работаем с задержками без delay() на arduino.”

    1. Ширина импульса меняется значением, указанным в сравнении if (millis() – currentTime2 > 200), в данном случае 200, или в чём заключается вопрос?

  1. Я так понял человек хотел регулировать длительность импульса независимо от частоты его повторения, мне тоже интересно это, вы можете подсказать как правильно сделать? Хочу сделать генератор импульсов с регулируемой частотой от 1 до 100гц и регулируемой длительностью импульса от 0.1 до 20мс.

  2. Николай

    А как сделать , что бы можно было менять время горения и время не горения каждого светодиода. Например первый сетодиод горит 200мс не горит 1000мс. второй светодиод гоит 1000мс не горит 5000мс.

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *