Forked LEDMatrix and added horizontal scrolling

Fork of LEDMatrix by Yihui Xiong

Revision:
2:cd2da920cf98
Parent:
1:79cf2e115449
Child:
3:1e06e89bc0c9
diff -r 79cf2e115449 -r cd2da920cf98 LEDMatrix.cpp
--- a/LEDMatrix.cpp	Mon Jan 11 21:05:53 2016 +0000
+++ b/LEDMatrix.cpp	Thu Jan 14 12:57:59 2016 +0000
@@ -16,10 +16,14 @@
  *  @auther     Yihui Xiong
  *  @date       Nov 8, 2013
  *  @license    Apache
+ *
+ * Heavily modified by Rob Dobson, 2016
  */
 
 #include "LEDMatrix.h"
 #include "mbed.h"
+#include "fontBig.h"
+#include "font5x7.h"
 
 #if 0
 #define ASSERT(e)   if (!(e)) { Serial.println(#e); while (1); }
@@ -40,41 +44,66 @@
     this->d = d;
 
     mask = 0xff;
-    state = 0;
-    _horizontalScrollPos = 0;
+    _isEnabled = false;
+    for (int i = 0; i < LED_MATRIX_LEDS_VERTICALLY; i++)
+    {
+        _hScrollPos[i] = 0;
+        _hScrollWidth[i] = LED_MATRIX_LEDS_HORIZONTALLY;
+    }
+    for (int i = 0; i < LED_MATRIX_MAX_LINES; i++)
+    {
+        _lineScrollInc[i] = 0;
+    }
+    _isBusy = false;
+    _charSeparation = 1;
 }
 
