Arduino задержка без delay

Arduino delay millis и micros для организации задержки в скетче

Задержки в Ардуино играют очень большую роль. Без них не сможет работать даже самый простой пример Blink, который моргает светодиодом через заданный промежуток времени. Но большинство начинающих программистов мало знают о временных задержках и используют только Arduino delay, не зная побочных эффектов этой команды. В этой статье я подробно расскажу о временных функциях и особенностях их использования в среде разработки Arduino IDE.

Функции времени в Ардуино

В Arduino cуществует несколько различных команд, которые отвечают за работу со временем и паузы:

Они отличаются по точности и имеют свои особенности, которые стоит учитывать при написании кода.

Использование функции arduino delay

Ардуино delay является самой простой командой и её чаще всего используют новички. По сути она является задержкой, которая приостанавливает работу программы, на указанное в скобках число миллисекунд. (В одной секунде 1000 миллисекунд.) Максимальное значение может быть 4294967295 мс, что примерно ровняется 50 суткам. Давайте рассмотрим простой пример, наглядно показывающий работу этой команды.

В методе setup прописываем, что пин 13 будет использоваться, как выход. В основной части программы сначала на пин подается высокий сигнал, затем делаем задержку в 10 секунд. На это время программа как бы приостанавливается. Дальше подается низкий сигнал и опять задержка и все начинается сначала. В итоге мы получаем, что на пин поочередно подается, то 5 В, то 0.

Пример delay с миганием светодиодом

Пример схемы для иллюстрации работы функции delay.
Можно построить схему со светодиодом и резистором. Тогда у нас получится стандартный пример – мигание светодиодом. Для этого на пин, который мы обозначили как выходной, необходимо подключить светодиод плюсовым контактом. Свободную ногу светодиода через резистор приблизительно на 220 Ом (можно немного больше) подключаем на землю. Определить полярность можно, если посмотреть на его внутренности. Большая чашечка внутри соединена с минусом, а маленькая ножка с плюсом. Если ваш светодиод новый, то определить полярность можно по длине выводов: длинная ножка – плюс, короткая – минус.

Функция delayMicroseconds

Данная функция является полным аналогом delay за исключением того, что единицы измерения у нее не миллисекунды, а микросекунды (в 1 секунде – 1000000 микросекунд). Максимальное значение будет 16383, что равно 16 миллисекундам. Разрешение равно 4, то есть число будет всегда кратно четырем. Кусочек примера будет выглядеть следующим образом:

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

Функция millis вместо delay

Функция millis() позволит выполнить задержку без delay на ардуино, тем самым обойти недостатки предыдущих способов. Максимальное значение параметра millis такое же, как и у функции delay (4294967295мс или 50 суток). При переполнении значение просто сбрасывается в 0, не забывайте об этом.

С помощью millis мы не останавливаем выполнение всего скетча, а просто указываем, сколько времени ардуино должна просто “обходить” именно тот блок кода, который мы хотим приостановить. В отличие от delay millis сама по себе ничего не останавливает. Данная команда просто возвращает нам от встроенного таймера микроконтроллера количество миллисекунд, прошедших с момента запуска. При каждом вызове loop Мы сами измеряем время, прошедшее с последнего вызова нашего кода и если разница времени меньше желаемой паузы, то игнорируем код. Как только разница станет больше нужной паузы, мы выполняем код, получаем текущее время с помощью той же millis и запоминаем его – это время будет новой точкой отсчета. В следующем цикле отсчет уже будет от новой точки и мы опять будем игнорировать код, пока новая разница millis и нашего сохраненного прежде значения не достигнет вновь желаемой паузы.

Вот пример, наглядно иллюстрирующий работу команды:

Сначала мы вводим переменную timing, в ней будет храниться количество миллисекунд. По умолчанию значение переменной равно 0. В основной части программы проверяем условие: если количество миллисекунд с запуска микроконтроллера минус число, записанное в переменную timing больше, чем 10000, то выполняется действие по выводу сообщения в монитор порта и в переменную записывается текущее значение времени. В результате работы программы каждые 10 секунд в монитор порта будет выводиться надпись 10 seconds. Данный способ позволяет моргать светодиодом без delay.

Функция micros вместо delay

Данная функция так же может выполнить задержку, не используя команду delay. Она работает точно так же, как и millis, но считает не миллисекунды, а микросекунды с разрешением в 4мкс. Её максимальное значение 4294967295 микросекунд или 70 минут. При переполнении значение просто сбрасывается в 0, не забывайте об этом.

