There are a lot of LED strips, including RGB ones, but the most recent trend is strips in which each individual pixel can be addressed individually. These RGB LEDs include the controller inside the LED enclosure itself, and one of the most popular is the WS2812B:
A nice and small 5×5 mm unit with just 4 pins: 2 for power, data IN, and data OUT.
But these WS2812B LEDs do require fairly precise timing. Even a hick-up of just a few microseconds can be enough to mess up the readout when driving a whole string of them.
Let’s find out whether we can drive these “NeoPixels” with an LPC810, eh?
The timing requires a series of “1” and “0” signals of either 400 or 800 ns. The precise timing is not too critical though – we could use a 2.4 MHz clock, i.e. a 417 ns cycle time. This would match up nicely with the µC’s 12 MHz internal clock, as simple divide-by-5.
There is no way to toggle each individual bit in software, but luckily, we don’t have to. The SPI hardware can send out bits at the requested rate with little overhead. But we need to send 3 on/off periods of 417 ns each, and that doesn’t fit nicely into either 8 or 16 bits.
The solution is to set the SPI hardware to 12-bit “words”, and to look up these words via a small constant table. Here is the main code of the leds demo on GitHub:
static void spiSend (uint16_t cmd) {
while ((LPC_SPI0->STAT & STAT_TXRDY) == 0)
;
LPC_SPI0->TXDAT = cmd;
}
static const uint16_t bits[] = {
0b100100100100,
0b100100100110,
0b100100110100,
0b100100110110,
0b100110100100,
0b100110100110,
0b100110110100,
0b100110110110,
0b110100100100,
0b110100100110,
0b110100110100,
0b110100110110,
0b110110100100,
0b110110100110,
0b110110110100,
0b110110110110,
};
static void sendByte (int value) {
spiSend(bits[value >> 4]);
spiSend(bits[value & 0xF]);
}
static void sendRGB (int r, int g, int b) {
sendByte(g);
sendByte(r);
sendByte(b);
}
Sending 12 bits @ 2.4 MHz takes 5 microseconds. If we were to set the LPC810 to run at its maximum 30 MHz clock, we could probably drive the SPI hardware entirely via interrupts, but for a simple timed example this is not really necessary. Instead, we can let the SysTick timer interrupt the polling loop used to drive the SPI bus – without losing track of time.
Note: we do need to set LPC_FLASHCTRL->FLASHCFG = 0 for just a little more speed.
Let’s turn to the hardware side of things. Here is an LPC810 tied directly to a 60-LED ring (hmmm … a clock display, anyone?), as available from Watterott:
Just for kicks, here is the minimal circuit needed to make it all work – just 3 components!
The supply side of things is the same as before: a small red LED to drop the voltage from 5V to 3.3V (hidden inside the DIP socket) and a 0.1 µF decoupling capacitor.
Surprisingly, no extra decoupling is needed, despite the high current switching involved. The only tricky parameter seems to be the supply voltage: if this drops under 5.0 .. 5.1 V, the data IN voltage level might not be sufficient to reliably trigger the chain of LEDs.
The “leds” demo firmware implements a little comet-like fading motion, chasing around the ring. For a little video showing this animation in action, see: comet-ring.mp4
Total code size is 628 bytes. Plenty of room for a lot more neat animations – go wild!
[Back to article index]