Open florenso opened 3 weeks ago
You can resolve this using additional circuitry and stock WLED.
what kind of circuitry? i could add a xor circuitry to one of the pwm signals, but even they would be in the same phase...
Differential output comes to mind. You do not need phase shifted output for that.
Please use WLED forum or Discord for help and support question.
Correction. Using H-bridge (a mandatory circuit in this case) will require phase shifted PWM.
@jw2013 claimed he had a working solution for phase shifted PWM and exactly the same use case.
That's correct, I also needed a solution to run 'polarity change LED strings' via WLED. As @blazoncek mentioned, using a H-bridge is mandatory, and it needs to be controlled via phase shift PWM. That's the complicated part. Back then, I got it working on ESP8266 only, using an interrupt handler. The relevant working code looks like this:
unsigned char busPwmIntRoutine1Pin1 = 255;
unsigned char busPwmIntRoutine1Pin2 = 255;
unsigned char busPwmIntRoutine1Value1 = 255;
unsigned char busPwmIntRoutine1Value2 = 255;
void ICACHE_RAM_ATTR busPwmIntRoutine1() {
if ( digitalRead(busPwmIntRoutine1Pin1) ) {
GPOC = 1 << busPwmIntRoutine1Pin2;
}
else {
GPOS = 1 << busPwmIntRoutine1Pin2;
}
}
void analogWriteCombined(unsigned char gpio1, unsigned char gpio2, unsigned char value1, unsigned char value2) {
if ( (gpio1 == 0xFF) || (gpio2 == 0xFF) ) return;
if ( (busPwmIntRoutine1Pin1 == gpio1) && (busPwmIntRoutine1Pin2 == gpio2)
&& (busPwmIntRoutine1Value1 == value1) && (busPwmIntRoutine1Value1 == value2) ) return;
busPwmIntRoutine1Pin1 = gpio1;
busPwmIntRoutine1Pin2 = gpio2;
busPwmIntRoutine1Value1 = value1;
busPwmIntRoutine1Value2 = value2;
if ( (value1 == 0) || (value1 == 255) || (value2 == 0) || (value2 == 255) ) {
detachInterrupt(digitalPinToInterrupt(gpio1));
analogWrite( gpio2, value2 );
analogWrite( gpio1, value1 );
return;
}
unsigned int value = value1 + value2;
if ( value < 253 ) { // !!!D*RTY WORKAROUND: use overlap instead of gap
unsigned int overlap = 253 - value;
analogWrite( gpio1, value1+overlap );
analogWrite( gpio2, overlap );
attachInterrupt(digitalPinToInterrupt(gpio1), &busPwmIntRoutine1, FALLING);
}
else if ( value > 255 ) {
}
else {
analogWrite( gpio1, value1 );
digitalWrite( gpio2, LOW );
attachInterrupt(digitalPinToInterrupt(gpio1), &busPwmIntRoutine1, CHANGE);
}
}
Later I found that there also exists a phase shift PWM API for ESP8266 only, but I never rewrote the WLED code (more below).
As motor drivers, depending on the voltage, I used either DRV8833 (3V to 10V) or DRV8871 (6.5V to 45V). Most polarity change LED strings use 31V, I assume that's also the case for you?
It would also be possible to use darlington drivers like L293D and L298D, but those are ancient and cause a big voltage drop.
It's funny that the question came up today, as right now I'm working on a completely different approach, that will be portable, and supports more channels:
https://www.wemos.cc/en/latest/d1_mini_shield/hr8833_motor.html (3V to 10V) https://www.wemos.cc/en/latest/d1_mini_shield/at8870_motor.html (6.5V to 38V)
OOTB, those motor shields are only usable via the LOLIN_I2C motor library, which does not expose the functionality required for phase shift PWM. So I started to develop my own firmware for the onboard microcontroller STC8H1K08, to make it behave similar to the PCA9685 :-)
I already got some working code. If others are interested, I'll consider making this open source. BTW: The same motor drivers, using that firmware, could also drive non-addressable RGBW LED strips, with phase shift functionality included. Just saying ;-)
The 'official' way to create a phase locked PWM can be found here, at least for ESP8266: https://github.com/esp8266/Arduino/blob/master/cores/esp8266/core_esp8266_waveform.h https://github.com/esp8266/Arduino/blob/master/cores/esp8266/core_esp8266_waveform_phase.cpp
IIRC, one needs to call enablePhaseLockedWaveform(), to make the linker use core_esp8266_waveform_phase instead of core_esp8266_waveform_pwm.
There are currently two people interested into this @paolotk and @dedehai.
@dedehai & I have a POC ready (untested). If you want a binary, contact me on Discord.
The video below shows an AT8870 motor shield (custom firmware), connected to a polarity change fairy light. Please excuse the quality, it's running at 240 Hz (240 Hz * 255 microsteps = 61200 interrupts per second), still my cellphone records a lot of flickering. Looks perfect for the human eye though :-)
https://github.com/user-attachments/assets/17e78ae5-0a72-4a4b-a9f8-fd31665a9705
Just tested the idea on a (analog) 12V RGBW strip, 5m with 108 LEDs/m. Works perfectly, too. Now going to read https://github.com/Aircoookie/WLED/pull/4107 regarding a proper implementation as a bus.
@jw2013 you may want to check bus-config
branch to use future implementation. @paolotk was also instructed to do so.
That branch already includes PWM phase shifting POC by @dedehai though it is currently flawed but does work.
working (but not final) implementation: https://github.com/Aircoookie/WLED/pull/4115
@blazoncek, referring to https://github.com/Aircoookie/WLED/blob/bus-config/wled00/bus_manager.cpp#L687
The existing Bus classes are hardcoded in the BusManager::add() method. How would I correctly add support for an I2C based PWM bus, which does not use any pins, but makes use of the global 'Wire' instance?
Regarding the settings_leds.html and const.h, I'd like to use something like that:
<option value="75">I²C Polarity</option>
<option value="76">I²C White</option>
<option value="77">I²C CCT</option>
<option value="78">I²C RGB</option>
<option value="79">I²C RGBW</option>
@jw2013 that's something similar like @netmindz is doing with hub75 support.
bus-config
branch is supposed to provide that in the future. ATM it is still a POC waiting for enhancements.
Other parts of WLED currently require at least one GPIO to be allocated to function properly.
But this has nothing to do with this issue so please open a new one. Better yet, join discussion on Discord.
@florenso please use bus-config
branch and report if the LEDs are working.
You will need to enable "Phase shift" in settings. And make sure you have short-circuit protection (i.e. fuse) in place just in case.
Is your feature request related to a problem? Please describe. i have a led strip that contains cold and warm leds, but it only has two wires.
Describe the solution you'd like I want two pwm channels (cc, cw) that are never high at the same time. so that i have a option to configure this type of led strip.
Describe alternatives you've considered Use the led strip with only warm or cold light.
Additional context I know a lot of chips cant handle phase shifting, but if you support it only for the easy ones (esp32 for example) i would be verry happy! (because i already solderd one..., would be awsome to add this functionality)