Платформа Arduino предоставляет нам несколько способов выполнения задержки в своем проекте. С помощью delay вы можете быстро поставить на паузу выполнение скетча, но при этом заблокируете работу микроконтроллера. Использование команды millis позволяет обойтись в ардуино без delay, но для этого потребуется чуть больше программировать. Выбирайте лучший способ в зависимости от сложности вашего проекта. Как правило, в простых скетчах и при задержке меньше 10 секунд используют delay. Если логика работы сложнее и требуется большая задержка, то вместо delay лучше использовать millis.

Arduino задержка без delay

РАБОТАЕМ ПО ТАЙМЕРУ В ARDUINO

Я думаю все знают классический алгоритм создания таймера на millis() – счётчике аптайма:

Читать еще:  Philips bass sound she3590

Классический таймер на millis()

Несколько таймеров

Данный алгоритм позволяет спокойно переходить через переполнение millis() без потери периода, но имеет один большой минус – время считается с момента последнего вызова таймера, и при наличии задержек в коде таймер будет накапливать погрешность, отклоняясь от “ритма”. Недавно я придумал более точный алгоритм таймера на миллис, который соблюдает свой период даже после пропуска хода!

Улучшенный таймер

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

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

В этой библиотеке реализован полноценный таймер на счётчике аптайма, позволяющий даже “приостановить” счёт (не приостанавливая сам аптайм).

Примечание: таких таймеров можно создать сколько душе угодно (пока хватит памяти), что позволяет создавать сложные программы с кучей подзадач, но для функционирования данного таймера нужен “чистый” loop с минимальным количеством задержек, или вообще без них. Всё таки таймер “опрашивается” в ручном режиме. Таймер, который не боится задержек, делается на прерывании таймера, смотрите вот эту библиотеку.

БИБЛИОТЕКА GYVERTIMER

GyverTimer v3.2

GTimer – полноценный таймер на базе системных millis() / micros(), обеспечивающий удобную мультизадачность и работу с временем, используя всего одно родное прерывание таймера (Timer 0)

  • Миллисекундный и микросекундный таймер
  • Два режима работы:
    • Режим интервала: таймер “срабатывает” каждый заданный интервал времени
    • Режим таймаута: таймер “срабатывает” один раз по истечении времени (до следующего перезапуска)
  • Служебные функции:
    • Старт
    • Стоп
    • Сброс
    • Продолжить

Поддерживаемые платформы: все Arduino (используются стандартные Wiring-функции)

ДОКУМЕНТАЦИЯ

Документация

Конструктор

Класс GTimer позволяет работать как с миллисекундным, так и с микросекундным таймером. В общем виде пример выглядит так:

Где type это MS (,мс, миллисекундный таймер) или US (мкс, микросекундный), period – период в мс или мкс соответственно.

Настройки по умолчанию

  • При создании таймера можно ничего не указывать : GTimer myTimer; , тогда таймер будет сконфигурирован как миллисекундный и не запустится
  • Если указать только тип таймера (MS/US) GTimer myTimer(MS); , таймер настроится на выбранный режим (мс/мкс) и не запустится
  • Если указать тип таймера и интервал GTimer myTimer(US, 5000); , таймер настроится на выбранный режим (мс/мкс) и запустится в режиме интервала

Режимы работы

Таймер может работать в режиме интервалов и в режиме таймаута:

  • Интервалы. Запуск – метод setInterval(время) с указанием времени. В режиме интервалов таймер срабатывает (метод isReady() возвращает true) каждый раз при достижении указанного периода и автоматически перезапускается. Удобно для периодических действий
  • Таймаут. Запуск – метод setTimeout(время) с указанием времени. В режиме таймаута таймер срабатывает (метод isReady() возвращает true) только один раз при достижении указанного периода и автоматически отключается. Для повторного запуска нужно вызвать .setTimeout() с указанием периода, или просто .start() – запустит таймер на новый круг с прежним периодом

Управление таймером

Для управления состоянием таймера есть следующие методы:

  • start() – запускает (перезапускает) таймер с последним установленным временем
  • stop() – останавливает таймер
  • resume() – продолжает отсчёт таймера с момента остановки
  • reset() – сбрасывает таймер (отсчёт периода/таймаута начинается заново)
  • isEnabled() – возвращает true, если таймер работает (если он не stop() или не вышел таймаут)

Контроль доступа. Часть 2 — Работаем с задержками без delay() на arduino.

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

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

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

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

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

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

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

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

Читать еще:  Mc34063 понижающий преобразователь 12v на 5v

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

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

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

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

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

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

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

Очередная задержка без delay

  • Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии

