Pryankster
New member
I'm building an LED lighting project for my PC. My motherboard has a single addressible RGB port and knows "natively" how to drive WS2812 LEDs. I have several devices that I want to hook up that don't have the ability to "daisy chain", so I want to make a device that allows me to connect up multiple 2812 strands (or maybe even other kinds of ARGB, like DotStars. I envision a USB packet to configure the lengths of the output strips, and then using the teensy to direct the incoming pulse trains to the various outputs. This could be used, for example, to control the lights on multiple ARGB fans. My back-of-the-envelope design uses Paul's SERIAL/DMA based WS2812 code on the output side, but could the serial input be used on the input side?
Something like this:
The reason that I want to buffer the whole chain is two fold: I may want/needs to swizzle the send bytes to compensate for different RGB ordering, and/or, direct some of those bytes into APA/Dotstar output. (which is much more forgiving than 2812)
Am I completely barking up the wrong tree? does this seem feasable?
-- pryankster
Something like this:
Code:
int outputChainLength[8];
int numChains;
uint8_t *outputBuffer[2];
void init()
{
// this would be configured VIA a USB control packet, here for illustration.
outputChainLength[0] = 16; // case lights
outputChainLength[1] = 8; // a fan
outputChainLength[2] = 0; // nothing plugged in to output 2.
outputChainLength[3] = 8; // another fan.
numChains = 4;
// count up total LED length ...
for (int i = 0; i < numChains; i++ ) {
numLEDs += outputChainLength[i];
}
// double buffer output. (maybe need triple buffer?) that could be a lot of RAM.
outputBuffer[0] = calloc(numLEDs * 12);
outputBuffer[1] = calloc(numLEDs * 12);
}
void recvRGB()
{
// forever, read LED data from motherboard -- it thinks it's talking to WS2812s.
while(1) {
// read in all RAW WS2812 control bytes, (12 bytes per LED @ 4Mbps)
// waitDMARead will read in up to numLEDs*12 bytes, or until it gets a WS2812 'stop'.
int bytes = waitDMARead(rgbBuffer[page], numLEDs * 12);
// TODO: what if DMA still busy? throw away? triple buffer?
sendRGB(rgbBuffer[page], bytes);
page ^= 1;
}
}
volatile uint8_t *outputBuf;
volatile int outputChain;
volatile int outputBytes;
// 'buf' is raw serial data to send, bytes is number of raw bytes (LEDs * 12)
void sendRGB(uint8_t *buf, int bytes)
{
outputChain = 0;
outputBuf = buf;
outputBytes = bytes;
// make sure DMA is done.
waitDMAComplete();
// invoke DMA callback to start next dump to LED strips
OnDMAComplete();
}
void OnDMAComplete()
{
if (outputBytes == 0) return; // out of bytes.. we're done.
while( outputChain < numChains && outputChainLength[chain] == 0) chain++;
if (chain == numChains) return; // we're done.
SetOutputMUX(outputChain);
int len = outputChainLength[chain] * 12;
if (len > outputBytes) len = outputBytes;
// TODO: swizzle bytes for RGB -> GBR, RBG, etc.
startSerialDMA( outputBuf, len, OnDMAComplete ); // bytes, length, on-complete-callback
outputBuf += len;
outputBytes -= len;
}
The reason that I want to buffer the whole chain is two fold: I may want/needs to swizzle the send bytes to compensate for different RGB ordering, and/or, direct some of those bytes into APA/Dotstar output. (which is much more forgiving than 2812)
Am I completely barking up the wrong tree? does this seem feasable?
-- pryankster