NeoPixelBus icon indicating copy to clipboard operation
NeoPixelBus copied to clipboard

Invert brightness values

Open DerSpatz opened this issue 2 years ago • 3 comments

I first opened this issue in WLED, but was told it would fit here better:

I am currently designing small LED modules that can be used as light show for parties, raves etc. The modules are powered by 12V and use two WS2818 ICs. One WS2818 controls three RGB LEDs in series (because 12V), while the other WS2818 is used to control warm white, cold white and UV LEDs (3 each). As the WW, CW and UV LEDs have a much higher forward current (and I need the additional brightness), I am using INK1101 LED drivers (https://datasheet.lcsc.com/lcsc/2202132000_Cross-chip-INK1101ST_C2840169.pdf) for these LEDs. The DIM pin of the INK1101 works like an ENABLE pin and has an internal 20k pullup. Unfortunately, the output of the WS2818 is "active low", so I'd need an additional inverting circuit between WS2818 and the three INK1101s. Board space is limited, additional costs are bad (there's a lot of single modules, so a few cents matter), and I do not have a good power source for the inverting gate (the other ICs all have an input resistor on their positive supply), so I'd rather not try to add the inverters to the design. The RGB data line and the WWCWUV data line are in parallel and independent from each other.

The easiest solution would be a software solution: Invert all the brightness values that are sent by WLED, so that internal brightness=0 is send out as brightness=255 and vice versa.

This is not about inverting the actual output levels on the MCU pin, but inverting the brightness values that are sent to the LEDs.

I assume this could also be useful for other hardware designs that use an external driver IC to run higher powered LEDs, and it could add interesting new twists to existing effects.

DerSpatz avatar Jul 13 '22 10:07 DerSpatz

This can be solved today by "you" the developer just inverting the color values before calling SetPixelColor(). This "issues/request" could be implemented by adding a Invert() method to the RgbColor color object.

While you provided great example on why you need it, I don't understand what your limitations are for you just doing it versus some built in feature. Elaborate?

Makuna avatar Jul 13 '22 16:07 Makuna

While I am pretty good at designing electronics, my programming skills are next to none. I know enough about how software works to design my hardware in way way that it can work with the software, but that's it. I know for someone skilled in programming, adding an Invert() method is a very minor task (at least I assume so), but for me it would mean having to learn programming so that it works without errors. This is why I asked here, and I assume there might some other people who find that useful, too.

DerSpatz avatar Jul 14 '22 14:07 DerSpatz

@DerSpatz Often it is better to start the conversation on solutions in the "discussions area" and once you know what you need then to create an "issue" that is specific and actionable.

And by "limitations" don't think I meant your skills or knowledge; I meant within what limitations of a solution you are using.

Do you really only need the invert? You can do this today by the following code, which I will add at some point to color objects.

color.R = 255 - color.R;
color.G = 255 - color.G;
color.B = 255 - color.B;

or

color.R ~= color.R;
color.G ~= color.G;
color.B ~= color.B;

Makuna avatar Jul 14 '22 16:07 Makuna

Another option is a special Color Feature, like NeoGrbIink1101Feature. Here is an implementation. If you need another color order (like RGB) you can search for the normal and see the differences. What color order are they?

class NeoGrbInk1101Feature : public Neo3ByteElementsNoSettings
{
public:
    static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color)
    {
        uint8_t* p = getPixelAddress(pPixels, indexPixel);

        *p++ = ~color.G;
        *p++ = ~color.R;
        *p = ~color.B;
    }

    static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel)
    {
        ColorObject color;
        const uint8_t* p = getPixelAddress(pPixels, indexPixel);

        color.G = ~(*p++);
        color.R = ~(*p++);
        color.B = ~(*p);

        return color;
    }
    
    static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel)
    {
        ColorObject color;
        const uint8_t* p = getPixelAddress(reinterpret_cast<const uint8_t*>(pPixels), indexPixel);

        color.G = ~pgm_read_byte(p++);
        color.R = ~pgm_read_byte(p++);
        color.B = ~pgm_read_byte(p);

        return color;
    }
};

Makuna avatar Feb 12 '23 19:02 Makuna

@DerSpatz I think my last comment is best option at solving this. Do you know what color order Ink1101 have?

I can wrap this up and check this change in; once I know that detail.

Makuna avatar Mar 12 '23 17:03 Makuna

Hey Makuna, thank you for your effort!

The INK1101 does not have a special color order as it is just a LED driver IC. The color order comes from the IC controlling the three INK1101, in my case this is a normal WS2818B. The inversion is necessary because the WS2818B has an active low output, while the INK1101 needs an active high input. On my PCB, every output of the WS2818B is pulled high with a resistor. You could add an inverting circuit (or inverter IC) after that, of course, but that would add more cost and necessary board space for something that could be done in software.

