Странная и не логичная работа простейшей программки для управления светодиодами!!!
- Войдите на сайт для отправки комментариев
Вс, 16/12/2012 - 23:37
Уважаемые форумчане, помогите пожалуйста разобраться с простейшей программкой, которая по идее должна плавно и поочередно включать/выключать светодиоды подключенные к PWM выходам.
То есть вначале плавно зажегся и погас один светодиод, затем второй и т.д.
Я пробовал вот такой код (см. ниже) но он работает почему-то неправильно! Обьясните ПОЖАЛУЙСТА, что в нём не так!!!
int brightness = 0; // уставливаем начально значение яркости int fadeAmount = 5; // шаг приращения/убывания яркости void setup() { pinMode(9, OUTPUT); pinMode(10, OUTPUT); pinMode(11, OUTPUT); } void loop() { analogWrite(9, brightness); analogWrite(10, brightness); analogWrite(11, brightness); // измением значение в переменной для яркости brightness = brightness + fadeAmount; // при достижение крайних значений для яркости // меняем знак переменной шага приращения/убывания яркости if (brightness == 0 || brightness == 255) { fadeAmount = -fadeAmount ; } // делаем паузу для достижения плавного наращивания/убывания яркости delay(10); }
В этом примере все нормально и логично - все светодиоды синхронно разгораются и синхронно тухнут.
Вам нужен пример 3.Analog -> Faiding и на базе него написать для нескольких светодиодов.
Для каждого из светодиодов мне понадобится создать переменную, в которую будет заноситься текущее значение яркости? И вызывать эти переменные поочередно?
P.S.:Честно говоря, я не совсем понимаю, как это будет выглядеть, поскольку только начал осваивать С++, и что называется могу "заблудиться в трех соснах" Подскажите ПОЖАЛУЙСТА верное направление!
Попробуйте нарисовать диаграмму, как должны зажигаться светодиоды. В какой последовательности.
К примеру, если есть два светодиода, можно расписать алгоритм так:
1. Включить LED1
2. Ждать 500 мс
3. Погасить LED1
4. Включить LED2
5. Ждать 500 мс
6. Погасить LED2
7. Ждать 500 мс
8. Повторить с п.1.
Сложно? По этому алгоритму написать даже в тупом варианте не составит труда, подумайте.
Если светодиодов больше, соответственно будет больше пунктов.
UPD: Я расписал алгоритм простого включения/выключения, но переведите это на смену яркости. Т.е. меняйте яркость одного светодиода, затем второго, затем третьего и т.п. Если они у Вас смена яркости пересекается по времени, то на каждый светодиод - своя переменная.
Написал вот такую программку, но все равно светодиоды почему-то не зажигаются поочередно.
1.Вначале плавно зажигается первый светодиод
2.Затем он плавно гаснет и ПОЧЕМУ-то зажигается не следующий светодиод, а ВСЕ светодиоды!
Подскажите пожалуйста, что нужно изменить в коде, чтобы исправить эту ошибку !
Толи вы игнорируете сообщения #1 и #3, толи пример Fading так сложно найти...
В Arduino IDE меню File(Файл) -> Exemples(Примеры) -> 3.Analog -> Faiding - этот пример использует циклы, собственно говоря то что вам и нужно. Смотрите его и плодите для каждого светодиода:
Можно не плодить, а вынести в функцию, а в качестве аргумента принимать номер вывода светодиода:
Этот метод является блокирующим, тот способ, на который вы попытались "замахнуться" выше, круче, но вам пока еще рано.
Можно попробовать вот так:
& 0xFF; не дает выйти за пределы младшего байта, то есть все равно что объявить
byte
brightness1
.Большое спасибо, maksim за пример кода с готовой функцией регулировки яркости! К сожалению, я сам не додумался, как её написать и "прикрутить" к управлению несколькими светодиодами.
А я попробовал реализовать этот алгоритм через введение еще одной переменной, в которую заносится количиство переходов яркости (от минимума до максимума и обратно). Написал вот такой код, но к сожалению он почему-то не работает!
переменная brightness должна быть у каждого светодиода своя.
& 0xFF Это побитное умножение на "1111 1111" ? Какой в нём смысл, ведь на выходе получиться то же число, что и на входе? Не так ли? Объясните пожалуйста!
max_prev_br и вторая переменная нигде не сбрасываются. counter в главном цикле нельзя делать -2, скорее его ограничивать нужно там, где он увеличивается.
Ivan_vasilevich, это зависит от размера переменной, он может быть больше байта.
Какой в нём смысл,
[...]
kisoft, в общем то, уже ответил на этот вопрос.
Конкретнее, у меня переменные brightnessX были объявлены как integer, то есть занимающими в памяти 2 байта и хранящими значения в диапазоне [-32768...32767].
Инструкция
brightness3 = (brightness3 + fadeAmount) & 0xFF;
для них равнозначна инструкции
brightness3 = (brightness3 + fadeAmount) & 0x00FF;
В конкретной задаче смысл этой операции - ограничение диапазона изменения яркости: вместо [-32768...32767] получаем [0...255], что, как справедливо заметил maksim, можно реализовать, используя вместо int тип byte.
В практическом смысле так оно и есть. Но не в дидактическом. Ибо освоив на простом примере операции битовой арифметики, можно, немного изменив тип (взявши char), маску (взявши 0x7F), ну и логику вычисления яркости, написать-таки компактный код плавного загорания-затухания диодов.
IMHO тут, с минимальными поправками подойдет скетч из стартового поста. Просто нужно не все три диода одновременно крутить, а положить номер текущего диода в переменную и переключать ее. Вообщем вместо трех analogWrite нужен один.
А вот версия не ограниченная поличеством светиков и не требующая что-бы "они были рядом". Просто перечисляем сколько нам нужно в объявлении массива leds.
leshak, изящно, но ТС придумал неплохой алгоритм, для него было бы лучше допилить его в плане обучения, а потом взять Ваш. ;)
IMHO, разумеется
Так ведь и я взял за базу алгоритм ТС, только из стартового поста
А разобратся почему стартовый не заработал как нужно это не обучение? :)
А похорошему, если обучение, то нужно перебрать и заставить работать все варианты.
По поводу варианта из #8
Строка строка "counter=-2;" явно просит какого-то условия. Вот давайте в уме прокрутим первый же проход loop-а.
counter=1; Вызвали fadding, начали плавно разгоратся. Каутер не менялся. И тут - трах-бабах.counter =-2;
Теперь counter=-1, на следующем проходе -3 - что есть нонсенс. Явно уменьшать каунтер нужно только "в определенный момент", а не всегда.
Далее. Зачем на копипасты
? Как источник потенциальных ошибок? (кстати уже очепятнулись. LED2 - дважды, а LED3 - потеряли).
Раз у нас диоды по порядку, то можно сделать Fadding(LED1+counter-1, brightness); Без всяких if-и проч.
А если counter базировать на нуле, а не единцице, то Fadding(LED1+counter, brightness);
Next: крайне плохая идея использовать для глобальной переменной и локальной одно и тоже имя. Я про brightness. Это череповато часами проведенными в отладке в попытке понять "ну почему". Меняю переменную, а она не меняется :) Так что внутри функции можно ее до bright сократить. Тогда четко будете видеть что вы меняете.
Или подумать "а заче ее вообще передавать параметром" если она у нас одно и глобальная (counter- же мы не передаем).
Next: пинов и яркостей с номерами больше 255 - не бывает (как и отрицательных). Поэтому стоит заглянуть в http://arduino.ru/Reference Найти раздел "типы данных" и прочитать их все. И применять тот который "минимально подходит". В данном случае это не int, а byte
Или подумать "а заче ее вообще передавать параметром" если она у нас одно и глобальная (counter- же мы не передаем).
Извините за возможно глупый вопрос, но я не понял что значит "передавать параметром". Напишите ПОЖАЛУЙСТА!!! Если конечно Вас это не затруднит.
А если написать вот так (имею ввиду с закорючкой &), в чём будет разница? Объясните пожалуйста!
http://www.mir-koda.ru/full_leson_cpp.php?id=12
Большое спасибо! Ваш код действительно хорош - к сожалению, сам я не додумался, что номер текущего светодиода можно запихнуть в переменную! Но всё равно не получается заставить огонек бежать обратно, используя led--
По идее, огонек ведь должен побежать назад! Но почему-то он останавливается (LED3 мигает)! Не понимаю, почему? Объясните ПОЖАЛУЙСТА!
Самое быстрое - взять пример из сообщения #14, там где я пины запихнул в массив, и просто заполнитье его так:
По идее, огонек ведь должен побежать назад!
С какого испуга? led>11, выполняет при led==12. Сделали вы ему led--. Он стал опять 11. Проше свой цикл разгорания, стал 12, вы опять уменьшили на единицу, стал 11 :)
Кстати пример как сделать что-бы переменная led сначала увеличивалась, а потом уменьшалась (а не прыгала в ноль) - у вас перед глазами.
Ведь вы, фактически тоже самое делается с яркостью (brightness). Сначала увеличиваете до какого-то предела, потом уменьшаете в ноль. И опять заново.
Кстати пример как сделать что-бы переменная led сначала увеличивалась, а потом уменьшалась (а не прыгала в ноль) - у вас перед глазами.
Да, Вы абсолютно правы. Вот этот фрагмент кода:
По аналогии с этим я пробовал написать и так
и так:
Но всё равно ничего не получается! Пришлось создать ещё одну переменную ledAmount для того, чтобы можно было переворачивать её значение! В итоге программа получилась такой:
Только, что попробовал и удивился - наконец-то заработало! Но всё-таки мне интересно, можно ли было здесь обойтись без ввода ещё одной переменной? имею ввиду ledAmount
Но всё-таки мне интересно, можно ли было здесь обойтись без ввода ещё одной переменной? имею ввиду ledAmount
А почему бы и нет - вынесите все то, что сейчас в loop'е, в отдельную функцию, выбросив все лишнее (логику определения направления движения волны) и адаптировав оставшиеся переменные/константы под параметры функции, а в loop'е поставьте два последовательных вывода этой функции - один раз с прямым ходом, один раз с обратным. Это может выглядеть, например, так:
Здесь, как вы можете догадаться, первый параметр номер самого левого диода в волне, второй - номер самого правого, ну а третий - величина приращения номера.
Строго говоря, "от еще одной переменной" вы при этом не избавитесь, но зато ощутите ту пользу, которую может дать следование принципам структурного программирования - текст программы станет менее громоздким, более понятным, легче модифицируемым.
будет гораздо лучше чем более "экономный" вариант, без лишних переменных и вызова функций:
В первом случае беглым взглядом видно "где, что", да банально сама формула e=(m*v^2)/2 легко узнаваема любым текхническим экспертом который работает с вами и даже не знает програмирования. Теоретически ответ: можно попытатся извратится без переменной. Воспользоватся каким-то свойствами чисел, тем как они хранятся в памяти. Как-то заюзать переполнения, отрицательные числа и т.п. Но... это будут "чистые выебосы". Ког будет из серии "фиг поймешь" и экономии памяти/шагов все равно не будет (даже наоборот). Отказатся от переменной можно и поменяв сам алгоритм. Если мы возьмем подход не содержащий понятия "шаг приращения"/"направления". Собственно выше я как раз и дал пример - поместить номера led-дов в массив. И зажигать их по очереде. Если мы составим очередь 9,10,11,10, то визуально оно нам даст "туда и обратно" (за счет повторения 10-тки), хотя в реальности массив проходится только в одну сторону. В переменных мы тут "выиграли", а вот в памяти - нет (еще один байт на дополнительную десятку. а если пинов будет больше, то даже проиграем). Но, подход с хранением пинов в массиве, а бегание "индексом массива" - IMHO более кошерный в любом случае. Чем изменение самой переменной led. Уходит привязка к нумерации пинов. Код становится более переносим. Можно задавать не только линейное поведение. Да банально если у будет 20-ть пинов, то включить их на выход можно будет обыкновенным for, а не выписывать 20-ть pinMode Так что я рекомендовал бы вам, в учебных целях. Взять пример из #14 и допилить его до "туда-обратно", без дублирования 10-тки, в массиве leds.
P.S. Но вы молодец!!! Потому то что дочитали мою "простыню" до конца :) И "трижды молодец" что дожали свой пример.
Но всё-таки мне интересно, можно ли было здесь обойтись без ввода ещё одной переменной? имею ввиду ledAmount
А почему бы и нет - вынесите все то, что сейчас в loop'е, в отдельную функцию, выбросив все лишнее (логику определения направления движения волны) и адаптировав оставшиеся переменные/константы под параметры функции, а в loop'е поставьте два последовательных вывода этой функции - один раз с прямым ходом, один раз с обратным. Это может выглядеть, например, так:
Здесь, как вы можете догадаться, первый параметр номер самого левого диода в волне, второй - номер самого правого, ну а третий - величина приращения номера.
Это весьма неплохая идея! Оцените пожалуйста код, написанный на её основе!
Мне вот не нравится, что здесь в 20 и 29 строках Start_led + ledAmount
фактически повторяется! Можно ли избавиться от такой записи (имею ввиду используя тот де цикл for, что я и использовал)???
20 строка потрясает :)
Это точно тоже самое, только без "мишуры".
Позволю себе чуть модифицировать текст вашей функции:
Ну и
1) Крутить цикл по светодиодам с помощью for не получится - последний светодиод не будет отрабатываться (как только переменная цикла станет равной end_led, сразу же выполнится условие выхода из цикла). Можно было бы попытаться извратиться и придумать какое-либо более сложное условие окончания цикла, но зачем, если есть конструкция do-while - с проверкой после прохождения тела цикла? (не уверен, что синтаксически правильно употребил ее - редко приходится использовать)
2) brightnessamount должна быть объявлена глобальной переменной. А еще лучше - передана параметром
3) два цикла - продолжая путешествие в дебри структурного программирования - тоже просятся быть оформленными в виде функций
А вот версия не ограниченная поличеством светиков и не требующая что-бы "они были рядом". Просто перечисляем сколько нам нужно в объявлении массива leds.
Объясните пожалуйста, как работает в этом коде вот такая строка
Я лишь понял, что TOTAL присваивается результат от деления sizeof(leds)/sizeof(byte), а откуда берутся sizeof(leds) и sizeof(byte) ??? Напишите пожалуйста!
1) Крутить цикл по светодиодам с помощью for не получится - последний светодиод не будет отрабатываться (как только переменная цикла станет равной end_led, сразу же выполнится условие выхода из цикла).
Да, Вы совершенно правы, к сожалению я только-что это понял!
Можно было бы попытаться извратиться и придумать какое-либо более сложное условие окончания цикла, но зачем, если есть конструкция do-while - с проверкой после прохождения тела цикла?
Задать такое условие для выхода из этого цикла не так-то просто! Тут есть над чем покумекать! Операторы сравнения <= и => тут наверняка бесполезны, так как отсчет номеров светодиодов при вызовах функции
идет в разных направлениях ! Я умудрился прикрутить != Но,оказывается, если написать, что-то вроде
то последний по счету светодиод действительно не попадает в цикл!
Неужели действительно с помощью цикла for (его туда не запихнуть) и эта задача нерешаема? Пожалуйста напишите, кто знает!
Ну, извратиться все же можно. И разными способами.
Вот так:
Или (уже в теле функции и используя ваш стиль представления заголовков циклов) вот так:
Первый случай уж совсем изврат - необходимо будет каждому (потенциальному) пользователю функции втолковывать, почему это в качестве последнего светодиода надо указывать фиктивный. В качестве этого бестолкового пользователя можете выступить и вы сами - вернувшись месяцев этак через парочку к использованию этой функции в другом проекте.
Во втором случае вроде бы таких проблем нет, ибо все подготовительные работы происходят внутри функции, незаметно для глаз посторонних. Однако и здесь логика не совсем очевидна и при определенных условиях может добавить седых волос при устранении неожиданно возникших побочных эффектов.
Так что, по крайней мере для меня, самым логичным в этой ситуации будет использование цикла с постусловием (do ... while - преодолел таки свою лень и взглянул на синтаксис этой конструкции: в предыдущем моем посту она использована корректно). Тут, конечно, кое-что ручками придется сделать, зато никаких извратов и условие выхода из цикла кристально понятное и недвусмысленное.
Я лишь понял, что TOTAL присваивается результат от деления sizeof(leds)/sizeof(byte), а откуда берутся sizeof(leds) и sizeof(byte) ??? Напишите пожалуйста!
Не совсем верно.
Было бы неплохо загуглить что такое ключевое слово #define (искать скорее в справочниках по C/C++)
TOTAL это не переменная, а "определение". Под нее не выделяется память и проч.
Работает она примерно так: На этапе КОМПИЛЯЦИИ, а не выполнения, везде где попадется TOTAL, в тексте программы, он будет заменен на sizeof(leds)/sizeof(byte) и лиш потом будет произведена компиляция проги.
То есть елс ибы напишем код вида
То, с точки зрения компилятора это будет ИДЕНТИЧНО, если бы мы написали сразу digitalWrite(13,HIGH);
Под LED не будет выделятся память, для хранения переменной, при вызове функции не будут уходить такты на "прочитать переменную".
sizeof (опять-таки по хорошему нужно почитать справочник).
Ничто "ниоткуда" не берется. sizeof - встроенный в язык оператор. Который возвращает размер объекта или типа.
Когда я написал byte leds[] - я не указал компилятору какого размера этот массив. Сказал "смотри сам по тому сколько элементов я перечислю при инициализации. Он подсчитал и выделил соотсвествующие количество памяти.
Но мне самому нужно знать "сколько там элементов" (а пальцем считать не хочется). Поэтому я беру sizeof(leds) и он говорит мне, сколько же памяти компилятор выделил под массив leds (памяти!, а не "сколько там элементов").
Что бы узнать "сколько там элементов", мне нужно, теперь, количество выделенной памяти, поделить на "сколько памяти занимает один элемент". Про это мне доложит sizeof(byte) (массив-то байтов был. если бы был массив элементов другого типа, я бы писал sizeof(ТИП_ЭЛЕМЕНТА)
Ну, то есть теоретически деление на sizeof(byte) можн было-бы и опустить. Я и так знаю что байт занимает 1 байт :)
Но, это опять тот случай когда лучше в явном виде указать "что откуда берется", чем держать в памяти "что мы тут имели ввиду". Да и лучше привыкнуть использовать единообразную конструкцию вычисления размера массива вне зависимости от его типа, а не "для byte по одному вычислять, для int по другому" :)
А вообщем можно, спокойно написать
#define TOTAL 3
И не морочится, с sizeof. Но, тогда будет нужно не забыть подправлять его если мы меняем количество светиков в leds[]. Я предпочитаю возложить эту рутину на компилятор.
... зато никаких извратов и условие выхода из цикла кристально понятное и недвусмысленное.
Полностью согласен! Ваш вариант с циклом "do while" намного лучше! Только что попробовал его заюзать. Получился такой вот код:
Но компилятор "ругается", выделяя желтым цветом 32 строку: while(cur_led != End_Led); И выдает надпись:
Просмотрел текст программы уже несколько раз! Всё вроде бы логично. Не понимаю, о чём предупреждает компилятор, что ему не нравиться??? Пожалуйста подскажите!
brightnessamount не проинициализирована, последний светодиод по этому алгоритму работать не будет, об этом уже говорили. Почему ошибка - не вижу, мелкий шрифт на телефоне.
И удалите строку 3.
Да. Вы правы!
Да, действительно она здесь лишняя!
...последний светодиод по этому алгоритму работать не будет, об этом уже говорили.
Условие проверяется в конце цикла! http://arduino.ru/Reference/DoWhile
Подправил код, учитывая Ваши замечания. Получилось так:
Но компилятор всё равно ругается:
выделяет желтым цветом строку 31: while(cur_led != End_Led); И выдает надпись:
Это очень злобная ошибка. Серьезно.
while([ТУТ ВКРАЛИСЬ НЕВИДИМЫЕ СИМВОЛЫ]cur_led
Встанте так что-бы курсор мигал между c и u d слове cur_led. Нажимите один раз "стерлка влево", далее нажимайте несклько раз Backspace пока не сотрется круглая скобка. Наберите ее опять. Компилируйте.
Ну или, тупо, сотрите все эту строку и наберите заново.
Вот поправленный компилирующийся вариант. Глазами отличий не найдете, только HEX редактором.
offtop: ну почему везде тип int? Ну нельзя так привыкать.
29 строка, пусть cur_led = 10, прибавляем 1, получаем 11
31 строка while( 11 != 11 ) уже не выполняется, значит 11 светодиод не будет работать, а в обратную сторону - 9-ый.
Это я к тому, что не надо заблуждаться.
Вцелом, конечно же всё будет корректно работать, на первом проходе сработают светодиоды 9 и 10 (doWave(9,11,1)), а на обратном пути 11 и 10 (doWave(11,9,-1)).
Поэтому просьба не говорить, что здесь do while, а не while, разницы не будет, поверьте и проверьте.
Кстати, хорошо что с do-while для учебы разобрались. Но "бегать по порядку", обычно применяют for.
Дела ло бы ровно тоже самое (скорее всего в машинные коды даже скомпилится так же).
Чисто из спортивного интереса. Разгораемся и гаснем без используя только однку переменную. Без направления шага.
Это не пример "как нужно делать". Просто "игры ума" :)
Поэтому просьба не говорить, что здесь do while, а не while, разницы не будет, поверьте и проверьте.
Да действительно! Вы правы! Последний по счету светодиод не срабатывает! А я думал, что используя "do while" вместо "while" он будет работать!
Может быть, я неправильно использовал этот цикл? Или даже с помощью "do while" эта задача нерешаемая?
Подскажите ПОЖАЛУЙСТА!
cur_led != (End_Led+ledAmount)
Может быть, я неправильно использовал этот цикл? Или даже с помощью "do while" эта задача нерешаемая?
Все конструкции циклов между собой взаимозаменяемы. А for и do-while так вообще практически эквиваленты. Используются скорее "что привычней" и "что в данном случае легчи читаться будет".
Так что танцевать нужно не от "взять правильный цикл", а от "какое у нас условие выхода из цикла", нужно решить "когда пора прекращать".
Попробуйте
Это очень злобная ошибка. Серьезно.
while([ТУТ ВКРАЛИСЬ НЕВИДИМЫЕ СИМВОЛЫ]cur_led
Без Вашей помощи я б точно сам не нашел! Большое спасибо! Интересно, а КАК нашли эти скрытые символы Вы??? Напишите пожалуйста!
С синусойдой я перемудрил. Можно проще (и не так тяжело для проца, как sin и операции с плавающими точками)
Это очень злобная ошибка. Серьезно.
while([ТУТ ВКРАЛИСЬ НЕВИДИМЫЕ СИМВОЛЫ]cur_led
Без Вашей помощи я б точно сам не нашел! Большое спасибо! Интересно, а КАК нашли эти скрытые символы Вы??? Напишите пожалуйста!
Просто большой опыт бега по граблям :) Кричало (чуть ниже) что не определена перменная которой явно нет в коде. Ну значит явно "что-то с кодировками". Перенабрал руками строку - заработало. Ну а дальше "просто что-бы убедится" открыл hex редактором и посмотрел побайтово. Искал что-то типа "русскую букву 'Эc' вместо английской 'Си' и т.п.), коды символов меньше 32 и т.п.
Применив тот же подход к номеру led-а, можно "ужать-извратится" всю задачу целиком (разгоратся по очереди, туда и обратно), до такого
Но, конечно это "чисто повыежачиватся". За такой код в "боевой задаче" положенно моментальное увольнение :)