Управление светодиодами WS2812B с помощью ШИМ
- Войдите на сайт для отправки комментариев
Втр, 30/11/2021 - 10:33
Всем привет, имеется arduino nano и требуется управлять светодиодами по ШИМ. Управляющий сигнал на светодиоды выводится через D5, а считывание PWM происходит на контакте D3. Проблема в том, что не удается заставить работать оба входа одновременно. Когда работает подсветка, PWM не считывается. Пришлось реализовать программное прерывание: каждые 3 секунды запускается считывание PWM и при определенном значении PWM подсветка потом гаснет.
Возможно ли это сделать как то параллельно, чтобы подсветка работала постоянно и не ожидала пока считается сигнал PWM?
//считывание PWM и выполнение условия включения ленты
if (_gtv5 == 0)
{
_gtv3 = (_SCT_1_PDON) < (1000);
}
//переключения между считыванием PWM и включением LED
if (!(0))
{
if (! _gen3I)
{
_gen3I = 1;
_gen3O = 1;
_gen3P = millis();
}
}
else
{
_gen3I = 0 ;
_gen3O= 0;
}
if (_gen3I)
{
if (_gen3O)
{
if (_isTimer(_gen3P , 3000))
{
_gen3P = millis();
_gen3O = 0;
}
}
else
{
if (_isTimer(_gen3P , 2000))
{
_gen3P = millis();
_gen3O = 1;
}
}
}
_gtv5 = _gen3O;
if (_gen3O)
{
if (_trgrt1I)
{
_trgrt1 = 0;
}
else
{
_trgrt1 = 1;
_trgrt1I = 1;
}
}
else
{
_trgrt1 = 0;
_trgrt1I = 0;
}
;
_gtv34 = _trgrt1;
}
bool _isTimer(unsigned long startTime, unsigned long period)
{
unsigned long currentTime;
currentTime = millis();
if (currentTime>= startTime)
{
return (currentTime>=(startTime + period));
}
else
{
return (currentTime >=(4294967295-startTime+period));
}
}
bool _isTimerMicros(unsigned long startTime, unsigned long period)
{
unsigned long currentTime;
currentTime = micros();
if (currentTime>= startTime)
{
return (currentTime>=(startTime + period));
}
else
{
return (currentTime >=(4294967295-startTime+period));
}
}
void _SCT_1coutFunction()
{
if(!(0))
{
_SCT_1_PDON= (micros()) - _SCT_1_PDTT;
}
attachInterrupt(digitalPinToInterrupt (3), _SCT_1positiveCoutFunction, RISING);
}
void _SCT_1positiveCoutFunction()
{
_SCT_1_PDTT= micros();
attachInterrupt(digitalPinToInterrupt (3), _SCT_1coutFunction, FALLING);
}
Это "цифровые" светодиоды и у них есть свой контроллер со своим ШИМ. Что вы от них хотите добиться ???
Мне нужно управлять этими светодиодами удаленно, с помощью пульта радиоуправления. К ардуино нано на d3 подключен приемник который может выдавать PWM сигнал. Хочу с помощью него управлять включением и отключением светодиодов (сигнала на d5).
Ага, т.е. при помощи ШИМ Вы управляете ардуиной (извне), а у она управляет светодиодами по обычной схеме, как положено. Я правильно Вас понял?
Ага, т.е. при помощи ШИМ Вы управляете ардуиной (извне), а у она управляет светодиодами по обычной схеме, как положено. Я правильно Вас понял?
Да, все верно
чё то ты там намудрил, смотри, тут я принимаю два канала PWM и всё работает
Возможно ли это сделать как то параллельно
ХЗ. Смотря как у Вас реализовано управление адресными светодиодами. Я, например, часто делаю это с запрещёнными прерываниями, чтобы тайминги не сбивались. Там очень жёсткие и короткие тайминги и их нельзя обижать. Потому в то время, когда я вывожу информацию на ленту, никакого ШИМа читать нельзя. Если мне нужно что-то читать, я специально организую паузы между выводом пачки данных на ленту и в эти паузы читаю.
Если мне нужно что-то читать, я специально организую паузы между выводом пачки данных на ленту и в эти паузы читаю.
какой длительности?
ХЗ. Смотря как у Вас реализовано управление адресными светодиодами.
Весь код скетча здесь. Я делал в FLProg, т.к. слабо разбираюсь в программировании на С и для меня было непросто сделать даже текущий вариант :)
#include <Adafruit_NeoPixel.h> Adafruit_NeoPixel pixels= Adafruit_NeoPixel(70 , 5 , NEO_GRB + NEO_KHZ800); //first number change does distance between colors bool en_151307670_9; int num_led_151307670_9; int led_1r_151307670_9; int led_1g_151307670_9; int led_1b_151307670_9; bool en_151307670_34; int num_led_151307670_34; int led_1r_151307670_34; int led_1g_151307670_34; int led_1b_151307670_34; bool en_151307670_35; int num_led_151307670_35; int led_1r_151307670_35; int led_1g_151307670_35; int led_1b_151307670_35; bool en_151307670_1; int num_led_151307670_1; int led_1r_151307670_1; int led_1g_151307670_1; int led_1b_151307670_1; bool en_151307670_2; int num_led_151307670_2; int led_1r_151307670_2; int led_1g_151307670_2; int led_1b_151307670_2; bool en_151307670_3; int num_led_151307670_3; int led_1r_151307670_3; int led_1g_151307670_3; int led_1b_151307670_3; bool en_151307670_4; int num_led_151307670_4; int led_1r_151307670_4; int led_1g_151307670_4; int led_1b_151307670_4; bool en_151307670_5; int num_led_151307670_5; int led_1r_151307670_5; int led_1g_151307670_5; int led_1b_151307670_5; bool en_151307670_6; int num_led_151307670_6; int led_1r_151307670_6; int led_1g_151307670_6; int led_1b_151307670_6; bool en_151307670_7; int num_led_151307670_7; int led_1r_151307670_7; int led_1g_151307670_7; int led_1b_151307670_7; bool en_151307670_8; int num_led_151307670_8; int led_1r_151307670_8; int led_1g_151307670_8; int led_1b_151307670_8; bool en_151307670_81; int num_led_151307670_81; int led_1r_151307670_81; int led_1g_151307670_81; int led_1b_151307670_81; bool en_151307670_82; int num_led_151307670_82; int led_1r_151307670_82; int led_1g_151307670_82; int led_1b_151307670_82; bool _gtv3; bool _gtv5; bool _gtv34; int _gtv35; bool _gen2I = 0; bool _gen2O = 0; unsigned long _gen2P = 0UL; bool _gen1I = 0; bool _gen1O = 0; unsigned long _gen1P = 0UL; bool _trgrt1 = 0; bool _trgrt1I = 0; unsigned long _SCT_1_PDTT = 0UL; unsigned long _SCT_1_PDON = 0UL; bool _count2I = 0; int _count2_Value = 0; bool _count1I = 0; int _count1_Value = 0; bool _gen3I = 0; bool _gen3O = 0; unsigned long _gen3P = 0UL; void setup() { pinMode(3, INPUT); attachInterrupt(digitalPinToInterrupt (3), _SCT_1coutFunction, FALLING); attachInterrupt(digitalPinToInterrupt (3), _SCT_1positiveCoutFunction, RISING); pixels.begin(); pixels.show(); // Устанавливаем все светодиоды в состояние "Выключено" pixels.show(); // Устанавливаем все светодиоды в состояние "Выключено" pixels.show(); // Устанавливаем все светодиоды в состояние "Выключено" pixels.show(); // Устанавливаем все светодиоды в состояние "Выключено" pixels.show(); // Устанавливаем все светодиоды в состояние "Выключено" pixels.show(); // Устанавливаем все светодиоды в состояние "Выключено" pixels.show(); // Устанавливаем все светодиоды в состояние "Выключено" pixels.show(); // Устанавливаем все светодиоды в состояние "Выключено" pixels.show(); // Устанавливаем все светодиоды в состояние "Выключено" pixels.show(); // Устанавливаем все светодиоды в состояние "Выключено" pixels.show(); // Устанавливаем все светодиоды в состояние "Выключено" pixels.show(); // Устанавливаем все светодиоды в состояние "Выключено" pixels.show(); // Устанавливаем все светодиоды в состояние "Выключено" pixels.show(); // Устанавливаем все светодиоды в состояние "Выключено" } void loop() { //Плата:1 //Наименование:Инициализация подсветки if (_gtv5 == 1) { } //Плата:2 //Наименование:Счетчик для бегущей волны if (_gtv5 == 1) { if (_gtv3) { if (! _gen2I) { _gen2I = 1; _gen2O = 1; _gen2P = micros(); } } else { _gen2I = 0 ; _gen2O= 0; } if (_gen2I) { if (_isTimerMicros (_gen2P , 1)) { _gen2P = micros(); _gen2O = ! _gen2O; } } if (_gtv34) { _count2_Value = 0; } else { if (_gen2O) { if (! _count2I) { _count2I = 1; _count2_Value = _count2_Value + 1; } } else { _count2I = 0; } } _gtv35 = _count2_Value; if (!(_gtv3)) { if (! _gen1I) { _gen1I = 1; _gen1O = 1; _gen1P = millis(); } } else { _gen1I = 0 ; _gen1O= 0; } if (_gen1I) { if (_isTimer (_gen1P , 1)) { _gen1P = millis(); _gen1O = ! _gen1O; } } if (_gtv34) { _count1_Value = 0; } else { if (_gen1O) { if (! _count1I) { _count1I = 1; _count1_Value = _count1_Value + 1; } } else { _count1I = 0; } } en_151307670_9 = _gen1O; num_led_151307670_9 = _count1_Value; led_1r_151307670_9 = 0; led_1g_151307670_9 = 0; led_1b_151307670_9 = 0; if (en_151307670_9 == 1) { pixels.setPixelColor(num_led_151307670_9 -1, pixels.Color(led_1r_151307670_9 , led_1g_151307670_9 , led_1b_151307670_9)); pixels.show(); } else { pixels.setPixelColor(num_led_151307670_9 -1, pixels.Color(0 , 0 , 0)); pixels.show(); } ; } //Плата:3 //Наименование:Бегущая волна if (_gtv5 == 1) { en_151307670_35 = _gtv3; num_led_151307670_35 = (_gtv35)+(1); led_1r_151307670_35 = 100; led_1g_151307670_35 = 0; led_1b_151307670_35 = 0; if (en_151307670_35 == 1) { pixels.setPixelColor(num_led_151307670_35 -1, pixels.Color(led_1r_151307670_35 , led_1g_151307670_35 , led_1b_151307670_35)); pixels.show(); } else { pixels.setPixelColor(num_led_151307670_35 -1, pixels.Color(0 , 0 , 0)); pixels.show(); } ; en_151307670_2 = _gtv3; num_led_151307670_2 = (_gtv35)+(3); led_1r_151307670_2 = 200; led_1g_151307670_2 = 0; led_1b_151307670_2 = 0; if (en_151307670_2 == 1) { pixels.setPixelColor(num_led_151307670_2 -1, pixels.Color(led_1r_151307670_2 , led_1g_151307670_2 , led_1b_151307670_2)); pixels.show(); } else { pixels.setPixelColor(num_led_151307670_2 -1, pixels.Color(0 , 0 , 0)); pixels.show(); } ; en_151307670_3 = _gtv3; num_led_151307670_3 = (_gtv35)+(4); led_1r_151307670_3 = 255; led_1g_151307670_3 = 0; led_1b_151307670_3 = 0; if (en_151307670_3 == 1) { pixels.setPixelColor(num_led_151307670_3 -1, pixels.Color(led_1r_151307670_3 , led_1g_151307670_3 , led_1b_151307670_3)); pixels.show(); } else { pixels.setPixelColor(num_led_151307670_3 -1, pixels.Color(0 , 0 , 0)); pixels.show(); } ; en_151307670_1 = _gtv3; num_led_151307670_1 = (_gtv35)+(2); led_1r_151307670_1 = 150; led_1g_151307670_1 = 0; led_1b_151307670_1 = 0; if (en_151307670_1 == 1) { pixels.setPixelColor(num_led_151307670_1 -1, pixels.Color(led_1r_151307670_1 , led_1g_151307670_1 , led_1b_151307670_1)); pixels.show(); } else { pixels.setPixelColor(num_led_151307670_1 -1, pixels.Color(0 , 0 , 0)); pixels.show(); } ; en_151307670_34 = _gtv3; num_led_151307670_34 = _gtv35; led_1r_151307670_34 = 10; led_1g_151307670_34 = 0; led_1b_151307670_34 = 0; if (en_151307670_34 == 1) { pixels.setPixelColor(num_led_151307670_34 -1, pixels.Color(led_1r_151307670_34 , led_1g_151307670_34 , led_1b_151307670_34)); pixels.show(); } else { pixels.setPixelColor(num_led_151307670_34 -1, pixels.Color(0 , 0 , 0)); pixels.show(); } ; en_151307670_81 = (_gtv35) == (2); num_led_151307670_81 = 69; led_1r_151307670_81 = 255; led_1g_151307670_81 = 255; led_1b_151307670_81 = 255; if (en_151307670_81 == 1) { pixels.setPixelColor(num_led_151307670_81 -1, pixels.Color(led_1r_151307670_81 , led_1g_151307670_81 , led_1b_151307670_81)); pixels.show(); } else { pixels.setPixelColor(num_led_151307670_81 -1, pixels.Color(0 , 0 , 0)); pixels.show(); } ; en_151307670_4 = _gtv3; num_led_151307670_4 = (_gtv35)+(10); led_1r_151307670_4 = 10; led_1g_151307670_4 = 0; led_1b_151307670_4 = 0; if (en_151307670_4 == 1) { pixels.setPixelColor(num_led_151307670_4 -1, pixels.Color(led_1r_151307670_4 , led_1g_151307670_4 , led_1b_151307670_4)); pixels.show(); } else { pixels.setPixelColor(num_led_151307670_4 -1, pixels.Color(0 , 0 , 0)); pixels.show(); } ; en_151307670_6 = _gtv3; num_led_151307670_6 = (_gtv35)+(7); led_1r_151307670_6 = 150; led_1g_151307670_6 = 0; led_1b_151307670_6 = 0; if (en_151307670_6 == 1) { pixels.setPixelColor(num_led_151307670_6 -1, pixels.Color(led_1r_151307670_6 , led_1g_151307670_6 , led_1b_151307670_6)); pixels.show(); } else { pixels.setPixelColor(num_led_151307670_6 -1, pixels.Color(0 , 0 , 0)); pixels.show(); } ; en_151307670_8 = _gtv3; num_led_151307670_8 = (_gtv35)+(5); led_1r_151307670_8 = 255; led_1g_151307670_8 = 0; led_1b_151307670_8 = 0; if (en_151307670_8 == 1) { pixels.setPixelColor(num_led_151307670_8 -1, pixels.Color(led_1r_151307670_8 , led_1g_151307670_8 , led_1b_151307670_8)); pixels.show(); } else { pixels.setPixelColor(num_led_151307670_8 -1, pixels.Color(0 , 0 , 0)); pixels.show(); } ; en_151307670_82 = (_gtv35) == (2); num_led_151307670_82 = 70; led_1r_151307670_82 = 255; led_1g_151307670_82 = 255; led_1b_151307670_82 = 255; if (en_151307670_82 == 1) { pixels.setPixelColor(num_led_151307670_82 -1, pixels.Color(led_1r_151307670_82 , led_1g_151307670_82 , led_1b_151307670_82)); pixels.show(); } else { pixels.setPixelColor(num_led_151307670_82 -1, pixels.Color(0 , 0 , 0)); pixels.show(); } ; en_151307670_7 = _gtv3; num_led_151307670_7 = (_gtv35)+(6); led_1r_151307670_7 = 200; led_1g_151307670_7 = 0; led_1b_151307670_7 = 0; if (en_151307670_7 == 1) { pixels.setPixelColor(num_led_151307670_7 -1, pixels.Color(led_1r_151307670_7 , led_1g_151307670_7 , led_1b_151307670_7)); pixels.show(); } else { pixels.setPixelColor(num_led_151307670_7 -1, pixels.Color(0 , 0 , 0)); pixels.show(); } ; en_151307670_5 = _gtv3; num_led_151307670_5 = (_gtv35)+(8); led_1r_151307670_5 = 100; led_1g_151307670_5 = 0; led_1b_151307670_5 = 0; if (en_151307670_5 == 1) { pixels.setPixelColor(num_led_151307670_5 -1, pixels.Color(led_1r_151307670_5 , led_1g_151307670_5 , led_1b_151307670_5)); pixels.show(); } else { pixels.setPixelColor(num_led_151307670_5 -1, pixels.Color(0 , 0 , 0)); pixels.show(); } ; } //Плата:4 //Наименование:Блок считывания PWM и выполнение условия включения ленты if (_gtv5 == 0) { _gtv3 = (_SCT_1_PDON) < (1000); } //Плата:5 //Наименование:Блок переключения между считыванием PWM и включением LED if (!(0)) { if (! _gen3I) { _gen3I = 1; _gen3O = 1; _gen3P = millis(); } } else { _gen3I = 0 ; _gen3O= 0; } if (_gen3I) { if (_gen3O) { if (_isTimer(_gen3P , 3000)) { _gen3P = millis(); _gen3O = 0; } } else { if (_isTimer(_gen3P , 2000)) { _gen3P = millis(); _gen3O = 1; } } } _gtv5 = _gen3O; if (_gen3O) { if (_trgrt1I) { _trgrt1 = 0; } else { _trgrt1 = 1; _trgrt1I = 1; } } else { _trgrt1 = 0; _trgrt1I = 0; } ; _gtv34 = _trgrt1; } bool _isTimer(unsigned long startTime, unsigned long period) { unsigned long currentTime; currentTime = millis(); if (currentTime>= startTime) { return (currentTime>=(startTime + period)); } else { return (currentTime >=(4294967295-startTime+period)); } } bool _isTimerMicros(unsigned long startTime, unsigned long period) { unsigned long currentTime; currentTime = micros(); if (currentTime>= startTime) { return (currentTime>=(startTime + period)); } else { return (currentTime >=(4294967295-startTime+period)); } } void _SCT_1coutFunction() { if(!(0)) { _SCT_1_PDON= (micros()) - _SCT_1_PDTT; } attachInterrupt(digitalPinToInterrupt (3), _SCT_1positiveCoutFunction, RISING); } void _SCT_1positiveCoutFunction() { _SCT_1_PDTT= micros(); attachInterrupt(digitalPinToInterrupt (3), _SCT_1coutFunction, FALLING); }ну, в каждом проекте по своему, от много зависит.
В крайнем проекте, там так. Частота обновления ленты установлена 60Гц. Т.е. каждые 16667 микросекунд я начинаю обновлять ленту. Это занимает какое-то время (не помню точно, но сильно меньше периода), а все остальное время (до начала нового обновления) используется для других дел, в частности и для изменения массива из которого в следующий раз будет обновляться лента.
Весь код скетча здесь. Я делал в FLProg, т.к. слабо разбираюсь в программировании на С и для меня было непросто сделать даже текущий вариант :)
Ничего не могу сказать, т.к. слабо разбираюсь (вернее, совсем не разбираюсь) в FLProg :-(
В крайнем проекте, там так. Частота обновления ленты установлена 60Гц. Т.е. каждые 16667 микросекунд я начинаю обновлять ленту. Это занимает какое-то время (не помню точно, но сильно меньше периода), а все остальное время (до начала нового обновления) используется для других дел, в частности и для изменения массива из которого в следующий раз будет обновляться лента.
Спасибо, стало понятнее, что в целом двигался в правильном направлении, наверное мне нужно уменьшать период опроса ШИМ, чтобы прерывание было менее заметным.
Весь код скетча здесь. Я делал в FLProg, т.к. слабо разбираюсь в программировании на С и для меня было непросто сделать даже текущий вариант :)
Ничего не могу сказать, т.к. слабо разбираюсь (вернее, совсем не разбираюсь) в FLProg :-(
я тоже не разбираюсь, но строки 92 и 93 меня сильно озадачили, разве так можно?
Чтение PWM с использованием микрос совсем простое:
volatile unsigned int rc1_data = 0; // значение PWM полученное с приёмника volatile unsigned long start_timeRC1 = 0; volatile unsigned long prevTime; volatile byte flag_RC1 = 0; /*******Обработчик прерывания (чтение PWM) *******/ void Rc1() { if (digitalRead(2) == HIGH && flag_RC1 == 0) { //сохраняем значение времени начала импульса start_timeRC1 = micros(); flag_RC1 = 1; } if (digitalRead(2) == LOW && flag_RC1 == 1) { //сохраняем значение длительности импульса канала 1 rc1_data = micros() - start_timeRC1; flag_RC1 = 0; } }//END RC1 void setup() { attachInterrupt(0, Rc1, CHANGE); } void loop() { }я тоже не разбираюсь, но строки 92 и 93 меня сильно озадачили, разве так можно?
а это где такое? - строчки эти вижу, но в коде ТС они под другими номерами и не подряд
Чтение PWM с использованием микрос совсем простое:
нет, еще проще - pulsein
Чтение PWM с использованием микрос совсем простое:
volatile unsigned int rc1_data = 0; // значение PWM полученное с приёмника volatile unsigned long start_timeRC1 = 0; volatile unsigned long prevTime; volatile byte flag_RC1 = 0; /*******Обработчик прерывания (чтение PWM) *******/ void Rc1() { if (digitalRead(2) == HIGH && flag_RC1 == 0) { //сохраняем значение времени начала импульса start_timeRC1 = micros(); flag_RC1 = 1; } if (digitalRead(2) == LOW && flag_RC1 == 1) { //сохраняем значение длительности импульса канала 1 rc1_data = micros() - start_timeRC1; flag_RC1 = 0; } }//END RC1 void setup() { attachInterrupt(0, Rc1, CHANGE); } void loop() { }Спасибо за пример с функцией micros. А как теперь привязать переменную rc1_data к определенному PWM входу, например D3 в моем случае?
volatile unsigned int rc1_data = 0; // значение PWM полученное с приёмника volatile unsigned long start_timeRC1 = 0; volatile unsigned long prevTime; volatile byte flag_RC1 = 0; /*******Обработчик прерывания (чтение PWM) *******/ void Rc1() { if (digitalRead(3) == HIGH && flag_RC1 == 0) { //сохраняем значение времени начала импульса start_timeRC1 = micros(); flag_RC1 = 1; } if (digitalRead(3) == LOW && flag_RC1 == 1) { //сохраняем значение длительности импульса канала 1 rc1_data = micros() - start_timeRC1; flag_RC1 = 0; } }//END RC1 void setup() { attachInterrupt(1, Rc1, CHANGE); } void loop() { }Чтение PWM с использованием микрос совсем простое:
нет, еще проще - pulsein
я эту функцию не смотрел, но навряд ли используемое в ней прерывание будет выше INT0 и INT1 если оно вообще используется
Чтение PWM с использованием микрос совсем простое:
нет, еще проще - pulsein
я эту функцию не смотрел, но навряд ли используемое в ней прерывание будет выше INT0 и INT1 если оно вообще используется
Нет, не используется. И не нужно. И если хочешь задействовать аппаратные ресурсы, то это делается на сраным прерыванием и micros(), а таймером.
Нет, не используется. И не нужно. И если хочешь задействовать аппаратные ресурсы, то это делается на сраным прерыванием и micros(), а таймером.
есть у меня и на таймере, данке шон ЕвгенийП, точность там заоблачная получается, но оно тут не нужно
Ох, я наверное уже надоел, но теперь у меня проблема что в вашем коде переменная которой присвается флаг после считывания PWM с D3 int, а у меня объявлена bool "_gtv3"
//Считывание PWM и выполнение условия включения ленты if (_gtv5 == 0) { _gtv3 = (_SCT_1_PDON) < (1000);Как мне теперь увязать ваш код с моей переменной? А то ведь придется весь код перетряхивать(
volatile unsigned int rc1_data = 0; // значение PWM полученное с приёмника volatile unsigned long start_timeRC1 = 0; volatile unsigned long prevTime; volatile byte flag_RC1 = 0; /*******Обработчик прерывания (чтение PWM) *******/ void Rc1() { if (digitalRead(3) == HIGH && flag_RC1 == 0) { //сохраняем значение времени начала импульса start_timeRC1 = micros(); flag_RC1 = 1; } if (digitalRead(3) == LOW && flag_RC1 == 1) { //сохраняем значение длительности импульса канала 1 rc1_data = micros() - start_timeRC1; flag_RC1 = 0; } }//END RC1 void setup() { attachInterrupt(1, Rc1, CHANGE); } void loop() { }надо всё переписывать полностью, ваш код не исправить (практически)
надо всё переписывать полностью, ваш код не исправить (практически)
Понял, примерно так и думал. Спасибо, буду пробовать