задержки в ардуино без 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() в ардуино, чтобы во время ожидания, наш контроллер мог решать другие задачи. Нам это нужно будет, для управлением временем открытия замка, зажиганию диодов, подачи звука, и чтобы в это время мы могли решать другие задачи, такие как, опрос клавиатуры, кнопки выхода и считывателя.

К счастью, в ардуино уже есть для этого инструменты. Попытаюсь дать короткое объяснение, почему 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);
}

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

Как ещё можно реализовать задержку, чтобы в это время, можно было делать другой код? Самое лучшее решение – запустить условие проверки с некой переменной, которая постоянно увеличивает значение. В ардуино уже реализована такая переменная 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);

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

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


Комментарии

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

  1. Аватар пользователя Евгений
    Евгений

    Как ширину импульса регулировать в таком случае?

    1. Аватар пользователя lex232
      lex232

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

  2. Аватар пользователя Dmitriy
    Dmitriy

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

  3. Аватар пользователя Николай
    Николай

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

  4. Аватар пользователя Сергей
    Сергей

    Присоединяюсь к вопросу НИКОЛАЯ

Добавить комментарий