Плавное включение светодиодов
- Войдите на сайт для отправки комментариев
Здравствуйте форумчане, в ходе экспериментов с диодной лентой получил некоторые результаты и хочу ими поделиться, может кому-то они пригодятся)
Думаю многие знают что зависимость скважности ШИМ и яркости свечения светодиода не линейна и используя цикл такого вида мы плавного включения не получим
for(int i = 0; i <= 255; i++){
analogWrite(analogOutPin, i);
}
Для устранения нелинейности в большинстве случаев подойдет функция параболы y=x2/a, ее легко применить в цикле
for (unsigned int i = 0; i <= 255; i++) {
analogWrite(analogOutPin, pow(i,2)/255);
delay(3);
}
Но в таком виде наш 8ми битный шим будет задействован не полностью, в цикле включения будет использовано 196 из 256 разных значений скважности, использовать все 256 значений позволит такой цикл
for (unsigned int i = 0; i <= 505; i++) {
analogWrite(analogOutPin, pow(i,2)/1000);
delay(3);
}
Так же можно использовать функцию такого вида y=x^3/a^2, вот графики двух этих функций

Как видно на графике вторая функция(синего цвета) дает более плавный старт, но использовать ее лучше в таком виде, тогда нет резких скачков в конце цикла
for (unsigned int i = 0; i <= 630; i++) {
analogWrite(analogOutPin, pow(i,3)/980100);
delay(3);
}
Если стоит задача просто плавно включить и выключить светодиод, можно использовать программный ШИМ, его мне подсказал форумчанин dimax, правда пришлось его код переделать под себя, с его помощью можно получить большую плавность за счет увеличенной разрядности, и оставить аппаратный ШИМ на другие нужды
#define Bitrate 1500
#define Speed 1
void setup() {
Serial.begin(9600);
pinMode(12, OUTPUT);
}
void loop() {
led_ON();
delay(2000);
led_OFF();
delay(2000);
}
void led_ON() {
for(uint16_t i = 0; i<Bitrate; i+=Speed) {
uint16_t del = pow(i,2) / Bitrate;
PORTB |= 1 << PB4; //включение 12й ноги
delayMicroseconds(del);
PORTB &= ~(1 << PB4); //выключение 12й ноги
delayMicroseconds(Bitrate - del);
}
PORTB |= 1 << PB4;
}
void led_OFF() {
for(uint16_t i = Bitrate; i>Speed; i-=Speed) {
uint16_t del = pow(i,2) / Bitrate;
PORTB |= 1 << PB4; //включение 12й ноги
delayMicroseconds(del);
PORTB &= ~(1 << PB4); //выключение 12й ноги
delayMicroseconds(Bitrate - del);
}
PORTB &= ~(1 << PB4);
}
Изменением параметров Bitrate и Speed можно добиться необходимой скорости включения.
Строго не судите, пост пишу в первый раз)
Думаю многие знают что зависимость скважности ШИМ и яркости свечения светодиода не линейна и используя цикл такого вида мы плавного включения не получим
Думаю многие знают что зависимость скважности ШИМ и яркости свечения светодиода не линейна и используя цикл такого вида мы плавного включения не получим
нужно перешивать глаза.
Та ладно. Может скоро народ начнет собирать деньги на "плавное включение Солнца" А то резкое "включение" Солнца утром и "выключение" вечером приведет что Солнце быстро "перегорит". Или вообще поменять Солнце на "энеросберегающее" с "приятным для глаза свечением".
Alex_Mirko,
правильно, что Вы пробуете, но Вы зря предварительно не изучили матчасть в плане физиологии зрения.
Отсюда у Вас ненужные поиски, то квадрат, то квадрат делённый на куб. На самом деле глаз воспринимет изменения совещённогсти как равномерное тогда, когда оно изменяется по показательному закону, имейте это в виду на будущее.
Точно также ухо воспринимает изменение громкости, отсюда регуляторы громкости имеют показательную харакетристику.
ЕвгенийП, Я привел конкретные примеры полученные опытным путем, они просты в реализации, результат их использования меня вполне устроил, восприятие яркости сложная тема, и углубляться в нее для создания моргалки я не собираюсь. Если у вас есть примеры применения показательного закона так поделитесь ими.
a*=1.07 ;
Вместо 1.07 подбор по вкусу. Про это и написал Евгений. Такие примеры есть у каждого. И не нужно огород городить.
wdrakula, теперь все встало на свои места, спасибо)
Тема уже не раз подымалась. И варианты решения известны: аппаратное изменение тока, переход на ШИМ высокого разрешения(аппаратный или програмный) и переключение на програмный ШИМ при малой яркости и аппарвтный при большой.
И светодиод нелинейный. Он вобще весь нелинейный принципиально. ШИМ линейный но напряжение не тождественно световому потоку.
/// Если включен светодиод на 50 % процентов времени, то и насветит на эти 50% Ведь яркость зависит от напряжения включения. А при ШИМе светодиод или выключен или включен.
Нет. Кроме времени когда он включен или выключен есть время на его переключение. Собственно как и у других полупроводниковых приборов. У обычных светодиодов порядка десятых мсек, что очевидно при частое ШИМ в КГц на малой яркости сказывается. Идут процессы заряда-разряда емкости перехода, диффузии носителей и т.д. Он в это время не светит по полной пока не наступит диамическое равновесие генерации и рекомбинации. Вобще как он светит при переходных - ХЗ, но ясно что если часть энергии уходит на заряд емкости, то она не уходит в свет. Вот занятная статейка http://www.rlocman.ru/shem/schematics.html?di=112354 не совсем по теме, но график емкости от напряжения наглядно показывает степень нелинейности процессов, а по величине емкости можна и время заряда оценить.
Сомневающимся - простой опыт, светодиод к генератору прямоугольных импульсов. Напряжение и скважность фиксируем частоту меняем в широких пределах, наблюдаем изменение яркости.
ПС. Все это не отменяет нелинейность светочувствительности глаза, только дополняет.
Программно есть формула логарифма для 255 значений. У меня где то на работе записана. Я на ней сделал с десяток контроллеров. Работает, но разрядность теряется
Работает, но разрядность теряется
куда теряется?
Программно есть формула логарифма для 255 значений. У меня где то на работе записана. Я на ней сделал с десяток контроллеров. Работает, но разрядность теряется
это писец какой-то. 0х01 х02 0х04 0x08 0х10 х20 0х40 0x80 нормальная шкала логарифмов с основанием 2. Но видимо народ мозг терят когда дело касается логарифмов . Для народа логарифмы с основанием 10 и основанием e. Хотя думаю к поледнему относится как к богу, или верит, или не знает. ;)
Работает, но разрядность теряется
куда теряется?
В никуда.
На самом деле я не вникал тогда в логику рассчетов логарифма, так как проект надо было сдавать срочно, и в математике я не особо силен. Тогда я нашел дейвствующий пример на каком то англоязычном сайте и он меня вполне устроил.
Теряется я думаю не разрядность, а некоторые значения. Кстати по этой же формуле я делал торможение мачты с нагрузкой в 300кг и это помогло, хотя в линейном варианте тормозило неудачно
это писец какой-то. 0х01 х02 0х04 0x08 0х10 х20 0х40 0x80 нормальная шкала логарифмов...
Одно значение на октаву Вы считаете нормальной шкалой?
это писец какой-то. 0х01 х02 0х04 0x08 0х10 х20 0х40 0x80 нормальная шкала логарифмов...
Одно значение на октаву Вы считаете нормальной шкалой?
А что в Ардуино есть октавы @_@. Обычно это называют байтом. Но спутать 8 значений с одним . Советую вам отдохнуть.
ПС: Если по теме, то проще использовать такой метод для плавного зажигания светодиода:(255>>8),(255>>7),(255>>6),(255>>5),(255>>4),(255>>3),(255>>2),(255>>1),(255>>0)
ПС: Если по теме, то проще использовать такой метод для плавного зажигания светодиода:(255>>8),(255>>7),(255>>6),(255>>5),(255>>4),(255>>3),(255>>2),(255>>1),(255>>0)
не смотря на элегантность решения - ну, нихера это не плавное зажигание.
8 ступенек вместо 255, которые тоже заметны, если обращать внимание.
А что в Ардуино есть октавы @_@. Обычно это называют байтом. Но спутать 8 значений с одним . Советую вам отдохнуть.
Октавы есть везде, где есть логарифмическая шкала. И байтами их никогда не называли. Даже в шутку.
Ступени все равно будут заметны на 8ми битах, как ни крути. Просто логарифмическое регулирование дает более линейное(на глаз) нарастание яркости.
Убрать скачки можно конденсатором в затворе ключевого транзистора, но подбирать надо индивидуально под каждый тип ленты или диодов
А что в Ардуино есть октавы @_@. Обычно это называют байтом. Но спутать 8 значений с одним . Советую вам отдохнуть.
Вы же, как мне казалось, неплохой программист и имеете какое-то образование, так зачем позориться?
"отличается на октаву" тождественно "отличается в два раза". Выглядит так, будто вы этого не знали.
Пошло, конечно, из музыки, где, например, "Ля" первой октавы = 440Гц, второй = 880Гц, а малой, соответственно = 220Гц.
Изначально разница в октаву по высоте звука ранялась разнице в высоте полной струны и половины струны.
Разобрался:
число 980100 нужно, чтобы получить 255 - максимум, доступный для этого ШИМ-порта.
Но обнаружилось странное. У 10-битных ШИМов на 255 тоже включается максимум. Может кто подскажет почему?
Разобрался: ... Может кто подскажет почему?
Теме три года. Вы уж говорите поподробнее в чём именно Вы разобрались и что Вам нужно подсказывать.
Вы уж говорите поподробнее в чём именно Вы разобрались и что Вам нужно подсказывать.
У 10-битных ШИМов на 255 тоже включается максимум. Может кто подскажет почему?
Максимум у 10-битного ШИМа должен быть при 1023, но также максимальный уровень (стопроцентное заполнение, как мне кажется на глаз) будет если задать значение 255 в analogWrite.
Может это связано с тем, что у меня изменена частота ШИМа:
Максимум у 10-битного ШИМа должен быть при 1023, но также максимальный уровень (стопроцентное заполнение, как мне кажется на глаз) будет если задать значение 255 в analogWrite.
У analogWrite максимум всегда достигается при 255, независимо от того, какой там таймер. Откройте файл Arduino.h и убедитесь, что у неё параметр восьмибитный
Тогда почему значение 256 ярче, чем 254? По такой логике значение 256 должно гасить до нуля, так как младшие 8 бит равны нулю.
На самом деле, первый параметр обозначает порт(ногу/пин/и т.п.), а второй параметр - int - двухбайтный. Но это не объясняет, почему 255 выдает максимум.
Приведите минимальный полный код, иллюстрирующий "дефект".
На самом деле, первый параметр обозначает порт
Это да, это я чё-то не того ... бес попутал. Прошу прощения,
Но первая фраза всё равно верна - 255 у него максимум независимо от таймера.
А то, что у Вас там видно, так я не вижу ни этого эффекта, ни Вашего скетча, откуда мне знать.
И да, обязательно скажите, что у Вас за ардуина (памятуя о вчерашней теме)
void setup() { // Пины D9 и D10 - 15.6 кГц 10bit TCCR1A = 0b00000011; // 10bit TCCR1B = 0b00001001; // x1 fast pwm /* // Пины D9 и D10 - 7.8 кГц 10bit TCCR1A = 0b00000011; // 10bit TCCR1B = 0b00000001; // x1 phase correct */ pinMode (9, OUTPUT); } void loop() { int i; for (i=0;i<1024;i++) { // if ( i == 255 ) continue; // если добавить, то сразу видно, что моргает именно на 255 analogWrite (9, i); delay (10); } }Я пробовал на 7.8 или 15.6 кГц для 10 бит - оба варианта дают такой "мырг" на 255.
что у Вас за ардуина (памятуя о вчерашней теме)
Китайская Arduino Nano с CH340. Светодиоды подключены через модуль XY-MOS с двумя GA6L.
Ну, так бы сразу и говорили, что у Вас настройки свои, а для управления Вы зачем-то используете штатную analogWrite!!!
А теперь посмотрите в текст analogWrite. Вы удивитесь, но при 255 она тупо выключает PWM и фигачит туда постоянный HIGH-уровень. Теперь всё понятно? Больше нет вопросов?
Тогда почему значение 256 ярче, чем 254? По такой логике значение 256 должно гасить до нуля, так как младшие 8 бит равны нулю.
потому что 254 и 256 - обычные значения, а 255 "особое". 254 и 256 дают на выходе сигнал скважности, соответственно 254/1023 и 256/1023 - примерно 25%
А для 255 (и для 0)в коде analoWrite() есть специальное условие
if (val == 0) { digitalWrite(pin, LOW); } else if (val == 255) { digitalWrite(pin, HIGH); }Добавка - проверил сейчас на осциллографе - при ваших настройках таймера все значения от 0 до 1023 (кроме 255) работают верно, 300 дает ШИМ примерно 30%, 512 - 50%
Встройте в свой код обработку особого случая - 255 - и все будет работать и с analogWrite() тоже
Встройте в свой код обработку особого случая - 255 - и все будет работать и с analogWrite() тоже
Так и получилось. Проверяю, если 255 и выставляю 256. Там не должно быть заметно на глаз такое небольшое расхождение. Как-то печально это.
Как-то печально это.
и что в этом печального ? :)
А что печально? В Ардуино максимум, что можно передать analogWrite - это 255. Вы полезли с ручными настройками, которые несовместимы с настройками среды Ардуино - сам себе злобный буратино. Не пользуйтесь analogWrite, а продолжайте работать напрямую с регистрами и будет Вам счастье.
Теперь надо прикрутить плавное изменение между произвольными точками яркости (косинусоидное, наверное) поверх плавного изменения по всей шкале, ориентированного на неравномерность свечения светодиодов в зависимости от ШИМ. Пока плавности удалось достичь при полном проходе от минимума до максимума, но если нужно остановиться где-то в середине шкалы, то выглядит немного грубо.
Вроде Х лет назад я где-то этот косинус применял зачем-то, а теперь не могу вспомнить, как это посчитать: плавное повышение в начале, нарастание скорости в середине и плавное замедление к концу. Косинус вроде простая вещь, катет на гипотенузу/радиус поделить, но мозгов не хватает. Может кто подскажет?
Нужно взять шкалу типа этого:
for (unsigned int i = 0; i <= 630; i++) { analogWrite(analogOutPin, pow(i,3)/980100); delay(3); }А сверху на эти значения наложить плавное изменение, например, с 500 до 200. Вот так:
Тогда яркость будет плавно меняться на любом диапазоне, а не только включаться и выключаться.
В Ардуино максимум, что можно передать analogWrite - это 255.
Евгений, это не так. Я там выше писал - значения выше 255 правильно работают в analogWrite
А что печально? В Ардуино максимум, что можно передать analogWrite - это 255. Вы полезли с ручными настройками, которые несовместимы с настройками среды Ардуино - сам себе злобный буратино. Не пользуйтесь analogWrite, а продолжайте работать напрямую с регистрами и будет Вам счастье.
Да я просто со светом полез экспериментировать. Это вторая мысль уменя для чего всю это технологию "доступного микроконтроллера" можно было использовать. Ну то есть я всего неделю в этом разбираюсь. Насмотрелся на ютубе как все супер-просто и даже для самых тупых доступно, а значит и мне по силам. Но, оказалось, сходу несколько проблем (оказывается загрузчик нужно поменять, иначе оно вообще не работает), оказывается ШИМ меньше килогерца по умолчанию, ну и всякое такое. Все эти штуки, вроде как, решает среда Arduino IDE и готовые платы, в том числе, с программатором на борту. Просто не хотелось в ассемблер лезть, был такой опыт и знаю, что времени нужно больше, и более тщательно продумывать логику работы. Эх, возможно придется все-таки углубляться.
Ну ладно, спойлера здесь нет, немного не в тему. Спасибо, что разъяснили.
В Ардуино максимум, что можно передать analogWrite - это 255.
Евгений, это не так. Я там выше писал - значения выше 255 правильно работают в analogWrite
чет Петрович меня пугает сегодня :).
В Ардуино максимум, что можно передать analogWrite - это 255.
Евгений, это не так. Я там выше писал - значения выше 255 правильно работают в analogWrite
Вы не поняли о чём я (ТС, похоже тоже).
В Ардуино (именно в среде ардуино, без лазанья грязными руками в регистры, как это сделал ТС) низзя, т.к. ШИМ восьмибитовый всегда, на любых таймерах. Поэтому то, что там 255 заменяется на HIGH, багом в среде ардуино не является.
Но ТС решил, настройки сделать "грязными руками", и при этом продолжать пользовать analogWrite - и нарвался. А теперь среда ардуино виновата :-)
Прошу помощи не получается сделать плавное достижение значения на PWM после получения значения по MQTT
Может кто помочь с доделкой...
#include <ESP8266WiFi.h> #include <PubSubClient.h> const char *ssid = "***"; //название сети const char *password = "****"; //пароль сети const char *mqtt_server = "****"; //адрес mqtt брокера WiFiClient espClient; //инициализация вифи PubSubClient client(espClient); //инициализация mqtt клиента #define LEDPIN 2 //устанавливаем пин с лед лентой // функция получения топиков от брокера void callback(char* topic, byte* payload, unsigned int length) { Serial.println(); Serial.print(topic); // выводим в сериал порт название топика Serial.print(" => "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } // выводим в сериал порт значение полученных данных String strTopic = String(topic); //получаем название топика if (strTopic == "ESP03/led") //проверяем из нужного ли топика пришли данные { payload[length] = 0; //чистим от мусора, длинна строки String strPayload = String((char*)payload); //считываем значение топика int val = strPayload.toInt(); //конвертируем для шим int stled = map(val, 0, 100, 1023, 0); //приводим значение 0-100 в значение 1000-0 analogWrite(LEDPIN, stled); //устанавливаем уровень шим сигнала Serial.print(" => "); Serial.print(stled); // для отладки } } void setup() { pinMode(LEDPIN, OUTPUT); //устанвливаем ка выход Serial.begin(115200); } void loop() { if (WiFi.status() != WL_CONNECTED) //если нет подключения к вифи { WiFi.begin(ssid, password); //конектимся if (WiFi.waitForConnectResult() != WL_CONNECTED) return; //если не получилось то повторяем } if (WiFi.status() == WL_CONNECTED) //если подключились { if (!client.connected()) //если нет mqtt { // то подключаемся к mqtt client.setServer(mqtt_server, 1883); client.setCallback(callback); client.connect("ESP03led"); //id клиента(должно быть уникальным в сети) client.subscribe("ESP03/led"); //подписываемся на топик } if (client.connected()) { // если подключились client.loop(); // то запускаем цикл получения топиков))) } } }Смотрел вот такую конструкцию но по итогу ничего не изменилось у меня может что то не верно делал..
int fadeAmount = 3; // шаг изменения яркости void setup() { } void loop() { analogWrite(LEDPIN, stled); // устанавливаем значение stled = stled + fadeAmount; // прибавляем шаг изменения яркости, которая установится в следующем цикле if (stled == 0|| stled == 1023) { // Условие fadeAmount = -fadeAmount ; // Меняем знак // delay(500); // ожидаем 1/2 секунды } delay(50); // ожидаем 1/20 секунды }Смотрел вот такую конструкцию но по итогу ничего не изменилось у меня может что то не верно делал..
int fadeAmount = 3; // шаг изменения яркости void setup() { } void loop() { analogWrite(LEDPIN, stled); // устанавливаем значение stled = stled + fadeAmount; // прибавляем шаг изменения яркости, которая установится в следующем цикле if (stled == 0|| stled == 1023) { // Условие fadeAmount = -fadeAmount ; // Меняем знак // delay(500); // ожидаем 1/2 секунды } delay(50); // ожидаем 1/20 секунды }1. стр. 06: "В Ардуино максимум, что можно передать analogWrite - это 255." (#31).
2. стр. 08. например, после 1021 следующее значение stled - 1024. 1023, может быть, когда-то будет - но когда?
Так у меня модуль ESP я так понял у него значения от 0 до 1023...
он у меня по факту сейчас в MQTT передаешь значение яркость меняется но не плавно а хотелось бы плавно...
или я что то не понимаю и нужно управлять до 255
sergei_a88@bk.r - главаная проблема - неправильное условие в строчках 8-10. Вместо проверки на равенство поставьте "больше-равно" и "меньше-равно"
Без изменений также изменяется рыками яркость
#include <ESP8266WiFi.h> #include <PubSubClient.h> const char *ssid = "****"; //название сети const char *password = "****"; //пароль сети const char *mqtt_server = "192.168.***"; //адрес mqtt брокера WiFiClient espClient; //инициализация вифи PubSubClient client(espClient); //инициализация mqtt клиента #define LEDPIN 2 //устанавливаем пин с лед лентой int fadeAmount = 3; // шаг изменения яркости // функция получения топиков от брокера void callback(char* topic, byte* payload, unsigned int length) { Serial.println(); Serial.print(topic); // выводим в сериал порт название топика Serial.print(" => "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } // выводим в сериал порт значение полученных данных String strTopic = String(topic); //получаем название топика if (strTopic == "ESP03/led") //проверяем из нужного ли топика пришли данные { payload[length] = 0; //чистим от мусора, длинна строки String strPayload = String((char*)payload); //считываем значение топика int val = strPayload.toInt(); //конвертируем для шим int stled = map(val, 0, 100, 1023, 0); //приводим значение 0-100 в значение 1000-0 analogWrite(LEDPIN, stled); // устанавливаем значение stled = stled + fadeAmount; // прибавляем шаг изменения яркости, которая установится в следующем цикле if (stled >= 0|| stled <= 1023) { // Условие fadeAmount = -fadeAmount ; // Меняем знак // delay(500); // ожидаем 1/2 секунды } delay(50); // ожидаем 1/20 секунды Serial.print(" => "); Serial.print(stled); // для отладки } } void setup() { pinMode(LEDPIN, OUTPUT); //устанвливаем ка выход Serial.begin(115200); } void loop() { if (WiFi.status() != WL_CONNECTED) //если нет подключения к вифи { WiFi.begin(ssid, password); //конектимся if (WiFi.waitForConnectResult() != WL_CONNECTED) return; //если не получилось то повторяем } if (WiFi.status() == WL_CONNECTED) //если подключились { if (!client.connected()) //если нет mqtt { // то подключаемся к mqtt client.setServer(mqtt_server, 1883); client.setCallback(callback); client.connect("ESP03led"); //id клиента(должно быть уникальным в сети) client.subscribe("ESP03/led"); //подписываемся на топик } if (client.connected()) { // если подключились client.loop(); // то запускаем цикл получения топиков))) } } }я имел в виду вставить в кусок кода в сообщении #38. а не в ваш.
Ваш код из #41 в принципе неверный, там никакого "плавного затухания" вообще не может быть, потому что вы не увеличиваете и уменьшаете яркость, а на каждом шаге читаете ее значение из сети заново
Выкиньте из этого кода работу с MQTT вовсе - и затухание заработает
спасибо
На самом деле функция отличная - что бы я делал без математики и людей шарящих в ней...
Ребят, я конечно хз, сидит ли кто на форуме, но честно говоря думаю что с моей проблемой многие сталкивались:
Мне нужно было состряпать плавное включение/выключение светодиода по нажатии на одну и ту же кнопку. Я с этой проблемой е**лся 5 часов в связи с полным отсутствием у себя нормальной базы в знаниях ArduinoIDE. Но всё же благодаря этому доброму человеку из 2017 я состряпал-таки эту прогу.
Прошу ко вниманию нуждающихся в ней:
void setup{
поделюсь старым баяном которым пользуюсь:
const byte loga[64]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,16,18,22,25,28,29,30,33,36,39,42,46,49,53,56,60,64,68,72, 77,81,86,90,95,100,105,110,116,121,127,132,138,144,150,156,163,169,176,182,189,196,203,210,218,225,233,240,248,255};