Собрал на ардуине терморегулятор. Все работает как надо.

Вкратце алгоритм работы выглядит следующим образом

При повторной подаче сигнала на 8 вывод процедура должна повториться. Решил избавиться от delay. Для упрощения пытаюсь пока выполнить все это с помощью кнопки и светодиода — при нажатии кнопки светодиод загорается через определенный интервал, после отпускания сразу гаснет. Вот код

Но в ходе моделирования возникли проблемы

1. при повторном нажатии кнопки задержка отсутствует

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

Помогите допилить код.

  • Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии

Удалите 25 строчку.

  • Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии

Вероятно, проблема в переменной previousMillis. Если ее инициализировать при нажатии кнопки, то должно работать как надо. Например добавить флажок, определяющий состояние удержания кнопки. В начале, флаг = false. После нажатия, если флаг == false, то previousMillis = millis() и флаг = true. После отжатия, флаг = false.

Ответ на первый и второй вопрос, кроется в том, что Вы присваиваете «previousMillis = 0;» в строках 4 и 25, причем присвоение в строке 22 будет замещено присвоением в строке 25. Если после рестарта прошло более 20 секунд,то условие «if (currentMillis — previousMillis >= interval)» будет выполнятся с первой попытки («millis() — 0 >= 20000»).

  • Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии

Решил избавиться от delay.

Цель? Если всё работает как надо, то зачем избавляться? Какая разница будет оно стоять в цикле-delay или в цикле-loop? Или Вы просто поупражняться хотите?

  • Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии

Цель? Если всё работает как надо, то зачем избавляться? Какая разница будет оно стоять в цикле-delay или в цикле-loop? Или Вы просто поупражняться хотите?

хочу использовать сторожевого пса, а он, как вы понимаете, при таких задержках работает не совсем корректно. Да и во время delay информация на дисплее и поступающая от датчиков информация «замораживается». Ну и поупражняться лишним тоже никогда не будет.

  • Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии

Удалите 25 строчку.

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

upd: Пока пришла идея использовать следующий костыль — разбить один большой delay на несколько маленьких, а между ними вставить wdt_reset. Но из-за спортивного интереса все-таки хочу добить задержку без delay.

  • Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии

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

Не понимаю. В чём некорректность работы WatchDog и чем ему помешают delay?

Тоже не понимаю. Если Вы уберёте delay, но не будете опрашивать датчики и обновлять дисплей чаще, чем сейчас, она будет точно также «замораживаться».

Это я понимаю и считаю правильным! Удачи!

  • Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии

Не понимаю. В чём некорректность работы WatchDog и чем ему помешают delay?

Читать еще:  Kemppi minarc evo 200 цена

Максимальное время таймера wdt 8 секунд

если задержка больше этих 8 секунд, то в это время команда wdt_reset() соответственно не действует, МК думает что программа зависла и происходит перезагрузка системы.

  • Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии

Фу, ты, блин, думал и впрямь проблема :))) Ставите делай хоть на 4 секунды, дергаете ресет и снова на те же 4 — не вижу проблемы.

  • Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии

Ну пока так и сделал

  • Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии

Вместо if используйте while ( currentMillis — previousMillis static const

без комментариев — анализируем, делаем выводы.

  • Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии

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

Вот была тема по применению классов в одной концепции.

Хотя сейчас я пользуюсь этим.

  • Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии

Спасибо конечно что привели в пример, но подобным(только слегка более ООП образным) я пользуюсь всегда =)

есть Init есть run(на самом деле много больше(load reinit pause waitfor) и их может быть сколько угодно (имеется ввиду классов) простите под рукой ничего ненайду такого(все исходники на внешнем винте, а он остался в машине) но суть именно в том что каждый класс содержит некий минимум стандартных процедур аля Init,Run. и каждая вызывается при каждом проходе цикла Setup(init) loop(run). без задержек, просто если время для класса не наступило — он пропускает итерацию, и ждет следующей =)

Это позволяет в любом проекте оч быстро разобратся что за чем и для чего, плюс в любой момент времени можно повесится на обработчик данных, и добавив всего один класс, не переписывая половины проекта, допустим начать передавать данные на внешний контроллер, или собрать 2-3-4 проекта в один =)

Задержка в ARDUINO без delay(), с проверкой millis() на переполнение.

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

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

Описание библиотеки можно посмотреть здесь: Задержка в ARDUINO без delay()

Текущая версия: 0.0.2 / 2019.01.28

Изменения:
1. Тип возвращаемых данных методом getState() стал enum;
2. При вызове метода setTime() метод reset() выполняется сразу внутри него.

