driving WS2812 programmable RGB LEDs using hardware SPI

There is quite some interesting information on the internet already on driving WS2812 programmable LEDs already:

eliaselectronics.com

 

But the most interesting one I found was this one:

cpldcpu.wordpress.com

Especially the timing tables it contains, and especially the concluding one, which I took the liberty to copy here:

ws2812_timing_table

 

The WS2812 has a single data signal that provides both a clock and data. A zero bit is encoded as a short pulse and a 1 bit is encoded as a longer pulse. In order the differentiate them the zero pulse should not exceed a certain length and the 1 bit should be minimal a certain length.

This got me thinking how this easily matches on hardware SPI.

The idea is to use 4Mhz SPI and send 1000000 for a zero and 11100000 for a 1. Given that a bit at 4Mhz is 250 ns this gives a 250 ns pulse for a zero and a 750 ns for a one. Looking in the table this sits comfortably inside the timing window allowed by the WS2812 LEDs.

In practice this gives something like this for the zero bit:

 void zero_bit() {
spi->TXD = 0x80;
wait();
}

and something like this for the one bit:

 

void one_bit() {
spi->TXD = 0xE0;
wait();
}

The reason for the wait is that the particular ARM Cortex-M0 I’m using here does not expose DMA so I cannot use DMA to just send an entire frame asynchronously at once, but I need to wait a little bit to allow the MCU to push out the byte before the next one is entered. This could be done by busy looping on a flag but just some NOPs to wait seemed simpler to me:

static inline void wait() {
__ASM (
” NOPnt”
” NOPnt”
” NOPnt”
” NOPnt”

” NOPnt”
” NOPnt”
” NOPnt”
” NOPnt”

” NOPnt”
” NOPnt”
” NOPnt”
” NOPnt”

” NOPnt”
” NOPnt”
” NOPnt”
” NOPnt”
);
}

I’m using this code in practice and it works just fine and stable, tested with this adafruit neopixel strip:

1426-00

(image is copyright adafruit)

No Comments

Sorry, the comment form is closed at this time.