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.

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