Aircoookie / WLED

Control WS2812B and many more types of digital RGB LEDs with an ESP8266 or ESP32 over WiFi!
https://kno.wled.ge
MIT License
14.66k stars 3.15k forks source link

PWM enhancements (phase shifting and dithering) #4115

Closed DedeHai closed 3 weeks ago

DedeHai commented 1 month ago

TODO:

DedeHai commented 1 month ago

Looks quite complicated. 😄 I think you could forego phase shifting for anything but PWM CCT. And simplify that to always shift 180°.

your call, the code is already there :) adding the phase shift is getting the timers in sync (3 lines of code) and calculating the offset (4 lines of code). The CCT offset right now is calculated such that the pulses are best distributed (middle of off time of first pulse) i.e. 180°

blazoncek commented 1 month ago

I've updated the PR to include changes in PinManager. I do not understand why did you change PWM frequency logic in the constructor. There are odd and unexplainable values in the switch statement. Can you elaborate?

DedeHai commented 1 month ago

I dont fully get what you mean. I left the original calculation as changing that somehow broke proper saving/loading cfg in my tests and used the saved frequency values to calculate new ones, as the comment states: just a POC. Instead of calculating and saving the frequency, I would save the selection directly and calculate the frequency where it is used (in the show() function). what odd and unexplainable values are you referring to?

DedeHai commented 1 month ago

The frequencies chosen are preliminary and just a suggestion from my side. When dithering (4bit) is used, the lowest 16 PWM values reduce the frequency as the minimum pulse width is set by the 8bit 'actual' PWM pulse. At the lowest value of 1, the frequency drops by a factor of 16 (I see minimal flickering in slowest mode on 60FPS phone camera as the light is really dim in that setting, I read that you need at least 30x the FPS rate of the camera in order to get it flicker free, not sure how accurate that is).

In my tests using a FET module (dual AOD514 from Ali) I found that a GPIO can not drive it any faster than a 400ns pulse, below that, the output would not switch on (still need to get some IRLZ44n to check how they perform). So I chose frequencies that respect that minimum pulse (slowest and slow setting). In general, even when using a decent driver, pulses below 50ns are hard to do with power NFETs (unless you go for GaN or SiC technology but that is some advanced stuff).

These are the chosen settings:

the last one could also be dropped to 20kHz i.e. the WLED_PWM_FREQ (I just chose it to be different so I could use it to ID that mode as I had no other variable available).

I would also suggest a renaming of "slowest", "slow", "normal", "fast" and "fastest" but I have no good suggestions. Since I expect few users to read any documention on the new PWM settings, it should be clear what they are intended for.

I am open for suggestions on frequencies and bit depth / dithering bits (I assume also 2 and 3 bit dithering will work with some minor modifications).

blazoncek commented 1 month ago

When dithering (4bit) is used, the lowest 16 PWM values reduce the frequency as the minimum pulse width is set by the 8bit 'actual' PWM pulse. At the lowest value of 1, the frequency drops by a factor of 16

I am not sure I follow. Let me try to explain how I see "dithering" (that will extend pulse width fo our purposes).

Having 12 bit resolution means there are 4096 different pulse widths possible (minus 2 if we take 0 and 4095 out). Each pulse (regardless of its width) happens within a period of PWM frequency (i.e. 19531 Hz or 51.2 us). The shortest pulse will therefore be 12.5 ns. Since not all FETs are made equal (some cannot handle such short widths/steep rises) we want to "extend" those widths without loosing "perceived" smoothness of brightness steps introduced by lower bit depth (8 bits will yield minimum pulse width of 200 ns). How can we achieve that? By using persistence of human eye and reducing the bit depth but alternating the least important bit in time. As the eye will average out those on/off alternation of the lowest bit, the perceived brightness will be less than the bit depth would allow. All this happens regardless of the chosen PWM frequency and only depends on the bit depth and timed distribution of lowest bit alternations. The downside is that we need 16 cycles to reproduce those 16 lost possible values by using 4 bits less for depth.

