Плавное мигание светодиода с изменяемым вручную интервалом мигания. Помогите с кодом.
- Войдите на сайт для отправки комментариев
Приветсвую. Есть задача, которую я как-то не могу сообразить как решить.
Исходное - ардуино нано, светодиодик, кнопка.
Задача. 2 раза кликнуть кнопкой, чтобы посчиталось время между нажатиями, и потом плавно мигать светодиодом, так, чтобы время цикла мигания было равено времени между кликами кнопки.
Поехали:
Кнопка - измеряет время между двумя нажатиями, и кладет значение в переменную.
void button_work(){ if (digitalRead(button) == HIGH && button_flag ==0) { time_counter = millis(); button_flag = 1; } if (digitalRead(button) == LOW && button_flag == 1){ button_flag = 2; } if (digitalRead(button) == HIGH && button_flag ==2) { int current_millis = millis(); interval = current_millis - time_counter; button_flag = 3; } if (digitalRead(button) == LOW && button_flag == 3){ button_flag = 0; } }
Поскольку мне нужно замерять время всегда строго между двумя нажатиями - для этого такая несколько дурацкая конструкция if..else. Но - тем не менее она работает, и переменная interval получает необходимое значение в милисекундах.
Вторая часть - светодиод. Он должен мигать плавно (для этого подключен к выводу PWM), цикл мигания - 0-255-0. Функция должна брать значение переменной interval, и в зависимости от ее значения, длительность цикла мигания (0-255-0) должна быть равна длительности в мс в значении переменной interval. т.е. если у нас значение переменной interval = 1000 - то цикл мигания светодиодиком должен составлять 1с (1000мс). если интервал был посчитан больше или меньше - соответсвенно цикл мигания становится либо короче либо длиннее. Попрбоовал сначала написать 2 цикла for для мигания, основываясь на родном примере.
void led_fade(){ int timeliess = interval/100; for (int brightness = 0 ; brightness <= 255; brightness += 5) { // sets the value (range from 0 to 255): analogWrite(led, brightness); // wait some time to see the dimming effect delay(timeless); } // fade out from max to min in increments of 5 points: for (int brightness = 255 ; brightness >= 0; brightness -= 5) { // sets the value (range from 0 to 255): analogWrite(led, brightness); // wait some tim to see the dimming effect delay(timeless) } }
И в зависимости от того, какое значение переменной interval я укажу вверху кода (там где описываются все глобальные переменные), я получаю корректное мигание диодика.
Но за счет использования цикла for и функций delay() - я не имею возможности "нащелкать" нужный интервал с помощью кнопки (в главном лупе стоят 2, выше описаных функции, одна за другой). т.к. когда играет функция с светодиодом - на кнопку нет реакции, и у меня есть только очень короткое время на нажатие кнопки, до того как запустится функция с миганием. соотвесвтенно нужно избавится от циклов (for) и от делеев. А я чот не соображу как..
Заранее спасибо за помощь.
UPD: bwn любезно предложил идею
"Идея такая:
void cikl() { static unsigned long prevTime; prevTime = millis(); while(flag) { if (millis()-prevTime > interval) { flag = 0; } if (digitalRead(knop) == LOW) { flag = 0; delay(500); } //Здесь можете вызывать еще какие нибудь функции } }
flag и interval глобальные переменные. Чтобы пошел цикл надо сделать флаг в 1 и присвоить значение интервалу. Кнопка на пине через внутренний резистор.
при каждом проходе loop вызываете функцию cikl(), пока флаг равен 0 тут же из нее возвращаетесь. Как только флаг стал 1 (где это произойдет, вам решать), начал работать цикл while и работает до тех пор, пока не достигнет интервала или не нажмется кнопка. Из while можете вызывать функцию вашего светодиода (там по аналогичному таймеру делаете требуемое приращение уровня), в ней for и бесконечных циклов быть не должно. Посчитали, записали новое значение, конец функции. И этот круговорот будет происходить, пока флаг не станет 0.
Но я не могу понять как предложение bwn связать с моим кодом....
мигать диодиком нужно постоянно. только иногда (в зависимости от времени между двумя нажатиями кнопки) нужно изменять время мигания по длительности. В моменты нажатий - можно не мигать. Т.е. внешне это выглядит так - включаем ардуину - цикл мигания получает какое-то базовое значение interval и диодик начинает мигать. Хотим поменять длительность цикла мигания - нажимаем на кнопку (перестаем мигать, начинаем считать мс), и нажимаем 2ой раз на кнопку. Теперь диодик начинает мигает с интервалом времени, полученным от нажатий кнопки.
Я вам показал простой принцип, а не писал требуемый код.
Имеем задачи:
Отловить нажатие кнопки
Отловить отпускание кнопки
Измерить время. Это как я понял вы решили.
Изначально писал исходя из того, что по истечении интервала мигание останавливаем. У вас оказаывается иначе.
Значит: 1. Нужна функция фиксирующая время нажатия, расчитывающая приращение и интервалы между приращениями.
2. Функция отрабатывающая общий интервал, контролирующая кнопку и вызывающая функцию светодиода.
3. Функция светодиода контролирующая интервалы между приращениями, изменяющая переменную на значение приращения и записывающая это значение в порт (+контролировать граничные значения).
Получается: loop долбит беспрерывно функцию кнопки - поймали HIGH, подняли флаг, начали счет. Поймали LOW и поднятый флаг, сбросили флаг, посчитали общее время, рассчитали приращение и интервал между приращениями, занесли в переменные.
loop долбит непрерывно функцию светодиода, проверяем интервал между приращениями, по достижении увеличиваем переменную, заносим значение в порт. Достигнув максимального значения начинаем уменьшение до минимального, достигли минимального, начали увеличивать. Понадобится флаг операции - увеличение или уменьшение.
Будет получаться небольшая погрешность. Если недопустимо, то еще отсчитывать общий интервал и обнулять переменную яркости. Как то так. Все реализуется на if, millis и i++
Кнопки, если их вынести отдельно - отлавливаются в loop на ура. все считает и прочее.
Точно так же функция мигания - тоже отдельно работает как надо.
Проблема, что когда они в одном лупе - то это выглядит так - "cпрашиваем кнопки"-"Мигаем"-"cпрашиваем кнопки"-"Мигаем"... до бесконечности. Только время отведенное на опрос кнопки составляет 0,01с, а функция мигания (к примеру) 1с. И когда играет функция мигания - я не могу начать нажимать кнопки, т.к. они просто не реагируют.
Хорошо получается когда я ввожу еще одну кнопку - и тогда делаю обычное условие -
if (кнопка 2 == HIGH и новый_флаг ==0{
новый_флаг = 1;
button_work((в ней после второго нажатия сбрасываем новый_флаг в 0));
esle {led_fade()}
Но тогда нужно перед тем как начинать считать время нажимать кнопку2, и все равно - когда играет led_fade() - не вклинишься никак...
Как мне сделать так, чтобы первое нажатие кнопки отлавливало независимо от состояния лупа led_fade()? для этого хорошо бы убрать в принципе for и delay() из кода. Но тогда - как мне мигать плавно(!) светодиодом в заданном промежутке времени?
Вы меня похоже не поняли. For-ов и delay в коде быть не должно. Поймали HIGH запустили счетчик и подняли флаг. Флаг поднят, пока не поймали LOW. Здесь вычислили интервал, рассчитали ваши приращения и интервалы между приращениями (этими значениями будем пользоваться для светодиода), флаг сбросили и ждем следующего HIGH. За это время loop вызовет вашу функцию кнопок миллион раз.
Аналогично вызывается функция светодиода, в нее передаем наши значения интервала и приращения яркости. Как только интервал достигнут, приравняли prevTime миллису и изменили яркость светодиода и так по кругу. Задержка составляет - время проверки условий и пара операций присвоения. Ни о каких секундах речь не идет. Микросекунды. Почитайте блинк без делей
Вот здесь у Лешака мне больше нравится.
Там кстати дальше и приемы для вашего случая описаны.
Efremoff, напиши этот код
без for и без delay, примерно так:
По кнопкам:
if (but == HIGH && flag == 0) {
flag = 1;
timeCounter = millis()
}
if (but == LOW && flag ==1) {
flag = 0;
interval = millis() - timeCounter;
Здесь формула расчета интервалов и приращений.
}
Если воспользуетесь тем, как предложил Andy, то получится, timeless = interval/51 (фиксированное приращение 5 единиц)
Спасибо всем. Нажал-начал считать, отпустил-закончил считать - не подходит. должно быть 2 отдельных нажатия.
попробовал намудрить такой вот код (целиком все):
очень похоже что работает правильно. еще нужно потестить.
И интерсный вариант с кейсами - тоже попробую, может будет лучше.
при тестировании такая проблема - 3-5 установкок интервала отрабатывает ок. а вот потом - диод просто начинает светить непрерывно, не реагируя на нажатия в принципе.
Честно говоря, не очень понятно, как вы видите работу кнопки? Что значит два нажатия? Что произойдет, если второй раз забыли нажать?
второй раз не забудут.
есть всегда 2 последовательных нажатия, которые отмеряют интервал. Если второй раз нажать "забыли" - то лед будет мигать с предыдущим значением интервала до бесконечности. А когда все-таки второй раз нажмут - то прост будет огромный интервал, и лед мигать будет, просто очень и очень медленно. это ожидаемое поведение.
вышенаписаный мой код работает - нажатия отлавливаются, интервал задается, лед начинает мигать с нужным интервалом.
Минус - по истечении какого-то времени происходит переполнение какой-то из переменных, и значение brightness всегда становится 254-255. По крайней мере так рисует монитор порта.
Кажется я нашел. в функции кнопок во втором нажатии с millis(); работает переменная, которой я по ошибке назначил тип int. и соответсвенно через ~32 секунды она переполняется.
финальный код:
работает корректно.
Всем спасибо за идеи и направления.
Честно говоря, led_fade все таки сделал бы как у Andy. Формула и короче и понятней получается, ну да дело ваше.
bwn, я попробовал как у Andy - не заработало. видать либо я не так переменные внутри обозначил, либо что-то не так с самим кодом.
Для кода, который у вас разницы особо не какой (если только формула верная), если будет большой и нагруженный код, то у Andy отрабатывается только по совпадению условия, иначе сразу возврат. У вас отрабатывается всегда в полном объеме и использует операции с плавающей точкой. Это весьма затратно по времени, но не в данном случае.
И еще смущает int в 26 строке, что он там делает? Эта переменная не должна превышать byte.
Да, Byte там будет куда логичнее.
Готовое устройство в данном случае очень простое. даже как-то черезчур было использовать для этого целую ардуину (можно было обойтись каким-то голым контроллером). Но - ардуинки нано в запасе есть, стоят дешево (в китае), так что почему бы не попробовать. тем более что получилось вполне нормально.
Скорее всего добавится еще одна кнопка с фиксацией которая будет переключать HIGH-LOW между двуся свободными входами но это уже вопрос условия if-else. Т.е. нажимаем кнопку с фиксацией (которая переключает HIGH c входа напр. 5 на вход 6 - который становится LOW) - и тогда запускаем выше описаный луп с кнопками и миганием. Если же вход 5 становится LOW а вход 6 становится HIGH - то я уже хочу совершенно другие функции запускать. Но это уже совсем другая история, и думаю этими несколькими функциями все и ограничется в данном проекте.
Специально проверил, все работает:
Попробую. видать я что-то с переменными напутал.
ТС, а что этим вычисляется?
brightness = 128+127*cos(2*PI/interval*time_count);
Значение для PWM порта, на основании данных полученных от переменной интервал, и текущего значения в мс. от 0 до 255.
Т.е. скорость заполнения порта от 0 до 255, исходя из значения переменной inteval и текущего значения мс. Чем значение переменной interval больше - тем медленнее происходит насчитывание значение переменной brightness.
Т.е. каждый цикл лупа происходит изменение значения millis(). Соответсвенно каждый раз значение сos Будут менятся от -1 до 1. на это значение мы умножаем 127 (таким образом получаем значения от -127 до +127), к которому мы прибавляем 128 - и в сумме получаем постоянную пульсацию значения переменной от 0 до 255.
Т.е. каждый цикл лупа происходит изменение значения millis(). Соответсвенно каждый раз значение сos Будут менятся от -1 до 1. на это значение мы умножаем 127 (таким образом получаем значения от -127 до +127), к которому мы прибавляем 128 - и в сумме получаем постоянную пульсацию значения переменной от 0 до 255.
это как-то отличается от interval/256 ?
Да. кардинально. )
Да. кардинально. )
там линейная характеристика или специальное что-то для чего-то?