Yona-Appletree / LEDscape

Beagle Bone Black cape and firmware for driving a large number of WS281x LED strips.
126 stars 58 forks source link

More granular pixel data possible? #34

Closed mykolasmith closed 9 years ago

mykolasmith commented 9 years ago

Hi,

I'm wondering if it's possible to have greater control over pixel granularity, meaning uint16 [R,G,B] values as opposed to just 0-255? I believe the OPC protocol could be sufficiently modified to accept greater than uint8 values. I guess what I'm asking is: can the PRU do more granular stepping and does the WS2812 spec support it?

Sorry if this is the wrong place to be asking!

RGB-123 commented 9 years ago

mykola, we are limited by to 8 bit step because of the WS2812 chips themselves.

Yona-Appletree commented 9 years ago

Ryan is generally correct -- most addressable LEDs use 24bit color, which itself is linear, not logarithmic, so you get more resolution at the higher brightness levels. Internally, LEDscape does use 16bit color for the interpolation, dithering and luminance scaling, but I doubt you would see much improvement in your animations if we exposed this, and it would come at a fairly high cost in terms of complexity and bandwidth.

As such, I am going to close this slip, though I am open to pull requests if you are motivated to implement support which has a demonstrable improvement in quality and does not complicate the more common 8-bit case.

mykolasmith commented 9 years ago

@Yona-Appletree: the reason I asked was particularly due to resolution at lower brightness levels. Thanks again for the awesome software (@RGB-123: and hardware too!)

Yona-Appletree commented 9 years ago

It's worth noting that LEDscape does do interpolation and dithering at lower brightness levels by default that should give fairly good (linear) results when run with few enough LEDs to have high frame-rates. For best performance, we suggest not using more than 256 LEDs per strip, with better results at 128 LEDs.

mykolasmith commented 9 years ago

@Yona-Appletree I note that on the readme, it says: "256 per channel ~= 120 fps". Right now, my use case is 300 per channel x 48. I would consider cutting all my strips down from 5 meters if it meant getting to within 256 meant noticeably better low end performance. Also, would sending frames at 60fps vs. 120 make any difference?

Yona-Appletree commented 9 years ago

The rule is pretty simple -- fewer LEDs = higher framerate = better low-level control. I would play around with different numbers of LEDs and see the difference for yourself. You can easily tell LEDscape to send less data with -c COUNT. Try 64 LEDs -- that's about the best it will look. Try 128, see how that is. Then you can cut the strips to your liking and add another BBB, if that's worth it to you.

bigjosh commented 9 years ago

I humbly disagree that higher frame rates always give finer low brightness control, at least for WS281x pixels at high frame rates.

Try this experiment:

  1. configure ledscape for just one single LED (on one pin)
  2. set that LED to RGB color 000500 (very dim green)

Because you only have one LED, you should be seeing >4,000FPS, but the green LED is visibly flickering. This is due to the way the silicon on the LED is implemented. It has an internal state machine that only updates the actual PWM value from the data_in register displayed about once every 1-2ms. If you update the data_in register more frequently than about every 4ms, the displayed PWM will be an alias of the dithering rate. You can get more details here, but the short answer is that higher FPS is not always finer control. I think a good strategy to deal with this is to add a usleep in the render loop after a set of pixels is drawn. The value of the usleep will be determined by how long the draw took, and it will extend the time the LEDs have to actually latch and display the values just sent to them out to a configurable minimum (~10ms works well for WS2812Bs). LMK if you want a pull request for this functionality. In the mean time, just to prove I am not a crack pot you can just manually add a usleep( 10000 ); at the bottom of the render thread loop and try the above 1-pixel green test again.. :)