NeoPixels Revealed: Getting physical to uncover PWM Secrets

This is Part 2 in a series. Read Part 1 here.

 

I stuck a photo-detector on a NeoPixel to get an insider’s look at exactly how they do their PWM

Getting physical with a NeoPixel

Looking at the photo-detector output on a scope at a 500us timescale, the PWM of the LED shows up perfectly – it is running at about 500hz (~2ms per cycle).

Here is the LED showing the lowest brightness of 0x01…

LED output at minimum brightness (0x0x1)

LED output at minimum brightness (0x01)

…and at half brightness of 0x80…

LED output at half brightness (0x80)

…and finally full brightness of 0xFF…

LED output at full brightness (0xFF)

LED output at full brightness (0xFF)

 

We can see that the LED is never continuously on. Even when the color is set to full brightness, the LED still turns off for about 100us at the end of each PWM cycle. This means that there will always be some flicker with a Neopixel, although nothing noticeable to human eyes.

This PWM clock runs independently from the data coming in. This has some practical consequences. It means that there is up to ~2ms of jitter between when you latch new data and when it actually shows up on the LED. Here are a couple of extreme examples…

Here we see the LED come on almost immediately after the data stops

A lucky latch with very short latency

Here we see the LED come (top trace) on almost immediately after the data stops (middle trace). We just got lucky that the PWM cycle was ending just as we finished sending data.

This time there is delay of 2ms between the ned of the data and the LED coming on

Almost worst case of >1500ns of latency between latch and display

This time there is delay of >1.5ms (1,500,00ns!) between the end of the data and the LED lighting up. We just missed the end of the previous PWM cycle and so had to wait for the next one.

There is no practical way to avoid this jitter. Some implications include…

  • the shortest flash you can make with a NeoPixel is ~2ms. This rules out using an array of NeoPixels as a high-speed strobe flash.
  • if you try to show a frame of pixels for less than 2ms, there is a real chance that you might completely miss a PWM cycle and they will never actually be displayed at all. This practically limits your maximum frame rate before frames start getting dropped.
  • since each pixel has a free-running PWM clock,. there will always be up to ~2ms of time between resets when some pixels in the same string will be displaying new data while others are still displaying old data. This could cause problems like motion tearing and dithering if you take a photo of a changing Neopixel display using a 1/500 second or shorter exposure time.

So now we can detail exactly a frame refresh works…

  1. Every time the data signal falls from high to low, a countdown timer starts. The data line going from low to high stops the countdown.
  2. About ~6us after the reception of the last bit, the countdown timer expires and the newly received color data is latched into an internal buffer.
  3. At the end of the next asynchronous  PWM cycle (which could be anytime in the next ~2ms), the chip grabs the data from the buffer and starts displaying it on the LED.

FAQ

Q: Couldn’t I use a photo-detector or current probe to lock into the PWM clock and then make sure my data always ends just before the next PWM cycle starts?
A: Keep in mind that every pixel has its own independent PWM clock and they are all free-running and all have different periods due to manufacturing and environmental differences. So you probably will only be able to get your data to be optimally timed for a single pixel in the string.

8 comments

  1. tReg.

    “the shortest flash you can make with a NeoPixel is ~2ms. This rules out using an array of NeoPixels as a high-speed strobe flash.”
    No, it’s 2/255ms, though you can flash only once every 2ms, and won’t get much light and can’t synchronize several WS281x to have more.

      • Chris

        I suppose by using the 0x01 value and not the 0xff one…

        Was a nice read anyway. Thanks.

        Came here through Google because I was trying to figure out how to get the data transmission without bit banging but using the built-in PWM generator to create the 1 and 0 signals. Because then I maybe could make the data transfer interrupt driven. I wanted to find out if somebody already tried that.

        • bigjosh2

          The Octo library does some very fancy footwork to drive NeoPixels on his TEENSY ARM board. He uses DMA to select between PWM generators that actually generate the bits.

          I think I can do it on an ARM (RaspPI) without the PWM part by just driving the GPIO registers directly using DMA, but I haven’t actually tried yet. I think it will all come down to being able to get the DMA to trigger with low enough jitter. I’ll let you know!

  2. Ameno

    I wonder if we can get the pixels to naturally sync up, either with some repetitive pulsing at warmup, or some trickery caused by signal interferences on the power line or input signals.

    • bigjosh2

      There are plenty of ways, but all the ones I can imagine are complicated and expensive. If you need LEDs that are fast and synced at >~100Hz, then NeoPixels are probably not the right answer. But they are great for normal stuff to be seen by human eyes!

  3. Oliver Street

    You may want to follow up on how independent the PWM clocks turn out to be, it’s difficult to prevent oscillators from injection locking to each other, but there is likely to be an accumulating phase shift and there is also a re-clock propagation delay for the reset signal from chip to chip, so it may or may not be possible to sync to the pwm clock rate with a phase offset that achieves a simultaneous update for a usefully large number of pixels.

    • bigjosh2

      The phase difference among pixels on the same string do not seem to ever converge. In fact, it seems to increase from the moment that the pixels are powered up (start start in phase) until they diverge completely. The circuitry that decodes a new color from incoming data seems to run independently from the state machine that actually latches the new color on each PWM cycle.

Leave a Reply