This option would also work for all other LED driver ICs that need an active high input, in case someone else wants a higher powered addressable LED.

You could call the function "external LED driver" and make the user choose between (no/yes/inverted), while also keeping the usual selection of the LED controller type which sets timing, protocol and color order. "No" and "yes" in this case would not change the output polarity, and would have no direct effect, but maybe "yes" could allow the user to set a different current limit in future versions.

While I am not able to write the code, I would gladly write the documentation for this function once it is implemented!

DerSpatz avatar Mar 12 '23 18:03 DerSpatz

Wait, I thought it was stated it wasn't just an inverted signal.

"This is not about inverting the actual output levels on the MCU pin, but inverting the brightness values that are sent to the LEDs."

There is already support for inverted signal ouput for most uC. Just use an inverted method, NeoWs2812xInvertedMethod.

What uC are you using? Maybe you should provide a schematic of how you are using the INK1101.

Makuna avatar Mar 12 '23 19:03 Makuna

Sorry if I caused some confusion... From my understanding, the usual "inverted" methods invert the signal between uC (like Arduino or ESP32) and the LED controller IC (as in the tiny IC directly on the LED, or a WS2818B). The idea behind this is, as far as I thought, that you can easily add an inverter IC as level shifter to get from 3.3V to 5V.

I need the inversion after the LED controller IC and before the LED driver IC, and that would be done by inverting the brightness values.

Where can I find that NeoWs2812xInvertedMethod ?

Here's a schematic: Screenshot 2023-03-12 202818

DerSpatz avatar Mar 12 '23 19:03 DerSpatz

Ok, that clears it up. So there is no way to tell the WS2812b to invert its output, the only way is to just "think" of the values we set it to as inverted, so as I previously mentioned, you need a new color feature as I listed above. So with this "improved knowledge" the name change for the color feature to just an inverted one.

The NeoGrbInk1101Feature mentioned above would just be named NeoGrbInvertedFeature. And have a basic set of them to match common different color orders.

p.s. This library doesn't use flags very often, it relies on templates that minimize "generated" code and memory (flag requires memory to store, and code to branch on it). The templates just make it all compile out. This library supports uC that are very restricted in memory.

Makuna avatar Mar 12 '23 19:03 Makuna

And you are using these to increase the voltage and amperage support of your LEDs. I thought there were chips (like a WS2818) already out there that did that to some degree.

Makuna avatar Mar 12 '23 19:03 Makuna

And you are using these to increase the voltage and amperage support of your LEDs. I thought there were chips (like a WS2818) already out there that did that to some degree.

The WS2812B allows for higher input voltages, as you can connect three LEDs in series for each pin, but it is still limited to ~20mA of current. With a dedicated LED driver IC, you have much more freedom when it comes to voltage and current, and you could use more efficient switching drivers instead of simple linear drivers.

DerSpatz avatar Mar 12 '23 19:03 DerSpatz

Using just the approach I outlined (using a color feature) won't work for all instances. For example, when using luminance/gamma correction in the NeoPixelBusLg (replacement for NeoPixelBrightnessBus).

Still working on a solution, but I think it has to be done at the same level as gamma correction.

Makuna avatar Mar 13 '23 17:03 Makuna

https://github.com/Makuna/NeoPixelBus/pull/662

To get it to work in all scenarios, it must be applied after gamma correction; so it must either be used in a shader (term specific to this library) or with the NeoPixelBusLg.

If you look at the example for the NeoPixelBusLg (NeoPixelBusLg.ino) you will see how to invert the PWM range output as you need. You will find the following line in the example.

NeoPixelBusLg<NeoRgbFeature, NeoWs2812xMethod, NeoGammaInvertMethod<NeoGammaNullMethod>> strip(PixelCount, PixelPin);

The key part is NeoGammaInvertMethod<NeoGammaNullMethod> where you can replace the specific inner method with the gamma correction you need as this one demonstrates the null method. Like...

NeoPixelBusLg<NeoRgbFeature, NeoWs2812xMethod, NeoGammaInvertMethod<NeoGammaTableMethod>> strip(PixelCount, PixelPin);

WLED will be moving to the new NeoPixelBusLg at some point (they have a branch I believe with it already under test). At that point then the gamma wrapping they do could expose the invert wrapped.

Makuna avatar Mar 13 '23 23:03 Makuna

v2.7.4

Makuna avatar Apr 11 '23 17:04 Makuna