Timer.h

Скетч для проверки:

Навигация по записям

Оцените пожалуйста статью:

9 Replies to “ Задержка в ARDUINO без delay(), с проверкой millis() на переполнение. ”

Добрый день. Много искал библиотеки с таймерами. Ваша понравилась комментариями. Но программа не компилируется для Ардуино Нано. В чём может быть причина?

Плата:”Arduino Nano, ATmega328P”
Ошибка компиляции для платы Arduino Nano.

Радик здравствуйте! Укажите в комментарии лог компилятора. К сожалению ардуино нано нет под рукой и проверить не могу. На меге и уно все компилируется.

Добрый вечер. Ардуино нано уже использовал в другом проекте. Надеюсь на днях придёт ещё посылка с ардуинками, Тогда обязательно ещё раз всё проверю и отпишусь. Спасибо.

Arduino: 1.8.9 (Windows 10), Плата:”MH-ET LIVE Tiny88(16.0MHz)”

C:Program Files (x86)ArduinolibrariesTimerTimer.cpp: In constructor ‘Timer::Timer()’:

C:Program Files (x86)ArduinolibrariesTimerTimer.cpp:13:9: error: ‘timerState’ is not a class or namespace

C:Program Files (x86)ArduinolibrariesTimerTimer.cpp: In member function ‘void Timer::delay()’:

C:Program Files (x86)ArduinolibrariesTimerTimer.cpp:20:17: error: ‘timerState’ is not a class or namespace

C:Program Files (x86)ArduinolibrariesTimerTimer.cpp:22:81: error: ‘timerState’ is not a class or namespace

if(millis() + 4294967295 – _timeStore > _delayTimeBuffer)

C:Program Files (x86)ArduinolibrariesTimerTimer.cpp:25:68: error: ‘timerState’ is not a class or namespace

if(millis () > _timeStore + _delayTimeBuffer)

C:Program Files (x86)ArduinolibrariesTimerTimer.cpp: In member function ‘void Timer::start()’:

C:Program Files (x86)ArduinolibrariesTimerTimer.cpp:30:19: error: ‘timerState’ is not a class or namespace

if ((state == timerState::READY) || (state == timerState::PAUSED))<

C:Program Files (x86)ArduinolibrariesTimerTimer.cpp:30:51: error: ‘timerState’ is not a class or namespace

if ((state == timerState::READY) || (state == timerState::PAUSED))<

C:Program Files (x86)ArduinolibrariesTimerTimer.cpp:31:17: error: ‘timerState’ is not a class or namespace

C:Program Files (x86)ArduinolibrariesTimerTimer.cpp: In member function ‘void Timer::stop()’:

C:Program Files (x86)ArduinolibrariesTimerTimer.cpp:36:13: error: ‘timerState’ is not a class or namespace

C:Program Files (x86)ArduinolibrariesTimerTimer.cpp: In member function ‘void Timer::reset()’:

C:Program Files (x86)ArduinolibrariesTimerTimer.cpp:41:13: error: ‘timerState’ is not a class or namespace

exit status 1
Ошибка компиляции для платы MH-ET LIVE Tiny88(16.0MHz).

Этот отчёт будет иметь больше информации с
включенной опцией Файл -> Настройки ->
“Показать подробный вывод во время компиляции”

Евгений, выложите пожалуйста скетч который вы компилируете. Лог ошибок это хорошо, но можно увидеть весь сервиз?

Это компиляция примера который был в комплекте с библиотекой.

Arduino: 1.8.9 (Windows 10), Плата:”MH-ET LIVE Tiny88(16.0MHz)”

Изменены опции сборки, пересобираем все
C:Program Files (x86)ArduinolibrariesTimerexampleexample.ino: In function ‘void loop()’:

example:11:34: error: ‘Timer::timerState’ is not a class or namespace

if (timer.getState() == Timer::timerState::COUNTDOWN) Serial.print (F(“.”));

example:12:34: error: ‘Timer::timerState’ is not a class or namespace

if (timer.getState() == Timer::timerState::DELAY_OVER) <

exit status 1
‘Timer::timerState’ is not a class or namespace

Этот отчёт будет иметь больше информации с
включенной опцией Файл -> Настройки ->
“Показать подробный вывод во время компиляции”

Евгений, похоже по какой-то причине компилятор вместо типа данных ENUM распознает в переменной timerState класс или пространство имен, через оператор доступа “::”.

Интересно что для другого типа платы все ОК

Евгений, скачайте старую версию. Ссылка есть в описании. Там нет ENUMa.

Ссылка на основную публикацию
Adblock
detector