Можно ли обойтись без delay ()?
- Войдите на сайт для отправки комментариев
Попытался немного усложнить простой пример мигания светодиода без delay(). Интересно, как сделать, чтобы светодиод не только мигал, но и отсчитывал нужное количество раз между длинными паузами.
То есть например при одном условии мигнул 2 раза, при другом - например 5. Я думаю, что нужно создать функцию и просто передавать ей это число. То есть как-то так:
blink_count(5); // количество миганий 5
а сама функция, я думаю, должна выглядеть так:
void blinking_count(byte counter) { while (counter > 0) { blink_led(); // это обычное мигание с короткой паузой counter--; } }
а вот дальше должна идти пауза (например 1 секунда). Если дописать delay (1000); это конечно просто, а вот как сделать это через millis не используя delay вообще? То есть получается нужно отсчитывать два временных интервала. Я назвал их так
long long_interval = 1000; // интервал между циклами миганий long short_interval = 100; // короткий интервал между вкл./выкл. светодиода;
С коротким интервалом всё ясно, я могу использовать для него фактически готовую функцию? А как прикрутить ещё длинную паузу без delay? Кто разбирается, подскажите пожалуйста! Мой вариант кода
void blink_led (); // функция мигания (предварительное объявление) const int ledPin = 13; // номер выхода, подключенного к светодиоду //переменные int ledState = LOW; // этой переменной устанавливаем состояние светодиода long previousMillis = 0; // храним время последнего переключения светодиода long prevMill = 0; long long_interval = 1000; // интервал между циклами миганий long short_interval = 100; // короткий интервал между вкл./выкл. светодиода; void setup() { // задаем режим выхода для порта, подключенного к светодиоду pinMode(ledPin, OUTPUT); } void loop(){ blinking_count(4); // сколько раз мигнуть между длинной паузой } void blinking_count(byte counter) { while (counter > 0) { blink_led(); counter--; } long currMill = millis(); if(currMill - prevMill > long_interval) { prevMill = currMill; } } void blink_led () { unsigned long currentMillis = millis(); //проверяем не прошел ли нужный интервал, если прошел то if(currentMillis - previousMillis > short_interval) { // сохраняем время последнего переключения previousMillis = currentMillis; // если светодиод не горит, то зажигаем, и наоборот if (ledState == LOW) ledState = HIGH; else ledState = LOW; // устанавливаем состояния выхода, чтобы включить или выключить светодиод digitalWrite(ledPin, ledState); } }
к сожалению работает не так, как как хотелось бы. Подскажите пожалуйста, что я написал неправильно???
А Вы можете каждую строчку снабдить комментарием - что она дедлает? Только не в терминах "вычитает 1 из а", а в терминах поставленной задачи?
Сразу всё и поймёте.
Вот код с коментариями каждой строки функции:
Мне только непонятно, что использовать для перехода в начало функции лишь после того, как прошел интервал? Я пробовал оператор continue. Компилятор на него ругается!
Ну Вы же уже мигнули count раз. Нафига Вам вообще считать этот интервал и переходить потом в начало?
чтобы сделать длинную паузу после того, как светодиод быстро мигнул несколько раз! И вот я думал, что после того как цикл закончился (количество быстрых миганий отсчитано) сделать паузу, через отсчет интервала?
То есть буквально ЕСЛИ интервал прошел ТО ПРОДОЛЖИТЬ! То есть опять быстро помигать светодиодом.
Логично я так и пробовал писать!
IF (интервал прошел)
CONTINUE
А компилятор ругается на continue! Может я что-то не так написал? Подскажите пожалуйста!
Может и не так. Я не понимаю, что Вы хотите написать. Попробуйте объяснить внятно, чтобы кто-то кроме Вас понял (хотя я уверен, что настолько, насколько это надо для программирования, Вы и сами не понимаете).
если описать алгоритм то он будет таким:
включить светодиод;
пауза 100 миллисекунд;
выключить светодиод;
Это одно короткое мигание.
Я пытаюсь написал функцию, которая принимает в качестве параметра количество коротких миганий светодиода и делает (точнее должна делать) после них длительную паузу (1 секунда)! То есть, чтобы по количесву этих коротких миганий светодиода (их подсчету) можно было определить например режим работы устройства, если в нём всего 1 светодиод!
например в одном режиме работы устройство мигает светодиодом 2 раза, в другом 3 или 5 и т.д.
junior_develope, у вас ошибка в понимании работы данного устройства . Функций должно быть две. 1-я это отдача команды-"мигнуть столько импульсов" . И 2-я функция для выполнения задания. Ее надо закидывать в loop или вешать в обрабртчика таймера прерывания. А иначе ну никак.
ПС: я бы решал через цифровой автомат.
при неблокирующем подходе, то есть при таком программировании, при котором система не погибает в ожидании завершение какого либо действия (то есть без delay() ) следует немного иначе смотреть на код.
Вместо фунции совершающей законченное действие, мы сосздаем функцию делающюу нужный шаг в нужное время и завершающуюся на этом.
Если Вы учили иностранный язык, то ту аналогия со временами - мы заменяем перфект на континуус ;).
Цикл loop() мы при этом должны сделать максимальнго быстрым, при каждом вызове loop() перебирая наши неблокирующие "шаги".
В случае такого мигания, ка вы хотите, нужно "шаг" функции описать так:
1. при первом вызове: обнулить счетчик фаз мигания, включить светодиод, запомнить время, назначить величину интервала ожидания. Первый вызов от не первого отличаем по значению параметра, например так: положительное значение - первый вызов, ноль в параметре - вызов очередного "шага".
2. если это не первый вызов, то посмотреть на часы (то есть вызвать millis()), проверить не прошел ли интервал ожидания окончания фазы, если не прошел - выход, если прошел - меняем фазу светодиода, запоминаем время и назначает величину интервала ожидания, а затем выход.
3. если значение фазы достигло окончания цикла миганий - то все обнуляем и выходим. Фазу разумно взять как удвоенное количество миганий, потому что свет бывает в двух состояниях - включен и выключен и три мигания это 6 состояний: вкл, выкл, вкл, выкл, вкл, конец.
----------------------
Итог: первым оператором в loop() у нас будет вызов функции "шага" - например ее можно назвать checkBlink(0). С нулем в параметре.
Если мы, в каком то месте программы, хотим моргнуть три раза то вызовем checkBlink(3).
Это просто рекомендации. С использованием классов С++ можно это сделать читабельнее и изящнее. Если понимать, что делаешь.
если описать алгоритм то он будет таким:
включить светодиод;
пауза 100 миллисекунд;
выключить светодиод;
Это одно короткое мигание.
Я пытаюсь написал функцию, которая принимает в качестве параметра количество коротких миганий светодиода и делает (точнее должна делать) после них длительную паузу (1 секунда)!
Ну, это как раз к тому же, о чём я говорил. Вы не до конца продумали чего хотите. Это не алгоритм - это разведение руками в стили "грабить корованы".
Ну, например, в каком именно месте Вашего "алгоритма" указана, что между "короткими миганиями" должна быть пауза? И какая там должна быть пауза?
Это только один вопрос - их больше.
Попробуйте таки строго расписать последовательность действий. Чтобы Вы могли выполнить её автоматом: делай раз, делай два. Пока у Вас не будет такого понимания - программа у Вас не получится никогда.
Вот код с delay();
Работает!
вот эту громоздкую штуку:
09
if
(ledState == LOW)
10
ledState = HIGH;
11
else
12
ledState = LOW;
Проще записать как
ledState = !ledState;
Я бы на Вашем месте функцию организовал бы по-иному. В функции бы организовал цикл мигания, который бы повторялся столько раз, какой аргумент был передан в функцию.
Вот код с delay();
Почитайте что wdrakula написал. Нужно изменить подход, уйти от последовательного выполнения команд и соответствующего написания кода. У вас что то постоянно должно вызываться / проверяться, а вот результатом вызова / проверки может быть включение / выключение, а может и не быть в зависимости от времени и предыдущего состояния системы.
Создал ещё одну переменную blink_enable, разрешающую или запрещающую мигание:
однако светодиод не мигает - он просто включается и горит! Впечатление такое, что цикл while неправильный! Ведь по идее он должен выполнятся и выполняться, пока условие истинно. То есть пока счетчик не обнулится
Верно? При вызове функции мигания в loop в неё передается число 4. Это значит, что светодиод должен мигнуть 2 раза (изменить состояние 4 раза). Однако светодиод просто включается и горит!
Вот вы попали в цикл, а выйдете из него когда ? Когда светодиод моргнет 4 раза ? За что боролись то ? :) Разбейте это все на фазы в case или if и сделайте так чтобы loop() этот код "насквозь" пролетал или, в случае функции, вызвали её и если время события не пришло (ни какого) или мигание выключено - сразу на выход.
junior_developer - вы так и не поняли, что вам обьяснил Дракула в посте #8. Ваша новая программа опять построена принципиально неверно. Вы входите в вашу функцию LED_blinking и "сидите" в ней, пока светодиод не мигнет положенного числа раз. Например, если вы решили мигнуть 4 раза с промужутком в 200мс - ваша функция зависит программу на 0.8сек. Пока вы используете такой подход, вы НИКОГДА не достигнете результата. Попробуйте еще раз перечитать то, что написал Дракула...
значит цикл WHILE здесь использовать невозможно в принципе? Нужно проверять условие только через IF?
значит цикл WHILE здесь использовать невозможно в принципе? Нужно проверять условие только через IF?
да. while тут явно ни к месту. Но и с If надо строить логику не так, как у вас - а как написано в сообщении 8. А вообще - вы явно не поняли принципа работы "блин без делей". Попробуйте сначала переписать в этом стиле просто свою функцию однократного мигания... - она у вас тоже с делеями написана.
Для проверки его можно использовать, но по смыслу получится IF. Цикл у нас уже есть - loop(), остальное будет определяться, например, blink_enable / previousMillis / номером фазы / millis()... в общем текущим состоянием системы.
Структура программы должна быть такой
Самое интересное дальше, в структуре LED_run(), но на форуме уже много раз встречалось.
Светодиод уже мигает. Теперь в коде нет никаких задержек, а только две проверки:
прошел ли интервал;
достиг ли счетчик значения;
Когда, достиг - длительность пауы должна поменяться с 100мс на 1с. Однако этого почему-то не происходит! Я специально добавил для отладки такие строчки
и через порт приходит
Видно, что переменная interval_delay не меняется совсем! Хотя она должна измениться по условию:
Вот код целиком:
Не могу понять, почему значение interval_delay не меняется. Как такое может быть?
Не могу понять, почему значение interval_delay не меняется. Как такое может быть?
Да нет, оно меняется. Вставьте вывод значения interval_delay на 16 строке - увидите. Просто при следующем же цикле loop() через несколько микросекунд - длинная задержка меняется обратно на короткую и вы не успеваете заментить изменения.
а как же сделать, чтобы это значение не пропадало? Подскажите пожалуйста!
Засунуть проверку counter внутрь
if
(currentMillis - previousMillis > interval_delay) {
а как же сделать, чтобы это значение не пропадало? Подскажите пожалуйста!
например, перенести строки с 13 по 18 внутрь условия If на строке 22
я добавил ещё одну переменную и получилось так:
Теперь работает! Наконец-то
ну, можно и так, хотя непонятно, зачем эта переменная? Что мешает прямо в строке 37 поменять величину интервала? Тогда строки с 13 по 18 и вашу новую переменную можно смело выкинуть из кода.
Теперь работает! Наконец-то
осталось избавится от
ledState = !ledState;
и вернуть старую конструкцию
if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;
а то как-то не солидно, на фоне остального раздувательства))))))
ну, можно и так, хотя непонятно, зачем эта переменная? Что мешает прямо в строке 37 поменять величину интервала? Тогда строки с 13 по 18 и вашу новую переменную можно смело выкинуть из кода.
Обратите внимание ещё на строку 26. там эта переменная обнуляется.
то есть она нужна для того, чтобы понять, прошла ли длинная пауза.
Если прошла, то перейти к коротким вкл./ выкл. светодиода и т.д.
прошла ли длинная пауза проверяется вот тут if(currentMillis - previousMillis > interval_delay) больше проверок не нужно
Советы как сделать дали в #24 и #25.
Ну главное если работает и все устраивает, то оставляйте. Помощь оказана и получена. Все довольны.
Сократил вот так:
Значения переменных увеличил в несколько раз, чтобы не мелькали при выводе через порт. Вместо 100 мс поставил 500, а вместо 1сек 3 сек. В итоге значение выводятся вроде правильно! А вот светодиоду, кажется нет до них никакого дела - он просто мигает, то есть длительной паузы нету! Хотя, если смотреть через порт - ёё значение есть:
Вот, кому интересно:
Очень странный глюк? Кто не верит, может загрузить код и убедиться сам!
Глюка нет. Строка 12 решает.
А теперь вопрос, что мешает сделать так как написали в #24 или #25?
Вы имеете в виду вот так?
А вот если запихнуть эту проверку сразу после
то есть переделать код так
то получается, что кототкий интервал вначале программы повторяется 4 раза, а затем постоянно только 3 раза!
Кто-нибудь знает, в чем может быть причина?
Кто-нибудь знает, в чем может быть причина?
В 19 и 20 строках установка длинного интервала и сброс в 0, и тут же в 24 ++.