-void LEDMatrix::begin(uint8_t *displaybuf, uint16_t width, uint16_t height)
+void LEDMatrix::begin(uint8_t *_pDisplayBuf, uint16_t width, uint16_t height, uint16_t dispBufWidth, uint16_t numLines, int charSeparation)
 {
-    ASSERT(0 == (width % 32));
-    ASSERT(0 == (height % 16));
+    ASSERT(0 == (width % LED_MATRIX_LEDS_HORIZONTALLY));
+    ASSERT(0 == (scroll_width % LED_MATRIX_LEDS_HORIZONTALLY));
+    ASSERT(0 == (height % LED_MATRIX_LEDS_VERTICALLY));
 
-    this->displaybuf = displaybuf;
+    this->_pDisplayBuf = _pDisplayBuf;
     this->width = width;
     this->height = height;
+    this->dispBufWidth = dispBufWidth;
+    this->numLines = numLines;
+    this->_charSeparation = charSeparation;
+    if (numLines > LED_MATRIX_MAX_LINES)
+        this->numLines = LED_MATRIX_MAX_LINES;
+    for (int i = 0; i < LED_MATRIX_LEDS_VERTICALLY; i++)
+    {
+        this->_hScrollWidth[i] = dispBufWidth;
+    }
     
-    state = 1;
+    _isEnabled = true;
 }
 
 void LEDMatrix::drawPoint(uint16_t x, uint16_t y, uint8_t pixel)
 {
-    ASSERT(width > x);
+    ASSERT(dispBufWidth > x);
     ASSERT(height > y);
 
-    uint8_t *byte = displaybuf + (height - 1 - y) * width / 8 + (width - 1 - x) / 8;
-    uint8_t  bit = (width - 1 - x) % 8;
+    uint8_t *byte = _pDisplayBuf + (height - 1 - y) * dispBufWidth / 8 + x / 8;
+    uint8_t  bit = x % 8;
 
-    if (pixel) {
+    if (pixel)
+    {
         *byte |= 0x80 >> bit;
-    } else {
+    }
+    else 
+    {
         *byte &= ~(0x80 >> bit);
     }
 }
 
 void LEDMatrix::drawRect(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t pixel)
 {
-    for (uint16_t x = x1; x < x2; x++) {
-        for (uint16_t y = y1; y < y2; y++) {
+    for (uint16_t x = x1; x < x2; x++) 
+    {
+        for (uint16_t y = y1; y < y2; y++) 
+        {
             drawPoint(x, y, pixel);
         }
     }
@@ -84,115 +113,369 @@
 {
     ASSERT(0 == ((x2 - x1) % 8));
 
-    for (uint16_t x = x1; x < x2; x++) {
-        for (uint16_t y = y1; y < y2; y++) {
+    for (uint16_t x = x1; x < x2; x++) 
+    {
+        for (uint16_t y = y1; y < y2; y++) 
+        {
             uint8_t *byte = image + x * 8 + y / 8;
             uint8_t  bit = 7 - (y % 8);
             uint8_t  pixel = (*byte >> bit) & 1;
-
             drawPoint(x, y, pixel);
         }
     }
 }
 
-void LEDMatrix::clear()
-{
-    uint8_t *ptr = displaybuf;
-    for (uint16_t i = 0; i < (width * height / 8); i++) {
-        *ptr = 0x00;
-        ptr++;
-    }
-}
+//void LEDMatrix::clear()
+//{
+//    uint8_t *ptr = _pDisplayBuf;
+//    for (uint16_t i = 0; i < (dispBufWidth * height / 8); i++) 
+//    {
+//        *ptr = 0x00;
+//        ptr++;
+//    }
+//}
 
-void LEDMatrix::reverse()
+void LEDMatrix::invertColour()
 {
     mask = ~mask;
 }
 
-uint8_t LEDMatrix::isReversed()
+uint8_t LEDMatrix::isInvertedColour()
 {
     return mask;
 }
 
 void LEDMatrix::scan()
 {
-    static uint8_t row = 0;
+    // On each call this function handles one row of the display
+    static uint8_t scanRowIdx = 0;
 
-    if (!state) 
-    {
+    // Check if being updated
+    if (_isBusy)
         return;
-    }
+
+    // Check if display enabled
+    if (!_isEnabled)
+        return;
 
-    uint8_t *head = displaybuf + row * (width / 8);
-    for (uint8_t line = 0; line < (height / 16); line++) 
+    // Base of this scan row in buffer to display
+    uint8_t *pBufBase = _pDisplayBuf + scanRowIdx * (dispBufWidth / 8);
+    
+    // Handle multiple units vertically
+    for (int rowIdx = 0; rowIdx < (height / LED_MATRIX_LEDS_VERTICALLY); rowIdx++) 
     {
-        uint8_t *ptr = head;
-        head += line * width * 2;
+        // Calculate buffer position within this unit
+        uint8_t *pBuf = pBufBase + rowIdx * (dispBufWidth / 8) * LED_MATRIX_LEDS_VERTICALLY;
 
-        for (uint8_t byteIdx = 0; byteIdx < (width / 8); byteIdx++)
+        // Work along the display row sending out data as we go
+        // Noting that the first data we send out will be shunted to the end of the row
+        int hScrollPos = _hScrollPos[scanRowIdx];
+        int hScrollWidth = _hScrollWidth[scanRowIdx];
+        if (hScrollWidth > dispBufWidth)
+            hScrollWidth = dispBufWidth;
+        int bitOffset = hScrollPos % 8;
+        for (int byteIdx = (width / 8) - 1; byteIdx >= 0; byteIdx--)
         {
-            uint8_t* pByte1 = ptr + (((byteIdx*8 + width - _horizontalScrollPos + 7) % width) / 8);
-            uint8_t* pByte2 = ptr + (((byteIdx*8 + width - _horizontalScrollPos - 1) % width) / 8);
-            int bitOffset = _horizontalScrollPos % 8;
-            uint8_t pixels = ((*pByte1) >> bitOffset) + (((*pByte2) << (8 - bitOffset)) % 256);
-            
-//            uint8_t pixels = *ptr;
-//            ptr++;
-            pixels = pixels ^ mask;   // reverse: mask = 0xff, normal: mask =0x00 
+            // When a row is scrolled a single byte on the display can be a combination of two buffer bytes
+            uint8_t* pByte1 = pBuf + ((byteIdx*8 + hScrollPos) % hScrollWidth) / 8;
+            uint8_t* pByte2 = pBuf + (((byteIdx+1)*8 + hScrollPos) % hScrollWidth) / 8;
+            uint8_t pixels = ((*pByte1) << bitOffset) + (((*pByte2) >> (8 - bitOffset)) % 256);
+
+            // Reverse the bits so they come out in the right order                        
+            pixels = reverseBits(pixels) ^ mask;   // reverse: mask = 0xff, normal: mask =0x00 
             for (uint8_t bit = 0; bit < 8; bit++) 
             {
                 clk = 0;
                 r1 = pixels & (0x80 >> bit);
-//                wait_us(1);
                 clk = 1;
             }
         }
     }
 
-    oe = 1;              // disable display
+    // Disable display
+    oe = 1;
 
-    // select row
-    a = (row & 0x01);
-    b = (row & 0x02);
-    c = (row & 0x04);
-    d = (row & 0x08);
+    // Select row (rows are muxed)
+    int rowSel = LED_MATRIX_LEDS_VERTICALLY - 1 - scanRowIdx;
+    a = (rowSel & 0x01);
+    b = (rowSel & 0x02);
+    c = (rowSel & 0x04);
+    d = (rowSel & 0x08);
 
-    // latch data
+    // Latch data
     stb = 0;
-//    wait_us(1);
     stb = 1;
 
-    oe = 0;              // enable display
+    // Reenable display
+    oe = 0;
 
-    row = (row + 1) & 0x0F;
+    // Move the scan row on for next time
+    scanRowIdx = (scanRowIdx + 1) & 0x0F;
 }
 
 void LEDMatrix::on()
 {
-    state = 1;
+    _isEnabled = true;
 }
 
 void LEDMatrix::off()
 {
-    state = 0;
+    _isEnabled = false;
     oe = 1;
 }
 
-void LEDMatrix::scrollReset()
+bool LEDMatrix::getRowsToWorkOn(int lineIdx, int &startRow, int &numRows)
+{
+    // lineIdx == -1 means all lines
+    startRow = 0;
+    numRows = LED_MATRIX_LEDS_VERTICALLY;
+    if (lineIdx != -1)
+    {
+        if ((lineIdx < 0) || (lineIdx >= numLines))
+            return false;
+        numRows = height / numLines;
+        startRow = lineIdx * numRows;
+    }
+    return true;
+}    
+
+void LEDMatrix::scrollReset(int lineIdx)
+{
+    // Interpret param
+    int startRow = 0;
+    int numRows = LED_MATRIX_LEDS_VERTICALLY;
+    if (!getRowsToWorkOn(lineIdx, startRow, numRows))
+        return;
+    // Reset scroll position all rows
+    for (int i = startRow; i < startRow+numRows; i++)
+        _hScrollPos[i] = 0;
+}
+
+void LEDMatrix::scroll(int lineIdx, bool scrollLeft)
+{
+    // Interpret param
+    int startRow = 0;
+    int numRows = LED_MATRIX_LEDS_VERTICALLY;
+    if (!getRowsToWorkOn(lineIdx, startRow, numRows))
+        return;
+    // Reset scroll position all rows
+    for (int i = startRow; i < startRow+numRows; i++)
+    {
+        if (scrollLeft)
+        {
+            _hScrollPos[i]++;
+            if (_hScrollPos[i] >= _hScrollWidth[i])
+                _hScrollPos[i] = 0;
+        }
+        else
+        {
+            _hScrollPos[i]--;
+            if (_hScrollPos[i] < 0)
+                _hScrollPos[i] = _hScrollWidth[i]-1;
+        }
+    }
+}
+
+void LEDMatrix::scrollToPos(int lineIdx, int pos)
 {
-    _horizontalScrollPos = 0;
+    // Interpret param
+    int startRow = 0;
+    int numRows = LED_MATRIX_LEDS_VERTICALLY;
+    if (!getRowsToWorkOn(lineIdx, startRow, numRows))
+        return;
+    // Check pos
+    if ((pos < 0) || (pos >= dispBufWidth))
+        return;
+    // Reset scroll position all rows
+    for (int i = startRow; i < startRow+numRows; i++)
+        _hScrollPos[i] = pos;
+}
+
+void LEDMatrix::clear(int lineIdx)
+{
+    // Interpret param
+    int startRow = 0;
+    int numRows = LED_MATRIX_LEDS_VERTICALLY;
+    if (!getRowsToWorkOn(lineIdx, startRow, numRows))
+        return;
+    // Clear section
+    uint8_t *ptr = _pDisplayBuf + startRow * (dispBufWidth / 8);
+    int bytesToZero = numRows * (dispBufWidth / 8);
+    for (int i = 0; i < bytesToZero; i++) 
+        *ptr++ = 0x00;
+}
+
+void LEDMatrix::setupScroll(int lineIdx, int scrollWidth, int scrollInc)
+{
+    // Interpret param
+    int startRow = 0;
+    int numRows = LED_MATRIX_LEDS_VERTICALLY;
+    if (!getRowsToWorkOn(lineIdx, startRow, numRows))
+        return;
+    scrollWidth = (((scrollWidth / 8) + 1) * 8);
+    if (scrollWidth <= 0)
+        return;
+    for (int i = startRow; i < startRow+numRows; i++)
+    {
+        _hScrollPos[i] = 0;
+        _hScrollWidth[i] = scrollWidth;
+        _lineScrollInc[i] = scrollInc;
+    }
+//    int rowsInALine = height / numLines;
+//    for (int i = 0; i < rowsInALine; i++)
+//        _hScrollWidth[lineIdx*rowsInALine + i] = scrollWidth;
+}
+
+//void LEDMatrix::setLineScrollInc(int lineIdx, int scrollInc)
+//{
+//    if ((lineIdx < 0) || (lineIdx >= numLines))
+//        return;
+//    int rowsInALine = height / numLines;
+//    for (int i = 0; i < rowsInALine; i++)
+//        _lineScrollInc[lineIdx*rowsInALine + i] = scrollInc;
+//}
+
+void LEDMatrix::serviceEffects()
+{
+    for (int i = 0; i < LED_MATRIX_MAX_LINES; i++)
+    {
+        _hScrollPos[i] += _lineScrollInc[i];
+    }
+}
+    
+// Reverse bits in byte
+uint8_t LEDMatrix::reverseBits(uint8_t b) 
+{
+   b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
+   b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
+   b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
+   return b;
 }
 
-void LEDMatrix::scrollLeft()
+// Display ASCII char
+int LEDMatrix::displayChar(int xPos, int yPos, char ch)
 {
-    _horizontalScrollPos++;
-    if (_horizontalScrollPos >= width)
-        _horizontalScrollPos = 0;
+    const int FONT_HEIGHT = 7;
+    const int FONT_WIDTH = 5;
+    
+    // Find the width of the character
+    uint8_t chTotalBits = 0;
+    int chIdxInFont = (ch % 128) - 0x20;
+    // Special case for £, euro and yen signs
+    if ((ch >= 0xa3) && (ch <= 0xa5))
+        chIdxInFont = ch - 0xa3 + 128 - ' ';
+    for (int i = 0; i < FONT_HEIGHT; i++)
+    {
+        uint8_t chBits = font5x7[chIdxInFont][i];
+        chTotalBits |= chBits;
+    }
+    uint8_t rightShift = 0;
+    for (int j = 0; j < FONT_WIDTH; j++)
+    {
+        if (chTotalBits & 0x01)
+            break;
+        chTotalBits = chTotalBits >> 1;
+        rightShift++;
+    }
+    uint8_t charWidth = 8;
+    for (int j = 0; j < FONT_WIDTH+1; j++)
+    {
+        if (chTotalBits & 0x80)
+            break;
+        chTotalBits = chTotalBits << 1;
+        charWidth--;
+    }
+    
+    // display character
+    int xOffset = xPos / 8;
+    int bitOffset = xPos % 8;
+//    printf("%c %d %d %d %d\n", ch, charPos, charWidth, xOffset, bitOffset);
+    if (xOffset >= (dispBufWidth/8))
+        return 0;
+        
+    // Copy the bits of the character into the buffer
+    for (int i = 0; i < FONT_HEIGHT; i++)
+    {
+        // Check we don't go off the display buffer
+        int rowIdx = i + yPos;
+        if (rowIdx > height)
+            break;
+            
+        // 
+        uint8_t chBits = font5x7[chIdxInFont][i] >> rightShift;
+        int dispBufPos = (rowIdx * (dispBufWidth/8) + xOffset);
+        _pDisplayBuf[dispBufPos] |= ((chBits << (8 - charWidth)) >> bitOffset);
+        if (xOffset + 1 < (dispBufWidth/8))
+            _pDisplayBuf[dispBufPos + 1] |= ((chBits << (8-bitOffset)) << (8 - charWidth));
+    }
+
+    return charWidth;
 }
 
-void LEDMatrix::scrollToPos(int pos)
+// Display a large digit
+int LEDMatrix::displayLargeDigit(int curX, int curY, char ch)
 {
-    if ((pos <= 0) || (pos >= width))
-        return;
-    _horizontalScrollPos = pos;
+    // Bounds check
+    if ((ch < '0') || (ch > '9'))
+        return 0;
+        
+    // Get character data
+    const uint8_t* charData = bigFont[ch-'0'];
+    int numLines = sizeof(bigFont[0])/sizeof(bigFont[0][0]);
+    
+    // Find the position and width of the character
+    uint16_t chTotalBits = 0;
+    for (int i = 0; i < numLines; i++)
+        chTotalBits |= charData[i];
+    uint16_t rightShift = 0;
+    for (; rightShift < 16; rightShift++)
+    {
+        if (chTotalBits & 0x01)
+            break;
+        chTotalBits = chTotalBits >> 1;
+    }
+    uint16_t charWidth = 16;
+    for (; charWidth > 0; charWidth--)
+    {
+        if (chTotalBits & 0x8000)
+            break;
+        chTotalBits = chTotalBits << 1;
+    }
+
+    // display character
+    int xOffset = curX / 8;
+    int bitOffset = curX % 8;
+//    printf("%c %d %d %d %d %d\n", ch, curX, charWidth, rightShift, xOffset, bitOffset);
+    if (xOffset >= (dispBufWidth/8))
+        return 0;
+    for (int i = 0; i < numLines; i++)
+    {
+        uint32_t chBits = charData[i] >> rightShift;
+        chBits = (chBits << (24-charWidth)) >> bitOffset;
+        for (int j = 0; j < 3; j++)
+        {
+            if (xOffset + j >= (dispBufWidth/8))
+                break;
+            _pDisplayBuf[(i + curY) * (dispBufWidth/8) + xOffset + j] |= ((chBits >> (16-j*8)) & 0xff);
+        }
+    }
+
+    return charWidth;
 }
+
+// Display line of characters
+int LEDMatrix::displayLine(int lineIdx, const char* line)
+{
+    if (lineIdx >= numLines)
+        lineIdx = 0;
+    _isBusy = true;
+    int curX = 0;
+    int curY = lineIdx * height/numLines;
+    for (int chIdx = 0; chIdx < strlen(line); chIdx++)
+    {
+        curX += displayChar(curX, curY, line[chIdx]) + _charSeparation;
+    }
+    _isBusy = false;
+    return curX;
+}
+