Sunday, January 20, 2013

Fun with RGB LED Strips - Part 5: Memory Optimization; SMD5050 Data

Memory Optimization

First off, last night I took a moment to refactor code to transmit two NRZ bit patterns per SPI byte (the memory optimization I mentioned at the end of Part 3). That worked just fine, halving the memory requirement for the buffer used to hold outgoing pattern bytes.

All timings I've discussed so far still apply except for SPI bit rate. The SPI baudrate is now 3.2031 MHz, half of what it was. To achieve this, the SPI baudrate prescaler must be set to 16 (for SPI1) or 8 (for SPI2 or SPI3).

Now, instead of transmitting 8 SPI bytes (8 NRZ bit patterns, each 8 bits wide) per one data byte to be sent to the WS2811, I'm transmitting 4 SPI bytes (still 8 NRZ bit patterns, but only 4 bits wide).

This figure illustrates the transmission of a zero NRZ bit pattern followed by a one pattern:


SMD5050 Data

The SMD5050 contains an integrated WS2811 with high-speed mode (800 KHz) enabled. The only real 'gotcha' is that the RGB LED chips in the package are NOT all connected to the appropriate WS2811 RGB outputs: instead, R and G are swapped. I imagine some EE goofed, bleary-eyed from an extremely late night partying with a couple of hot assembly techs from manufacturing, and the blunder was not discovered until QA got their hands on a sample from the first million that came shooting off the line. At that point, of course, it'd be too late to do anything about it. (How does one say, "ooops, my bad," in Cantonese?)

Excepting the R/G swap, one can rely solely on the WS2811 datasheet (poor translation notwithstanding) to figure out how to get data to the chip. Here's the conceptual process:

1) Given a 3-byte RGB triplet, send 24 bits to the SMD5050 in G-R-B byte order, MSB first for each bit; send each bit using the '0' or '1' NRZ waveform;

2) Repeat (1) for each RGB triplet to be sent (corresponding to the number of chained SMD5050 LEDs);

3) After all triplets have been sent, hold the data line low for at least 50 µs before repeating the process for the next "refresh".

My implementation of the process goes like this:



In the Tx Data buffer, the single 0x00 byte header ensures that the data out line starts off low; the 21 byte 0x00 trailer ensures that the data out line stays low for at least 50 µs before any new data gets sent (this is the reset condition that causes all SMD5050s in the chain to update their PWM outputs to the RGB values they've just received and start looking for new data).

4 comments:

  1. Wow I'm glad I found your blog! Your explanations are really good and helpful for me. I also bought a WS2812 digital LED strip and I am wondering how to adress them. Now i found out the answer I think! I am using a TI Stellaris Launchpad with a M4F120 MCU on it (Cortex M4 as well). I am still getting used to it because its my first time with a non-AVR. Thanks again!

    ReplyDelete
    Replies
    1. I'm glad you found it useful! I'm not actually certain what the difference is between a WS2811 and a WS2812. The datasheets I've seen appear to be largely identical. I presume you're planning on using one of the SSI ports on the LM4F120. Best of luck with it!

      Delete
    2. AFAIK WS2811 is the IC which also exists with DIP/SOT package, and WS2812 is the 5050 LED with included WS2811 in the LED.

      Anyway, I'm happy that I made my strip light up with the method you discribed in your blog. Again, you have been a great help. I used the SSI Ports running in classic (Motorola Freescale) SPI mode. An interesting detail is, that you dont have to keep the exact timing which is descriped in the data sheet. The LM4F120 somehow does not have that many prescale options like your STM32F4 (or I couldnt find them), so my SPI clock is 3.33 Mhz which is the closest to the ideal 3.2 Mhz. It was running perfectly, which made me curious to test other clock frequencies. The result was, that the LED runs well from 2Mhz to 4Mhz, above 4Mhz some LEDs didnt get correct signal anymore.
      Although the data transfer was OK, I had issues with flickering all over the strip. After all, a pull-down resistor between 'din' and ground solved a problem. Did this problem occur with your setup?

      How did you create your animation? I wrote a routine which I dont consider as very effective. Your animations look really good, do you mind to share some code?

      Beside that, my next step will be the DMA thing, I never used that before :)

      Delete
    3. Interesting about the timing... I didn't play around with the timing specifics so much - I started off by trying to get as close as possible to the (apparent) specified timing, and when it worked, left it alone. :-)

      I did experience some flicker issues at first, when I was impatient and tried to drive the strip directly. Quite frankly, I don't remember exactly what I did - though I think I added a small resistor in-line and a cap to cut down on ringing.

      I'd be happy to share my code. I've enabled my email on my profile - if you send me a message, I'll send you a copy of it.

      The color animation used a very simple HSV-to-RGB conversion, with no correction for perceptual color.

      Delete