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.
main.cpp@0:bada179a0b70, 2014-11-06 (annotated)
- 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?
User | Revision | Line number | New 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 | } |