Is my understanding so far correct? If yes, then I see no point in changing the frequency as we already "extended" pulse width to a reasonable width by reducing bit depth (if your FETs are still too slow, you can use slower frequencies from the chosen list). So it is just a matter of correctly alternating that lowest bit in time to achieve "perceived" resolution of 12 instead of 8 bits.

FYI I have 2 IRLZ44N on my desk, attached to 2 5V 3W analog LEDs WW & CW. I am using level shifter for driving FETs and they perform adequately at 12 bit at 19531 Hz. I can see brightness changes down to global brightness of 1.

blazoncek commented 1 month ago

As a solution for your other problem (UI option) I would go with "Use dithering" checkbox. If enabled that would force 8 bit depth (regardless of the chosen frequency) but would use "dithered" output.

DedeHai commented 1 month ago

your understanding seems correct. let me put in other words: the dithering is a temporal dithering, any 8bit value is extended to 12bits by dividing the 8bit pulse into 16 (the number of 'spaces' between two 8 bit values) time slots. if the 'missing' bits for example are '5' out of 16, 5 of the 16 pulses will be increased by one 8-bit minimum pulse length. This then his averages out to 5/16th of a pulse length hence preserving the full resolution (perceived). you are right that we could keep the current frequency values, but I chose to change them such that the slowest settings result in a 400ns pulse (for the FETs I have at hand, even with a levelshifter the minimum pulse was 250ns, anything below that was just the same as 0, defeating the 12bit resolution which mostly helps in the lowest levels) We need to keep in mind that to the average user 'dithering' means nothing. As I mentioned, my approacht would be to have 5 or 6 settings, that are like 'presets' for different setups, the users will choose what looks best for them (if they even care) for whatever setup they have. My approach is not the best in versatility though.

blazoncek commented 1 month ago

We need to keep in mind that to the average user 'dithering' means nothing.

There is a simple answer for that: If your LEDs turn off at higher brightness levels (i.e. too soon) use dithering or try lower frequencies or both.

blazoncek commented 1 month ago

Temporal, that was the word I was searching for. 😄

blazoncek commented 1 month ago

but I chose to change them such that the slowest settings result in a 400ns pulse

The "slowest" frequency already is half of WLED_PWM_FREQ which is 9765 Hz that will produce 400 ns pulse with 8 bit resolution. Or does it not?

DedeHai commented 1 month ago

slowest is indeed the same. I just got some IRLZ44n in the mail today btw, will check how they perform. I have no objections on keeping the frequencies and adding a dithering option, my point is just that we could pre-define useful settings and make it less complicated. The slow 10kHz setting with dithering and no driver is absolutely fine for lighting, just might not be for fast moving setups or video lighting with high frame-rates. Just remember: WS2812 do PWM at 400Hz.

blazoncek commented 1 month ago

As I've commented on Discord I finally understand how this should work. Let me explain.

We should talk about 2 different things: phase shifting and dithering.

Lets start with phase shifting. Phase shifting is mandatory for reverse polarity CCT strips that need a H-bridge to work. For all other situations it may be useful but is not obligatory. It also gets complicated to calculate appropriate phase shifts the more GPIOs you have. So I would reserve phase shifting for CCT only strips and always do 180° shift. One prerequisite is that CCT blending has to be 0 as otherwise WW & CW signals may overlap which is forbidden for H-bridge. We can add a bit of dead time when individual channel brightness reaches 50% if we cannot properly sync timers.

Dithering. After studying Espressif documentation there really is nothing to it beside the fact that it's hidden behind writing directly to LEDC registers. As such I think this should be an option in UI for user to select (instead of phase shift which shoul always be enabled for CCT). When selected the only difference to current implementation (using ledcWrite()) is to reduce the calculated _depth to 8 bits (fixed) and shift pwmBri by <<4 when not using dithering when writing to duty register. This approach simplifies logic tremedously by only having 2 or perhaps 3 single statement ifs. Frequency should (and will) remain as is. The only exception may be redefinition of WLED_PWM_FREQ if the fastest frequency (10/3) cannot produce output at lowest brightness (too short pulse). Unfortunately I do not own a scope or logic analyzer so will depend on external verification.

I will update bus-config branch with the above some time later.

blazoncek commented 3 weeks ago

Closed in favor of #4126