Forked LEDMatrix and added horizontal scrolling

Fork of LEDMatrix by Yihui Xiong

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers LEDMatrix.cpp Source File

LEDMatrix.cpp

00001 /**
00002  * LED Matrix library for http://www.seeedstudio.com/depot/ultrathin-16x32-red-led-matrix-panel-p-1582.html
00003  * The LED Matrix panel has 32x16 pixels. Several panel can be combined together as a large screen.
00004  * 
00005  * Coordinate & Connection (mbed -> panel 0 -> panel 1 -> ...)
00006  *   (0, 0)                                     (0, 0)
00007  *     +--------+--------+--------+               +--------+--------+
00008  *     |   5    |    3   |    1   |               |    1   |    0   |
00009  *     |        |        |        |               |        |        |<----- mbed
00010  *     +--------+--------+--------+               +--------+--------+
00011  *     |   4    |    2   |    0   |                              (64, 16)
00012  *     |        |        |        |<----- mbed
00013  *     +--------+--------+--------+
00014  *                             (96, 32)
00015  *  Copyright (c) 2013 Seeed Technology Inc.
00016  *  @auther     Yihui Xiong
00017  *  @date       Nov 8, 2013
00018  *  @license    Apache
00019  *
00020  * Heavily modified by Rob Dobson, 2016
00021  */
00022 
00023 #include "LEDMatrix.h"
00024 #include "mbed.h"
00025 #include "fontBig.h"
00026 #include "font5x7.h"
00027 
00028 #if 0
00029 #define ASSERT(e)   if (!(e)) { Serial.println(#e); while (1); }
00030 #else
00031 #define ASSERT(e)
00032 #endif
00033 
00034 LEDMatrix::LEDMatrix(PinName pinA, PinName pinB, PinName pinC, PinName pinD, PinName pinOE, PinName pinR1, PinName pinSTB, PinName pinCLK) :
00035         a(pinA), b(pinB), c(pinC), d(pinD), oe(pinOE), r1(pinR1), stb(pinSTB), clk(pinCLK)
00036 {
00037     this->clk = clk;
00038     this->r1 = r1;
00039     this->stb = stb;
00040     this->oe = oe;
00041     this->a = a;
00042     this->b = b;
00043     this->c = c;
00044     this->d = d;
00045 
00046     mask = 0xff;
00047     _isEnabled = false;
00048     for (int i = 0; i < LED_MATRIX_LEDS_VERTICALLY; i++)
00049     {
00050         _hScrollPos[i] = 0;
00051         _hScrollWidth[i] = LED_MATRIX_LEDS_HORIZONTALLY;
00052     }
00053     for (int i = 0; i < LED_MATRIX_MAX_LINES; i++)
00054     {
00055         _lineScrollInc[i] = 0;
00056         _lineHighlight[i] = false;
00057         _lineInvert[i] = false;
00058     }
00059     _isBusy = false;
00060     _charSeparation = 1;
00061     _flashCounter = 0;
00062     _flashRate = 50;
00063     _flashState = false;
00064     _scrollCounter = 0;
00065     _scrollRate = 7;
00066 }
00067 
00068 void LEDMatrix::begin(uint8_t *_pDisplayBuf, uint16_t width, uint16_t height, uint16_t dispBufWidth, 
00069                 uint16_t numLines, int charSeparation, int flashRate, int scrollRate)
00070 {
00071     ASSERT(0 == (width % LED_MATRIX_LEDS_HORIZONTALLY));
00072     ASSERT(0 == (scroll_width % LED_MATRIX_LEDS_HORIZONTALLY));
00073     ASSERT(0 == (height % LED_MATRIX_LEDS_VERTICALLY));
00074 
00075     this->_pDisplayBuf = _pDisplayBuf;
00076     this->width = width;
00077     this->height = height;
00078     this->dispBufWidth = dispBufWidth;
00079     this->numLines = numLines;
00080     this->_charSeparation = charSeparation;
00081     if (flashRate > 0)
00082         this->_flashRate = flashRate;
00083     if (scrollRate > 0)
00084         this->_scrollRate = scrollRate;
00085     if (numLines > LED_MATRIX_MAX_LINES)
00086         this->numLines = LED_MATRIX_MAX_LINES;
00087     for (int i = 0; i < LED_MATRIX_LEDS_VERTICALLY; i++)
00088     {
00089         this->_hScrollWidth[i] = dispBufWidth;
00090     }
00091     
00092     _isEnabled = true;
00093 }
00094 
00095 void LEDMatrix::drawPoint(uint16_t x, uint16_t y, uint8_t pixel)
00096 {
00097     ASSERT(dispBufWidth > x);
00098     ASSERT(height > y);
00099 
00100     uint8_t *byte = _pDisplayBuf + (height - 1 - y) * dispBufWidth / 8 + x / 8;
00101     uint8_t  bit = x % 8;
00102 
00103     if (pixel)
00104     {
00105         *byte |= 0x80 >> bit;
00106     }
00107     else 
00108     {
00109         *byte &= ~(0x80 >> bit);
00110     }
00111 }
00112 
00113 void LEDMatrix::drawRect(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t pixel)
00114 {
00115     for (uint16_t x = x1; x < x2; x++) 
00116     {
00117         for (uint16_t y = y1; y < y2; y++) 
00118         {
00119             drawPoint(x, y, pixel);
00120         }
00121     }
00122 }
00123 
00124 void LEDMatrix::drawImage(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t *image)
00125 {
00126     ASSERT(0 == ((x2 - x1) % 8));
00127 
00128     for (uint16_t x = x1; x < x2; x++) 
00129     {
00130         for (uint16_t y = y1; y < y2; y++) 
00131         {
00132             uint8_t *byte = image + x * 8 + y / 8;
00133             uint8_t  bit = 7 - (y % 8);
00134             uint8_t  pixel = (*byte >> bit) & 1;
00135             drawPoint(x, y, pixel);
00136         }
00137     }
00138 }
00139 
00140 //void LEDMatrix::clear()
00141 //{
00142 //    uint8_t *ptr = _pDisplayBuf;
00143 //    for (uint16_t i = 0; i < (dispBufWidth * height / 8); i++) 
00144 //    {
00145 //        *ptr = 0x00;
00146 //        ptr++;
00147 //    }
00148 //}
00149 
00150 void LEDMatrix::invertColour()
00151 {
00152     mask = ~mask;
00153 }
00154 
00155 uint8_t LEDMatrix::isInvertedColour()
00156 {
00157     return mask;
00158 }
00159 
00160 void LEDMatrix::scan()
00161 {
00162     // On each call this function handles one row of the display
00163     static uint8_t scanRowIdx = 0;
00164 
00165     // Check if being updated
00166     if (_isBusy)
00167         return;
00168 
00169     // Check if display enabled
00170     if (!_isEnabled)
00171         return;
00172 
00173     // Base of this scan row in buffer to display
00174     uint8_t *pBufBase = _pDisplayBuf + scanRowIdx * (dispBufWidth / 8);
00175     
00176     // Handle multiple units vertically
00177     for (int rowIdx = 0; rowIdx < (height / LED_MATRIX_LEDS_VERTICALLY); rowIdx++) 
00178     {
00179         // Calculate buffer position within this unit
00180         uint8_t *pBuf = pBufBase + rowIdx * (dispBufWidth / 8) * LED_MATRIX_LEDS_VERTICALLY;
00181 
00182         // Work out which display line we're in
00183         uint8_t dispMask = mask;
00184         int rowsInLine = (height/numLines);
00185         int dispLine = scanRowIdx / rowsInLine;
00186         if (dispLine < numLines)
00187         {
00188             if (_lineInvert[dispLine])
00189                 dispMask = ~dispMask;
00190         }
00191                 
00192         // Work along the display row sending out data as we go
00193         // Noting that the first data we send out will be shunted to the end of the row
00194         int hScrollPos = _hScrollPos[scanRowIdx];
00195         int hScrollWidth = _hScrollWidth[scanRowIdx];
00196         if (hScrollWidth > dispBufWidth)
00197             hScrollWidth = dispBufWidth;
00198         int bitOffset = hScrollPos % 8;
00199         for (int byteIdx = (width / 8) - 1; byteIdx >= 0; byteIdx--)
00200         {
00201             // When a row is scrolled a single byte on the display can be a combination of two buffer bytes
00202             uint8_t* pByte1 = pBuf + ((byteIdx*8 + hScrollPos) % hScrollWidth) / 8;
00203             uint8_t* pByte2 = pBuf + (((byteIdx+1)*8 + hScrollPos) % hScrollWidth) / 8;
00204             uint8_t pixels = ((*pByte1) << bitOffset) + (((*pByte2) >> (8 - bitOffset)) % 256);
00205 
00206             // Reverse the bits so they come out in the right order                        
00207             pixels = reverseBits(pixels) ^ dispMask;   // reverse: mask = 0xff, normal: mask =0x00 
00208             for (uint8_t bit = 0; bit < 8; bit++) 
00209             {
00210                 clk = 0;
00211                 r1 = pixels & (0x80 >> bit);
00212                 clk = 1;
00213             }
00214         }
00215     }
00216 
00217     // Disable display
00218     oe = 1;
00219 
00220     // Select row (rows are muxed)
00221     int rowSel = LED_MATRIX_LEDS_VERTICALLY - 1 - scanRowIdx;
00222     a = (rowSel & 0x01);
00223     b = (rowSel & 0x02);
00224     c = (rowSel & 0x04);
00225     d = (rowSel & 0x08);
00226 
00227     // Latch data
00228     stb = 0;
00229     stb = 1;
00230 
00231     // Reenable display
00232     oe = 0;
00233 
00234     // Move the scan row on for next time
00235     scanRowIdx = (scanRowIdx + 1) & 0x0F;
00236 }
00237 
00238 void LEDMatrix::on()
00239 {
00240     _isEnabled = true;
00241 }
00242 
00243 void LEDMatrix::off()
00244 {
00245     _isEnabled = false;
00246     oe = 1;
00247 }
00248 
00249 bool LEDMatrix::getRowsToWorkOn(int lineIdx, int &startRow, int &numRows)
00250 {
00251     // lineIdx == -1 means all lines
00252     startRow = 0;
00253     numRows = LED_MATRIX_LEDS_VERTICALLY;
00254     if (lineIdx != -1)
00255     {
00256         if ((lineIdx < 0) || (lineIdx >= numLines))
00257             return false;
00258         numRows = height / numLines;
00259         startRow = lineIdx * numRows;
00260     }
00261     return true;
00262 }    
00263 
00264 void LEDMatrix::scrollReset(int lineIdx)
00265 {
00266     // Interpret param
00267     int startRow = 0;
00268     int numRows = LED_MATRIX_LEDS_VERTICALLY;
00269     if (!getRowsToWorkOn(lineIdx, startRow, numRows))
00270         return;
00271     // Reset scroll position all rows
00272     for (int i = startRow; i < startRow+numRows; i++)
00273         _hScrollPos[i] = 0;
00274 }
00275 
00276 void LEDMatrix::scroll(int lineIdx, bool scrollLeft)
00277 {
00278     // Interpret param
00279     int startRow = 0;
00280     int numRows = LED_MATRIX_LEDS_VERTICALLY;
00281     if (!getRowsToWorkOn(lineIdx, startRow, numRows))
00282         return;
00283     // Reset scroll position all rows
00284     for (int i = startRow; i < startRow+numRows; i++)
00285     {
00286         if (scrollLeft)
00287         {
00288             _hScrollPos[i]++;
00289             if (_hScrollPos[i] >= _hScrollWidth[i])
00290                 _hScrollPos[i] = 0;
00291         }
00292         else
00293         {
00294             _hScrollPos[i]--;
00295             if (_hScrollPos[i] < 0)
00296                 _hScrollPos[i] = _hScrollWidth[i]-1;
00297         }
00298     }
00299 }
00300 
00301 void LEDMatrix::scrollToPos(int lineIdx, int pos)
00302 {
00303     // Interpret param
00304     int startRow = 0;
00305     int numRows = LED_MATRIX_LEDS_VERTICALLY;
00306     if (!getRowsToWorkOn(lineIdx, startRow, numRows))
00307         return;
00308     // Check pos
00309     if ((pos < 0) || (pos >= dispBufWidth))
00310         return;
00311     // Reset scroll position all rows
00312     for (int i = startRow; i < startRow+numRows; i++)
00313         _hScrollPos[i] = pos;
00314 }
00315 
00316 void LEDMatrix::clear(int lineIdx)
00317 {
00318     // Interpret param
00319     int startRow = 0;
00320     int numRows = LED_MATRIX_LEDS_VERTICALLY;
00321     if (!getRowsToWorkOn(lineIdx, startRow, numRows))
00322         return;
00323     // Clear section
00324     uint8_t *ptr = _pDisplayBuf + startRow * (dispBufWidth / 8);
00325     int bytesToZero = numRows * (dispBufWidth / 8);
00326     for (int i = 0; i < bytesToZero; i++) 
00327         *ptr++ = 0x00;
00328     // Stop invert
00329     for (int i = 0; i < LED_MATRIX_MAX_LINES; i++)
00330         _lineInvert[i] = false;
00331 }
00332 
00333 void LEDMatrix::setScrollRate(int rate)
00334 {
00335     _scrollRate = rate;
00336 }
00337 
00338 void LEDMatrix::setFlashRate(int rate)
00339 {
00340     _flashRate = rate;
00341 }
00342 
00343 void LEDMatrix::setupScroll(int lineIdx, int scrollWidth, int scrollInc)
00344 {
00345     // Interpret param
00346     int startRow = 0;
00347     int numRows = LED_MATRIX_LEDS_VERTICALLY;
00348     if (!getRowsToWorkOn(lineIdx, startRow, numRows))
00349         return;
00350     scrollWidth = (((scrollWidth / 8) + 1) * 8);
00351     if (scrollWidth <= 0)
00352         return;
00353     for (int i = startRow; i < startRow+numRows; i++)
00354     {
00355         _hScrollPos[i] = 0;
00356         _hScrollWidth[i] = scrollWidth;
00357     }
00358     // Store the scroll increment
00359     int startLine = 0;
00360     int linesToProcess = numLines;
00361     if (lineIdx != -1)
00362     {
00363         if ((lineIdx < 0) || (lineIdx >= numLines))
00364             return;
00365         startLine = lineIdx;
00366         linesToProcess = 1;
00367     }
00368     for (int i = 0; i < linesToProcess; i++)
00369         _lineScrollInc[i+startLine] = scrollInc;
00370 }
00371 
00372 void LEDMatrix::setHighlight(int lineIdx, bool highlightOn)
00373 {
00374     // Store the highlight status
00375     int startLine = 0;
00376     int linesToProcess = numLines;
00377     if (lineIdx != -1)
00378     {
00379         if ((lineIdx < 0) || (lineIdx >= numLines))
00380             return;
00381         startLine = lineIdx;
00382         linesToProcess = 1;
00383     }
00384     for (int i = 0; i < linesToProcess; i++)
00385         _lineHighlight[i+startLine] = highlightOn;
00386 }
00387     
00388 void LEDMatrix::serviceEffects()
00389 {
00390     // Scroll logic
00391     _scrollCounter++;
00392     if (_scrollCounter >= _scrollRate)
00393     {
00394         _scrollCounter = 0;
00395         int rowsInLine = (height / numLines);
00396         for (int i = 0; i < numLines; i++)
00397         {
00398             for (int j = 0; j < rowsInLine; j++)
00399                 _hScrollPos[i*rowsInLine+j] += _lineScrollInc[i];
00400         }
00401     }
00402     
00403     // Update flash state
00404     _flashCounter++;
00405     if (_flashCounter >= _flashRate)
00406     {
00407         _flashCounter = 0;
00408         _flashState = !_flashState;
00409         for (int i = 0; i < numLines; i++)
00410             _lineInvert[i] = _lineHighlight[i] ? _flashState : false;
00411     }
00412 }
00413     
00414 // Reverse bits in byte
00415 uint8_t LEDMatrix::reverseBits(uint8_t b) 
00416 {
00417    b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
00418    b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
00419    b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
00420    return b;
00421 }
00422 
00423 // Display ASCII char
00424 int LEDMatrix::displayChar(int xPos, int yPos, char ch)
00425 {
00426     const int FONT_HEIGHT = 7;
00427     const int FONT_WIDTH = 5;
00428     
00429     // Find the width of the character
00430     uint8_t chTotalBits = 0;
00431     int chIdxInFont = (ch % 128) - 0x20;
00432     // Special case for £, euro and yen signs
00433     if ((ch >= 0xa3) && (ch <= 0xa5))
00434         chIdxInFont = ch - 0xa3 + 128 - ' ';
00435     for (int i = 0; i < FONT_HEIGHT; i++)
00436     {
00437         uint8_t chBits = font5x7[chIdxInFont][i];
00438         chTotalBits |= chBits;
00439     }
00440     uint8_t rightShift = 0;
00441     for (int j = 0; j < FONT_WIDTH; j++)
00442     {
00443         if (chTotalBits & 0x01)
00444             break;
00445         chTotalBits = chTotalBits >> 1;
00446         rightShift++;
00447     }
00448     uint8_t charWidth = 8;
00449     for (int j = 0; j < FONT_WIDTH+1; j++)
00450     {
00451         if (chTotalBits & 0x80)
00452             break;
00453         chTotalBits = chTotalBits << 1;
00454         charWidth--;
00455     }
00456     
00457     // display character
00458     int xOffset = xPos / 8;
00459     int bitOffset = xPos % 8;
00460 //    printf("%c %d %d %d %d\n", ch, charPos, charWidth, xOffset, bitOffset);
00461     if (xOffset >= (dispBufWidth/8))
00462         return 0;
00463         
00464     // Copy the bits of the character into the buffer
00465     for (int i = 0; i < FONT_HEIGHT; i++)
00466     {
00467         // Check we don't go off the display buffer
00468         int rowIdx = i + yPos;
00469         if (rowIdx > height)
00470             break;
00471             
00472         // 
00473         uint8_t chBits = font5x7[chIdxInFont][i] >> rightShift;
00474         int dispBufPos = (rowIdx * (dispBufWidth/8) + xOffset);
00475         _pDisplayBuf[dispBufPos] |= ((chBits << (8 - charWidth)) >> bitOffset);
00476         if (xOffset + 1 < (dispBufWidth/8))
00477             _pDisplayBuf[dispBufPos + 1] |= ((chBits << (8-bitOffset)) << (8 - charWidth));
00478     }
00479 
00480     return charWidth;
00481 }
00482 
00483 // Display a large digit
00484 int LEDMatrix::displayLargeDigit(int curX, int curY, char ch)
00485 {
00486     // Bounds check
00487     if ((ch < '0') || (ch > '9'))
00488         return 0;
00489         
00490     // Get character data
00491     const uint8_t* charData = bigFont[ch-'0'];
00492     int numLines = sizeof(bigFont[0])/sizeof(bigFont[0][0]);
00493     
00494     // Find the position and width of the character
00495     uint16_t chTotalBits = 0;
00496     for (int i = 0; i < numLines; i++)
00497         chTotalBits |= charData[i];
00498     uint16_t rightShift = 0;
00499     for (; rightShift < 16; rightShift++)
00500     {
00501         if (chTotalBits & 0x01)
00502             break;
00503         chTotalBits = chTotalBits >> 1;
00504     }
00505     uint16_t charWidth = 16;
00506     for (; charWidth > 0; charWidth--)
00507     {
00508         if (chTotalBits & 0x8000)
00509             break;
00510         chTotalBits = chTotalBits << 1;
00511     }
00512 
00513     // display character
00514     int xOffset = curX / 8;
00515     int bitOffset = curX % 8;
00516 //    printf("%c %d %d %d %d %d\n", ch, curX, charWidth, rightShift, xOffset, bitOffset);
00517     if (xOffset >= (dispBufWidth/8))
00518         return 0;
00519     for (int i = 0; i < numLines; i++)
00520     {
00521         uint32_t chBits = charData[i] >> rightShift;
00522         chBits = (chBits << (24-charWidth)) >> bitOffset;
00523         for (int j = 0; j < 3; j++)
00524         {
00525             if (xOffset + j >= (dispBufWidth/8))
00526                 break;
00527             _pDisplayBuf[(i + curY) * (dispBufWidth/8) + xOffset + j] |= ((chBits >> (16-j*8)) & 0xff);
00528         }
00529     }
00530 
00531     return charWidth;
00532 }
00533 
00534 // Display line of characters
00535 int LEDMatrix::displayLine(int lineIdx, const char* line)
00536 {
00537     if (lineIdx >= numLines)
00538         lineIdx = 0;
00539     _isBusy = true;
00540     int curX = 0;
00541     int curY = lineIdx * height/numLines;
00542     for (int chIdx = 0; chIdx < strlen(line); chIdx++)
00543     {
00544         curX += displayChar(curX, curY, line[chIdx]) + _charSeparation;
00545     }
00546     _isBusy = false;
00547     return curX;
00548 }
00549