Displays text on a WS2812 (NeoPixel) matrix display
Dependencies: mbed miniFont wsDrive
main.cpp
00001 #include "mbed.h" 00002 #include "wsDrive.h" 00003 #include "miniFont.h" 00004 00005 #define updatePeriodMS 2000 00006 00007 00008 /* 00009 LED config - Assume all LEDs are in one logical chain of len*count leds. 00010 Physical layout is: 00011 00012 ChainLen = 4, ChainCount = 2, cascadeZigZag undefined 00013 00014 D1 -> D2 -> D3 -> D4 00015 D5 -> D6 -> D7 -> D8 00016 00017 ChainLen = 4, ChainCount = 3, cascadeZigZag defined 00018 00019 D1 -> D2 -> D3 -> D4 00020 D8 <- D7 <- D6 <- D5 00021 D9 -> D10 -> D11 -> D12 00022 00023 Text will use the top 5 rows. Text displayed on a chainCount of < 5 will be trimmed. 00024 Letters are generally 5 pixels wide, numbers and punctuation are 5 or less. 00025 The default is for a 1 pixel gap between characters so you will need 6 leds per letter displayed. 00026 00027 System uses two buffers, one for that data to display, one to calculate the next data. 00028 This allows shifts in either direction without overwriting values we haven't used yet. 00029 Rather than do lots of copying each frame we toggle between which one is being displayed 00030 and which calculated. 00031 00032 */ 00033 00034 // LED config options: 00035 00036 // uncomment if the direction of connections reverse each row. 00037 const bool cascadeZigZag = false; 00038 const bool topRowL2R = true; 00039 00040 const uint16_t chainLen = 35; 00041 const uint16_t chainCount = 5; 00042 00043 // IO setup - currently set for mbuino. 00044 00045 DigitalIn dummy(P0_21,PullDown); 00046 wsDrive ledDriver(P0_21,P0_22,P1_15); 00047 DigitalIn progMode(P0_3,PullDown); 00048 BusOut LEDs(LED1, LED2, LED3, LED4, LED5, LED6, LED7); 00049 00050 00051 // globals 00052 00053 miniFont pixelText; 00054 00055 Timer updateRateTimer; 00056 00057 pixelInfo pixelData1[chainLen * chainCount]; 00058 pixelInfo pixelData2[chainLen * chainCount]; 00059 pixelInfo *livePixels; 00060 pixelInfo *nextPixels; 00061 00062 // could be optimised more but most of this is constants so the compiler should 00063 // do that for us. This is confusing enough as it is. 00064 inline bool reverseRow(uint16_t row) 00065 { 00066 if (cascadeZigZag) { 00067 if (topRowL2R) { // left to right we reverse odd rows 00068 if (row%2) // odd row 00069 return true; 00070 else 00071 return false; 00072 } else { // right to left reverse even rows. 00073 if (row%2) // odd row 00074 return false; 00075 else 00076 return true; 00077 } 00078 } else { 00079 if (topRowL2R) // left to right reverse none 00080 return false; 00081 else // right to left reverse all. 00082 return true; 00083 } 00084 } 00085 00086 inline uint16_t xy2index(uint16_t x, uint16_t y) 00087 { 00088 if (x >= chainLen) 00089 x = 0; 00090 if (y >= chainCount) 00091 y = 0; 00092 00093 if (reverseRow(y)) 00094 return (y+1)*chainLen - x - 1; 00095 else 00096 return y*chainLen + x; 00097 } 00098 00099 00100 void blankBuffer(pixelInfo *Ptr) 00101 { 00102 memset( (void *) Ptr, 00, sizeof(pixelInfo)*chainCount*chainLen ); 00103 } 00104 00105 00106 void initColour(pixelInfo *colour) 00107 { 00108 for (int i = 0; i<(chainLen * chainCount); i++) { 00109 nextPixels[i].R = colour->R; 00110 nextPixels[i].G = colour->G; 00111 nextPixels[i].B = colour->B; 00112 } 00113 } 00114 00115 void initGradient(pixelInfo *startColour, pixelInfo *endColour) 00116 { 00117 uint16_t thisPixIndex; 00118 float redDelta = ((float)(endColour->R) - (float)(startColour->R)) / chainLen; 00119 float greenDelta = ((float)(endColour->G) - (float)(startColour->G)) / chainLen; 00120 float blueDelta = ((float)(endColour->B) - (float)(startColour->B)) / chainLen; 00121 00122 for (int i = 0; i<(chainLen); i++) { 00123 for (uint8_t row = 0; row < chainCount; row++) { 00124 00125 thisPixIndex = xy2index(i,row); 00126 00127 nextPixels[thisPixIndex].R = redDelta*i + startColour->R; 00128 nextPixels[thisPixIndex].G = greenDelta*i + startColour->G; 00129 nextPixels[thisPixIndex].B = blueDelta*i + startColour->B; 00130 } 00131 } 00132 } 00133 00134 uint16_t initText(const char *text, pixelInfo colour, int16_t startPixel = 0, int16_t startRow = 0) 00135 { 00136 00137 uint8_t charIndex = 0; 00138 uint8_t thisWidth; 00139 uint8_t thisHeight; 00140 uint16_t pixelY; 00141 uint16_t pixelX; 00142 uint16_t thisPixIndex; 00143 const char *charData; 00144 00145 00146 if (startRow >= chainCount) 00147 return startPixel; 00148 00149 while (*(text+charIndex)) { 00150 00151 00152 if (startPixel >= chainLen) 00153 return chainLen; 00154 00155 thisHeight = pixelText.getPixHeight(*(text+charIndex)); 00156 thisWidth = pixelText.getPixWidth(*(text+charIndex)); 00157 00158 if (pixelText.getChar(*(text+charIndex),&charData)) { 00159 00160 if ((startPixel + thisWidth) > chainLen) 00161 thisWidth = chainLen - startPixel; 00162 00163 for (uint8_t row = 0; row < thisHeight; row++) { 00164 00165 pixelY = row + startRow; 00166 if (pixelY >= chainCount) 00167 break; 00168 00169 for (int16_t col = 0; col < thisWidth; col++) { 00170 pixelX = startPixel + col; 00171 if (pixelX >= chainLen) 00172 break; 00173 00174 thisPixIndex = xy2index(pixelX,pixelY); 00175 00176 if (*(charData+row) & (1<<(thisWidth-col-1)) ) { 00177 nextPixels[thisPixIndex].R = colour.R; 00178 nextPixels[thisPixIndex].G = colour.G; 00179 nextPixels[thisPixIndex].B = colour.B; 00180 } 00181 00182 } // end for col 00183 } //end for each row 00184 } // end if we got data for that character 00185 startPixel += thisWidth + 1; 00186 charIndex++; 00187 } // end while characters left 00188 return startPixel; 00189 } 00190 00191 void initChain () 00192 { 00193 pixelInfo textColour; 00194 pixelInfo bgColour1; 00195 pixelInfo bgColour2; 00196 00197 // optionally use initColour to set a background colour 00198 bgColour1.R = 0x00; 00199 bgColour1.G = 0x08; 00200 bgColour1.B = 0x00; 00201 00202 bgColour2.R = 0x08; 00203 bgColour2.G = 0x00; 00204 bgColour2.B = 0x00; 00205 00206 initGradient(&bgColour1,&bgColour2); 00207 00208 // set text colour 00209 textColour.R = 0x40; 00210 textColour.G = 0x00; 00211 textColour.B = 0x00; 00212 00213 // add text keeping the index for the next character. 00214 uint16_t nextChar = initText("H", textColour); 00215 00216 // change the colour 00217 textColour.R = 0x10; 00218 textColour.G = 0x10; 00219 textColour.B = 0x30; 00220 // add more text. 00221 nextChar = initText("el", textColour, nextChar); 00222 00223 // change the colour 00224 textColour.R = 0x30; 00225 textColour.G = 0x30; 00226 textColour.B = 0x00; 00227 // add more text. 00228 nextChar = initText("l", textColour, nextChar); 00229 00230 // one more colour 00231 textColour.R = 0x00; 00232 textColour.G = 0x40; 00233 textColour.B = 0x00; 00234 // and a final letter. 00235 initText("0", textColour, nextChar); 00236 00237 } 00238 00239 00240 // in theory lets you rotate red green and blue independantly an arbitary number of places in either direction. 00241 // not well tested. 00242 void rotateChain (int redStep, int greenStep, int blueStep) 00243 { 00244 while (redStep < 0) 00245 redStep += chainLen; 00246 00247 while (greenStep < 0) 00248 greenStep += chainLen; 00249 00250 while (blueStep < 0) 00251 blueStep += chainLen; 00252 00253 uint16_t thisPixIndex; 00254 uint16_t redPixIndex; 00255 uint16_t bluePixIndex; 00256 uint16_t greenPixIndex; 00257 00258 for (uint16_t col = 0; col<chainLen; col++) { 00259 for (uint8_t row = 0; row < chainCount; row++) { 00260 00261 thisPixIndex = xy2index(col,row); 00262 00263 redPixIndex = xy2index((col+redStep)%chainLen, row); 00264 bluePixIndex = xy2index((col+blueStep)%chainLen, row); 00265 greenPixIndex = xy2index((col+greenStep)%chainLen,row); 00266 00267 nextPixels[thisPixIndex].R = livePixels[redPixIndex].R; 00268 nextPixels[thisPixIndex].G = livePixels[greenPixIndex].G; 00269 nextPixels[thisPixIndex].B = livePixels[bluePixIndex].B; 00270 } 00271 } 00272 } 00273 00274 00275 void rotateChain (int stepsLeft) 00276 { 00277 while (stepsLeft < 0) 00278 stepsLeft += chainLen; 00279 00280 uint16_t thisPixIndex; 00281 uint16_t sourcePixIndex; 00282 00283 for (uint16_t col = 0; col<chainLen; col++) { 00284 for (uint8_t row = 0; row < chainCount; row++) { 00285 00286 thisPixIndex = xy2index(col,row); 00287 00288 sourcePixIndex = xy2index((col+stepsLeft)%chainLen, row); 00289 00290 nextPixels[thisPixIndex].R = livePixels[sourcePixIndex].R; 00291 nextPixels[thisPixIndex].G = livePixels[sourcePixIndex].G; 00292 nextPixels[thisPixIndex].B = livePixels[sourcePixIndex].B; 00293 } 00294 } 00295 } 00296 00297 00298 void swapBuffer() 00299 { 00300 pixelInfo *tmpPtr = nextPixels; 00301 nextPixels = livePixels; 00302 livePixels = tmpPtr; 00303 } 00304 00305 00306 int main () 00307 { 00308 LEDs = 1; 00309 00310 // setup buffers 00311 livePixels = pixelData1; 00312 nextPixels = pixelData2; 00313 00314 blankBuffer(livePixels); 00315 blankBuffer(nextPixels); 00316 00317 ledDriver.setData(livePixels, chainLen*chainCount); 00318 LEDs = LEDs+1; 00319 00320 // create the data to display 00321 initChain(); 00322 LEDs = LEDs+1; 00323 00324 // set the active buffer to be displayed. 00325 swapBuffer(); 00326 LEDs = LEDs+1; 00327 00328 updateRateTimer.start(); 00329 while (true) { 00330 00331 // update which buffer to use. 00332 ledDriver.setData(livePixels, chainLen*chainCount); 00333 // send the data 00334 ledDriver.sendData(); 00335 00336 LEDs = LEDs + 1; 00337 00338 // Copy move everthing to the left 00339 rotateChain(1); 00340 00341 // change which buffer to use. 00342 swapBuffer(); 00343 while (updateRateTimer.read_ms() < updatePeriodMS) { 00344 } 00345 updateRateTimer.reset(); 00346 } 00347 }
Generated on Thu Jul 14 2022 17:00:17 by 1.7.2