Approach to 8 channel bit banging

bit-bangled strippic

I'm looking at controlling some WS2812 5050 RGB LED strips from a microcontroller. I've successfully played with the Adafruit Neopixel library and I've written some code on a PIC18F2455 which I've also gotten working for some simple things (progressively change colour from red, to green to blue, and back etc).

However, all of that was for a single string. Ideally, since I'm using PORTB on the PIC micro, it'd be great to use all 8 bits to drive up to 8 strings from the one chip.

Yes, I know I'm getting greedy here 🙂

My question is, what approach would you recommend to take given that I have at most the timing involved for sending a 1 or a 0 on any individual channel is a few instruction cycles and the signalling protocol is non-standard 1-wire type protocol so the PIC doesn't have a dedicated peripheral to offload the work to (sending a 1 involves 'write 1, nop, write 0' and sending a 0 involves 'write 1, write 0, nop').

Additionally, there's probably only at most a hundred instruction cycles between the end of one bit at the start of the next before you hit the 50us "end code" and everything you've been writing out gets latched onto the LEDs and the data protocol resets back to waiting for the first bit again.

For a single string I've just been taking the three bytes for Green, Red, and Blue (that's the order these things use) and doing 24 "if (green & 0x80) write1(); else write0();" etc statements. But clearly that exact same approach isn't going to work for 8 bits at once.

Some options I considered:

  1. Computing a byte based on the first bit of the green value, then using carefully crafted assembly to (a) write 0xff to the port, (b) write the computed byte to the port, and then (c) write a 0x00 to the port. Rinse repeat 23 more times for the first LED on each of the 8 strings, then repeat again for however long it takes to output the entire string. Only problem is all that computing takes a fair amount of cycles and it's quite possible to take so long it ends up interfering with your output.

  2. Instead of storing each of the led string data as an array of GRB byte values and then computing them at output time, store them as a "smeared" bit array (eg, first string's LED 1 data is stored across 24bytes of memory in the first bit of each byte, second string's across the second bit of each byte, etc). Advantage is outputting is dead easy and fast, disadvantage is the workload is moved to the creation part of the process and you now need functions to get or set individual values.

Thoughts? Anyone have any reasons why you would or wouldn't do any of the above? Anyone know of any clever hacks to quickly "flip" an array of 8 bytes so byte 1 becomes the first bit of the 8 bytes in the result? 🙂

Best Answer

If changes of the values are rare (relative to the running of the LED update protocol) the second approach is clearly lighter on the CPU (and hence results in faster communication).

You might want to google for "vertical counters", which uses a similar approach to spread the bits of a number of counters 'vertically' over a number of bytes, with the aim of making a fast set of counters. I think I first heared of this from http://www.dattalo.com/technical/software/pic/vertcnt.html