Displays text on a WS2812 (NeoPixel) matrix display
Dependencies: mbed miniFont wsDrive
Displays text on an array of WS2812 LEDs (NeoPixels)
Text is 5 rows tall so any grid larger than that can be used. The font supports A-Z (capitals only, any lowercase input will be capitalised) numbers and some basic punctuation. Letters are 5 LEDs wide, some numbers and punctuation are smaller but generally you need 6 pixels wide per character you wish to fit. If displaying a string a 1 row space is left between characters, for other spacings either edit the code or print one letter at a time and adjust the offset for the next letter.
LEDs must be connected in horizontal rows, top row first. Rows can be either direction or alternating directions.
NOTE: The testing on this has been fairly minimal. It works with my one physical configuration, I think the logic for other configurations is correct but haven't tested it. If you find a problem please let me know.
Diff: main.cpp
- Revision:
- 0:bada179a0b70
- Child:
- 1:2014f027ed6f
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Thu Nov 06 13:55:51 2014 +0000 @@ -0,0 +1,286 @@ +#include "mbed.h" +#include "wsDrive.h" +#include "miniFont.h" + +#define updatePeriodMS 2000 + + +/* +LED config - Assume all LEDs are in one logical chain of len*count leds. +Physical layout is: + +ChainLen = 4, ChainCount = 2, cascadeZigZag undefined + + D1 -> D2 -> D3 -> D4 + D5 -> D6 -> D7 -> D8 + +ChainLen = 4, ChainCount = 3, cascadeZigZag defined + + D1 -> D2 -> D3 -> D4 + D8 <- D7 <- D6 <- D5 + D9 -> D10 -> D11 -> D12 + +Text will use the top 5 rows. Text displayed on a chainCount of < 5 will be trimmed. +Letters are generally 5 pixels wide, numbers and punctuation are 5 or less. +The default is for a 1 pixel gap between characters so you will need 6 leds per letter displayed. + +System uses two buffers, one for that data to display, one to calculate the next data. +This allows shifts in either direction without overwriting values we haven't used yet. +Rather than do lots of copying each frame we toggle between which one is being displayed +and which calculated. + +*/ + +// LED config options: + +//#define cascadeZigZag + +#define chainLen 35 +#define chainCount 5 + +// IO setup - currently set for mbuino. + +DigitalIn dummy(P0_21,PullDown); +wsDrive ledDriver(P0_21,P0_22,P1_15); +DigitalIn progMode(P0_3,PullDown); +BusOut LEDs(LED1, LED2, LED3, LED4, LED5, LED6, LED7); + + +// globals + +miniFont pixelText; + +Timer updateRateTimer; + +pixelInfo pixelData1[chainLen * chainCount]; +pixelInfo pixelData2[chainLen * chainCount]; +pixelInfo *livePixels; +pixelInfo *nextPixels; + + + +inline uint16_t xy2index(uint16_t x, uint16_t y) +{ + if (x >= chainLen) + x = 0; + if (y >= chainCount) + y =0; +#ifdef cascadeZigZag + if (y%2) + return (y+1)*chainLen - x - 1; + else + return y*chainLen + x; +#else + return y*chainLen + x; +#endif +} + + +void blankBuffer(pixelInfo *Ptr) +{ + memset( (void *) Ptr, 00, sizeof(pixelInfo)*chainCount*chainLen ); +} + + +void initColour(pixelInfo *colour) +{ + for (int i = 0; i<(chainLen * chainCount); i++) { + nextPixels[i].R = colour->R; + nextPixels[i].G = colour->G; + nextPixels[i].B = colour->B; + } +} + + +uint16_t initText(const char *text, pixelInfo colour, int16_t startPixel = 0, int16_t startRow = 0) +{ + + uint8_t charIndex = 0; + uint8_t thisWidth; + uint8_t thisHeight; + uint16_t pixelY; + uint16_t pixelX; + uint16_t thisPixIndex; + const char *charData; + + + if (startRow >= chainCount) + return startPixel; + + while (*(text+charIndex)) { + + + if (startPixel >= chainLen) + return chainLen; + + thisHeight = pixelText.getPixHeight(*(text+charIndex)); + thisWidth = pixelText.getPixWidth(*(text+charIndex)); + + if (pixelText.getChar(*(text+charIndex),&charData)) { + + if ((startPixel + thisWidth) > chainLen) + thisWidth = chainLen - startPixel; + + for (uint8_t row = 0; row < thisHeight; row++) { + + pixelY = row + startRow; + if (pixelY >= chainCount) + break; + + for (int16_t col = 0; col < thisWidth; col++) { + pixelX = startPixel + col; + if (pixelX >= chainLen) + break; + + thisPixIndex = xy2index(pixelX,pixelY); + + if (*(charData+row) & (1<<(thisWidth-col-1)) ) { + nextPixels[thisPixIndex].R = colour.R; + nextPixels[thisPixIndex].G = colour.G; + nextPixels[thisPixIndex].B = colour.B; + } + + } // end for col + } //end for each row + } // end if we got data for that character + startPixel += thisWidth + 1; + charIndex++; + } // end while characters left + return startPixel; +} + +void initChain () +{ + pixelInfo textColour; + + // optionally use initColour to set a background colour + + // set text colour + textColour.R = 0x30; + textColour.G = 0x30; + textColour.B = 0x00; + + // add text keeping the index for the next character. + uint16_t nextChar = initText("H", textColour); + + + // change the colour + textColour.R = 0x10; + textColour.G = 0x10; + textColour.B = 0x30; + // add more text. + nextChar = initText("ell", textColour, nextChar); + + + // one more colour + textColour.R = 0x40; + textColour.G = 0x00; + textColour.B = 0x00; + // and a final letter. + initText("0", textColour, nextChar); + +} + + +// in theory lets you rotate red green and blue independantly an arbitary number of places in either direction. +// not well tested. +void rotateChain (int redStep, int greenStep, int blueStep) +{ + while (redStep < 0) + redStep += chainLen; + + while (greenStep < 0) + greenStep += chainLen; + + while (blueStep < 0) + blueStep += chainLen; + + uint16_t thisPixIndex; + uint16_t redPixIndex; + uint16_t bluePixIndex; + uint16_t greenPixIndex; + + for (uint16_t col = 0; col<chainLen; col++) { + for (uint8_t row = 0; row < chainCount; row++) { + +#ifdef cascadeZigZag + if (row % 2) { + thisPixIndex = chainLen - col - 1 + row*chainLen; + redPixIndex = chainLen - redStep - 1 + row*chainLen; + greenPixIndex = chainLen - greenStep - 1 + row*chainLen; + bluePixIndex = chainLen - blueStep - 1 + row*chainLen; + } else +#endif + { + thisPixIndex = col + row*chainLen; + redPixIndex = redStep + row*chainLen; + greenPixIndex = greenStep + row*chainLen; + bluePixIndex = blueStep + row*chainLen; + } + + nextPixels[thisPixIndex].R = livePixels[redPixIndex].R; + nextPixels[thisPixIndex].G = livePixels[greenPixIndex].G; + nextPixels[thisPixIndex].B = livePixels[bluePixIndex].B; + } + redStep = (redStep+1) % chainLen; + greenStep = (greenStep+1) % chainLen; + blueStep = (blueStep+1) % chainLen; + } +} + +void rotateChain (int stepsLeft) +{ + rotateChain(stepsLeft,stepsLeft,stepsLeft); +} + + +void swapBuffer() +{ + pixelInfo *tmpPtr = nextPixels; + nextPixels = livePixels; + livePixels = tmpPtr; +} + + +int main () +{ + LEDs = 1; + +// setup buffers + livePixels = pixelData1; + nextPixels = pixelData2; + + blankBuffer(livePixels); + blankBuffer(nextPixels); + + ledDriver.setData(livePixels, chainLen*chainCount); + LEDs = LEDs+1; + +// create the data to display + initChain(); + LEDs = LEDs+1; + +// set the active buffer to be displayed. + swapBuffer(); + LEDs = LEDs+1; + + updateRateTimer.start(); + while (true) { + + // update which buffer to use. + ledDriver.setData(livePixels, chainLen*chainCount); + // send the data + ledDriver.sendData(); + + LEDs = LEDs + 1; + + // Copy move everthing to the left + rotateChain(1); + + // change which buffer to use. + swapBuffer(); + while (updateRateTimer.read_ms() < updatePeriodMS) { + } + updateRateTimer.reset(); + } +} \ No newline at end of file