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.

Committer:
AndyA
Date:
Thu Nov 06 13:55:51 2014 +0000
Revision:
0:bada179a0b70
Child:
1:2014f027ed6f
First commit;

Who changed what in which revision?

UserRevisionLine numberNew contents of line
AndyA 0:bada179a0b70 1 #include "mbed.h"
AndyA 0:bada179a0b70 2 #include "wsDrive.h"
AndyA 0:bada179a0b70 3 #include "miniFont.h"
AndyA 0:bada179a0b70 4
AndyA 0:bada179a0b70 5 #define updatePeriodMS 2000
AndyA 0:bada179a0b70 6
AndyA 0:bada179a0b70 7
AndyA 0:bada179a0b70 8 /*
AndyA 0:bada179a0b70 9 LED config - Assume all LEDs are in one logical chain of len*count leds.
AndyA 0:bada179a0b70 10 Physical layout is:
AndyA 0:bada179a0b70 11
AndyA 0:bada179a0b70 12 ChainLen = 4, ChainCount = 2, cascadeZigZag undefined
AndyA 0:bada179a0b70 13
AndyA 0:bada179a0b70 14 D1 -> D2 -> D3 -> D4
AndyA 0:bada179a0b70 15 D5 -> D6 -> D7 -> D8
AndyA 0:bada179a0b70 16
AndyA 0:bada179a0b70 17 ChainLen = 4, ChainCount = 3, cascadeZigZag defined
AndyA 0:bada179a0b70 18
AndyA 0:bada179a0b70 19 D1 -> D2 -> D3 -> D4
AndyA 0:bada179a0b70 20 D8 <- D7 <- D6 <- D5
AndyA 0:bada179a0b70 21 D9 -> D10 -> D11 -> D12
AndyA 0:bada179a0b70 22
AndyA 0:bada179a0b70 23 Text will use the top 5 rows. Text displayed on a chainCount of < 5 will be trimmed.
AndyA 0:bada179a0b70 24 Letters are generally 5 pixels wide, numbers and punctuation are 5 or less.
AndyA 0:bada179a0b70 25 The default is for a 1 pixel gap between characters so you will need 6 leds per letter displayed.
AndyA 0:bada179a0b70 26
AndyA 0:bada179a0b70 27 System uses two buffers, one for that data to display, one to calculate the next data.
AndyA 0:bada179a0b70 28 This allows shifts in either direction without overwriting values we haven't used yet.
AndyA 0:bada179a0b70 29 Rather than do lots of copying each frame we toggle between which one is being displayed
AndyA 0:bada179a0b70 30 and which calculated.
AndyA 0:bada179a0b70 31
AndyA 0:bada179a0b70 32 */
AndyA 0:bada179a0b70 33
AndyA 0:bada179a0b70 34 // LED config options:
AndyA 0:bada179a0b70 35
AndyA 0:bada179a0b70 36 //#define cascadeZigZag
AndyA 0:bada179a0b70 37
AndyA 0:bada179a0b70 38 #define chainLen 35
AndyA 0:bada179a0b70 39 #define chainCount 5
AndyA 0:bada179a0b70 40
AndyA 0:bada179a0b70 41 // IO setup - currently set for mbuino.
AndyA 0:bada179a0b70 42
AndyA 0:bada179a0b70 43 DigitalIn dummy(P0_21,PullDown);
AndyA 0:bada179a0b70 44 wsDrive ledDriver(P0_21,P0_22,P1_15);
AndyA 0:bada179a0b70 45 DigitalIn progMode(P0_3,PullDown);
AndyA 0:bada179a0b70 46 BusOut LEDs(LED1, LED2, LED3, LED4, LED5, LED6, LED7);
AndyA 0:bada179a0b70 47
AndyA 0:bada179a0b70 48
AndyA 0:bada179a0b70 49 // globals
AndyA 0:bada179a0b70 50
AndyA 0:bada179a0b70 51 miniFont pixelText;
AndyA 0:bada179a0b70 52
AndyA 0:bada179a0b70 53 Timer updateRateTimer;
AndyA 0:bada179a0b70 54
AndyA 0:bada179a0b70 55 pixelInfo pixelData1[chainLen * chainCount];
AndyA 0:bada179a0b70 56 pixelInfo pixelData2[chainLen * chainCount];
AndyA 0:bada179a0b70 57 pixelInfo *livePixels;
AndyA 0:bada179a0b70 58 pixelInfo *nextPixels;
AndyA 0:bada179a0b70 59
AndyA 0:bada179a0b70 60
AndyA 0:bada179a0b70 61
AndyA 0:bada179a0b70 62 inline uint16_t xy2index(uint16_t x, uint16_t y)
AndyA 0:bada179a0b70 63 {
AndyA 0:bada179a0b70 64 if (x >= chainLen)
AndyA 0:bada179a0b70 65 x = 0;
AndyA 0:bada179a0b70 66 if (y >= chainCount)
AndyA 0:bada179a0b70 67 y =0;
AndyA 0:bada179a0b70 68 #ifdef cascadeZigZag
AndyA 0:bada179a0b70 69 if (y%2)
AndyA 0:bada179a0b70 70 return (y+1)*chainLen - x - 1;
AndyA 0:bada179a0b70 71 else
AndyA 0:bada179a0b70 72 return y*chainLen + x;
AndyA 0:bada179a0b70 73 #else
AndyA 0:bada179a0b70 74 return y*chainLen + x;
AndyA 0:bada179a0b70 75 #endif
AndyA 0:bada179a0b70 76 }
AndyA 0:bada179a0b70 77
AndyA 0:bada179a0b70 78
AndyA 0:bada179a0b70 79 void blankBuffer(pixelInfo *Ptr)
AndyA 0:bada179a0b70 80 {
AndyA 0:bada179a0b70 81 memset( (void *) Ptr, 00, sizeof(pixelInfo)*chainCount*chainLen );
AndyA 0:bada179a0b70 82 }
AndyA 0:bada179a0b70 83
AndyA 0:bada179a0b70 84
AndyA 0:bada179a0b70 85 void initColour(pixelInfo *colour)
AndyA 0:bada179a0b70 86 {
AndyA 0:bada179a0b70 87 for (int i = 0; i<(chainLen * chainCount); i++) {
AndyA 0:bada179a0b70 88 nextPixels[i].R = colour->R;
AndyA 0:bada179a0b70 89 nextPixels[i].G = colour->G;
AndyA 0:bada179a0b70 90 nextPixels[i].B = colour->B;
AndyA 0:bada179a0b70 91 }
AndyA 0:bada179a0b70 92 }
AndyA 0:bada179a0b70 93
AndyA 0:bada179a0b70 94
AndyA 0:bada179a0b70 95 uint16_t initText(const char *text, pixelInfo colour, int16_t startPixel = 0, int16_t startRow = 0)
AndyA 0:bada179a0b70 96 {
AndyA 0:bada179a0b70 97
AndyA 0:bada179a0b70 98 uint8_t charIndex = 0;
AndyA 0:bada179a0b70 99 uint8_t thisWidth;
AndyA 0:bada179a0b70 100 uint8_t thisHeight;
AndyA 0:bada179a0b70 101 uint16_t pixelY;
AndyA 0:bada179a0b70 102 uint16_t pixelX;
AndyA 0:bada179a0b70 103 uint16_t thisPixIndex;
AndyA 0:bada179a0b70 104 const char *charData;
AndyA 0:bada179a0b70 105
AndyA 0:bada179a0b70 106
AndyA 0:bada179a0b70 107 if (startRow >= chainCount)
AndyA 0:bada179a0b70 108 return startPixel;
AndyA 0:bada179a0b70 109
AndyA 0:bada179a0b70 110 while (*(text+charIndex)) {
AndyA 0:bada179a0b70 111
AndyA 0:bada179a0b70 112
AndyA 0:bada179a0b70 113 if (startPixel >= chainLen)
AndyA 0:bada179a0b70 114 return chainLen;
AndyA 0:bada179a0b70 115
AndyA 0:bada179a0b70 116 thisHeight = pixelText.getPixHeight(*(text+charIndex));
AndyA 0:bada179a0b70 117 thisWidth = pixelText.getPixWidth(*(text+charIndex));
AndyA 0:bada179a0b70 118
AndyA 0:bada179a0b70 119 if (pixelText.getChar(*(text+charIndex),&charData)) {
AndyA 0:bada179a0b70 120
AndyA 0:bada179a0b70 121 if ((startPixel + thisWidth) > chainLen)
AndyA 0:bada179a0b70 122 thisWidth = chainLen - startPixel;
AndyA 0:bada179a0b70 123
AndyA 0:bada179a0b70 124 for (uint8_t row = 0; row < thisHeight; row++) {
AndyA 0:bada179a0b70 125
AndyA 0:bada179a0b70 126 pixelY = row + startRow;
AndyA 0:bada179a0b70 127 if (pixelY >= chainCount)
AndyA 0:bada179a0b70 128 break;
AndyA 0:bada179a0b70 129
AndyA 0:bada179a0b70 130 for (int16_t col = 0; col < thisWidth; col++) {
AndyA 0:bada179a0b70 131 pixelX = startPixel + col;
AndyA 0:bada179a0b70 132 if (pixelX >= chainLen)
AndyA 0:bada179a0b70 133 break;
AndyA 0:bada179a0b70 134
AndyA 0:bada179a0b70 135 thisPixIndex = xy2index(pixelX,pixelY);
AndyA 0:bada179a0b70 136
AndyA 0:bada179a0b70 137 if (*(charData+row) & (1<<(thisWidth-col-1)) ) {
AndyA 0:bada179a0b70 138 nextPixels[thisPixIndex].R = colour.R;
AndyA 0:bada179a0b70 139 nextPixels[thisPixIndex].G = colour.G;
AndyA 0:bada179a0b70 140 nextPixels[thisPixIndex].B = colour.B;
AndyA 0:bada179a0b70 141 }
AndyA 0:bada179a0b70 142
AndyA 0:bada179a0b70 143 } // end for col
AndyA 0:bada179a0b70 144 } //end for each row
AndyA 0:bada179a0b70 145 } // end if we got data for that character
AndyA 0:bada179a0b70 146 startPixel += thisWidth + 1;
AndyA 0:bada179a0b70 147 charIndex++;
AndyA 0:bada179a0b70 148 } // end while characters left
AndyA 0:bada179a0b70 149 return startPixel;
AndyA 0:bada179a0b70 150 }
AndyA 0:bada179a0b70 151
AndyA 0:bada179a0b70 152 void initChain ()
AndyA 0:bada179a0b70 153 {
AndyA 0:bada179a0b70 154 pixelInfo textColour;
AndyA 0:bada179a0b70 155
AndyA 0:bada179a0b70 156 // optionally use initColour to set a background colour
AndyA 0:bada179a0b70 157
AndyA 0:bada179a0b70 158 // set text colour
AndyA 0:bada179a0b70 159 textColour.R = 0x30;
AndyA 0:bada179a0b70 160 textColour.G = 0x30;
AndyA 0:bada179a0b70 161 textColour.B = 0x00;
AndyA 0:bada179a0b70 162
AndyA 0:bada179a0b70 163 // add text keeping the index for the next character.
AndyA 0:bada179a0b70 164 uint16_t nextChar = initText("H", textColour);
AndyA 0:bada179a0b70 165
AndyA 0:bada179a0b70 166
AndyA 0:bada179a0b70 167 // change the colour
AndyA 0:bada179a0b70 168 textColour.R = 0x10;
AndyA 0:bada179a0b70 169 textColour.G = 0x10;
AndyA 0:bada179a0b70 170 textColour.B = 0x30;
AndyA 0:bada179a0b70 171 // add more text.
AndyA 0:bada179a0b70 172 nextChar = initText("ell", textColour, nextChar);
AndyA 0:bada179a0b70 173
AndyA 0:bada179a0b70 174
AndyA 0:bada179a0b70 175 // one more colour
AndyA 0:bada179a0b70 176 textColour.R = 0x40;
AndyA 0:bada179a0b70 177 textColour.G = 0x00;
AndyA 0:bada179a0b70 178 textColour.B = 0x00;
AndyA 0:bada179a0b70 179 // and a final letter.
AndyA 0:bada179a0b70 180 initText("0", textColour, nextChar);
AndyA 0:bada179a0b70 181
AndyA 0:bada179a0b70 182 }
AndyA 0:bada179a0b70 183
AndyA 0:bada179a0b70 184
AndyA 0:bada179a0b70 185 // in theory lets you rotate red green and blue independantly an arbitary number of places in either direction.
AndyA 0:bada179a0b70 186 // not well tested.
AndyA 0:bada179a0b70 187 void rotateChain (int redStep, int greenStep, int blueStep)
AndyA 0:bada179a0b70 188 {
AndyA 0:bada179a0b70 189 while (redStep < 0)
AndyA 0:bada179a0b70 190 redStep += chainLen;
AndyA 0:bada179a0b70 191
AndyA 0:bada179a0b70 192 while (greenStep < 0)
AndyA 0:bada179a0b70 193 greenStep += chainLen;
AndyA 0:bada179a0b70 194
AndyA 0:bada179a0b70 195 while (blueStep < 0)
AndyA 0:bada179a0b70 196 blueStep += chainLen;
AndyA 0:bada179a0b70 197
AndyA 0:bada179a0b70 198 uint16_t thisPixIndex;
AndyA 0:bada179a0b70 199 uint16_t redPixIndex;
AndyA 0:bada179a0b70 200 uint16_t bluePixIndex;
AndyA 0:bada179a0b70 201 uint16_t greenPixIndex;
AndyA 0:bada179a0b70 202
AndyA 0:bada179a0b70 203 for (uint16_t col = 0; col<chainLen; col++) {
AndyA 0:bada179a0b70 204 for (uint8_t row = 0; row < chainCount; row++) {
AndyA 0:bada179a0b70 205
AndyA 0:bada179a0b70 206 #ifdef cascadeZigZag
AndyA 0:bada179a0b70 207 if (row % 2) {
AndyA 0:bada179a0b70 208 thisPixIndex = chainLen - col - 1 + row*chainLen;
AndyA 0:bada179a0b70 209 redPixIndex = chainLen - redStep - 1 + row*chainLen;
AndyA 0:bada179a0b70 210 greenPixIndex = chainLen - greenStep - 1 + row*chainLen;
AndyA 0:bada179a0b70 211 bluePixIndex = chainLen - blueStep - 1 + row*chainLen;
AndyA 0:bada179a0b70 212 } else
AndyA 0:bada179a0b70 213 #endif
AndyA 0:bada179a0b70 214 {
AndyA 0:bada179a0b70 215 thisPixIndex = col + row*chainLen;
AndyA 0:bada179a0b70 216 redPixIndex = redStep + row*chainLen;
AndyA 0:bada179a0b70 217 greenPixIndex = greenStep + row*chainLen;
AndyA 0:bada179a0b70 218 bluePixIndex = blueStep + row*chainLen;
AndyA 0:bada179a0b70 219 }
AndyA 0:bada179a0b70 220
AndyA 0:bada179a0b70 221 nextPixels[thisPixIndex].R = livePixels[redPixIndex].R;
AndyA 0:bada179a0b70 222 nextPixels[thisPixIndex].G = livePixels[greenPixIndex].G;
AndyA 0:bada179a0b70 223 nextPixels[thisPixIndex].B = livePixels[bluePixIndex].B;
AndyA 0:bada179a0b70 224 }
AndyA 0:bada179a0b70 225 redStep = (redStep+1) % chainLen;
AndyA 0:bada179a0b70 226 greenStep = (greenStep+1) % chainLen;
AndyA 0:bada179a0b70 227 blueStep = (blueStep+1) % chainLen;
AndyA 0:bada179a0b70 228 }
AndyA 0:bada179a0b70 229 }
AndyA 0:bada179a0b70 230
AndyA 0:bada179a0b70 231 void rotateChain (int stepsLeft)
AndyA 0:bada179a0b70 232 {
AndyA 0:bada179a0b70 233 rotateChain(stepsLeft,stepsLeft,stepsLeft);
AndyA 0:bada179a0b70 234 }
AndyA 0:bada179a0b70 235
AndyA 0:bada179a0b70 236
AndyA 0:bada179a0b70 237 void swapBuffer()
AndyA 0:bada179a0b70 238 {
AndyA 0:bada179a0b70 239 pixelInfo *tmpPtr = nextPixels;
AndyA 0:bada179a0b70 240 nextPixels = livePixels;
AndyA 0:bada179a0b70 241 livePixels = tmpPtr;
AndyA 0:bada179a0b70 242 }
AndyA 0:bada179a0b70 243
AndyA 0:bada179a0b70 244
AndyA 0:bada179a0b70 245 int main ()
AndyA 0:bada179a0b70 246 {
AndyA 0:bada179a0b70 247 LEDs = 1;
AndyA 0:bada179a0b70 248
AndyA 0:bada179a0b70 249 // setup buffers
AndyA 0:bada179a0b70 250 livePixels = pixelData1;
AndyA 0:bada179a0b70 251 nextPixels = pixelData2;
AndyA 0:bada179a0b70 252
AndyA 0:bada179a0b70 253 blankBuffer(livePixels);
AndyA 0:bada179a0b70 254 blankBuffer(nextPixels);
AndyA 0:bada179a0b70 255
AndyA 0:bada179a0b70 256 ledDriver.setData(livePixels, chainLen*chainCount);
AndyA 0:bada179a0b70 257 LEDs = LEDs+1;
AndyA 0:bada179a0b70 258
AndyA 0:bada179a0b70 259 // create the data to display
AndyA 0:bada179a0b70 260 initChain();
AndyA 0:bada179a0b70 261 LEDs = LEDs+1;
AndyA 0:bada179a0b70 262
AndyA 0:bada179a0b70 263 // set the active buffer to be displayed.
AndyA 0:bada179a0b70 264 swapBuffer();
AndyA 0:bada179a0b70 265 LEDs = LEDs+1;
AndyA 0:bada179a0b70 266
AndyA 0:bada179a0b70 267 updateRateTimer.start();
AndyA 0:bada179a0b70 268 while (true) {
AndyA 0:bada179a0b70 269
AndyA 0:bada179a0b70 270 // update which buffer to use.
AndyA 0:bada179a0b70 271 ledDriver.setData(livePixels, chainLen*chainCount);
AndyA 0:bada179a0b70 272 // send the data
AndyA 0:bada179a0b70 273 ledDriver.sendData();
AndyA 0:bada179a0b70 274
AndyA 0:bada179a0b70 275 LEDs = LEDs + 1;
AndyA 0:bada179a0b70 276
AndyA 0:bada179a0b70 277 // Copy move everthing to the left
AndyA 0:bada179a0b70 278 rotateChain(1);
AndyA 0:bada179a0b70 279
AndyA 0:bada179a0b70 280 // change which buffer to use.
AndyA 0:bada179a0b70 281 swapBuffer();
AndyA 0:bada179a0b70 282 while (updateRateTimer.read_ms() < updatePeriodMS) {
AndyA 0:bada179a0b70 283 }
AndyA 0:bada179a0b70 284 updateRateTimer.reset();
AndyA 0:bada179a0b70 285 }
AndyA 0:bada179a0b70 286 }