Forked LEDMatrix and added horizontal scrolling

Fork of LEDMatrix by Yihui Xiong

Files at this revision

API Documentation at this revision

Comitter:
Bobty
Date:
Thu Jan 14 12:57:59 2016 +0000
Parent:
1:79cf2e115449
Child:
3:1e06e89bc0c9
Commit message:
Improved scrolling; Now handles multiple independent lines of text; Converted the coordinate system to more conventional one

Changed in this revision

LEDMatrix.cpp Show annotated file Show diff for this revision Revisions of this file
LEDMatrix.h Show annotated file Show diff for this revision Revisions of this file
--- 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;
+}
+
--- a/LEDMatrix.h	Mon Jan 11 21:05:53 2016 +0000
+++ b/LEDMatrix.h	Thu Jan 14 12:57:59 2016 +0000
@@ -24,19 +24,25 @@
 
 #include "mbed.h"
 
-class LEDMatrix {
+const int LED_MATRIX_LEDS_HORIZONTALLY = 32;
+const int LED_MATRIX_LEDS_VERTICALLY = 16;
+const int LED_MATRIX_MAX_LINES = 16;
+
+class LEDMatrix 
+{
 public:
+    
     LEDMatrix(PinName pinA, PinName pinB, PinName pinC, PinName pinD, PinName pinOE, PinName pinR1, PinName pinSTB, PinName pinCLK);
     
     /**
      * set the display's display buffer and number, the buffer's size must be not less than 512 * number / 8 bytes
-     * @param displaybuf    display buffer
+     * @param pDisplayBuf    display buffer
      * @param number        panels' number
      */
-    void begin(uint8_t *displaybuf, uint16_t width, uint16_t height);
+    void begin(uint8_t *pDisplayBuf, uint16_t width, uint16_t height, uint16_t scrollWidth, uint16_t numLines, int charSeparation = 1);
 
     /**
-     * draw a point
+     * draw a point - origin is like a graph with 0,0 at the lower-left corner
      * @param x     x
      * @param y     y
      * @param pixel 0: led off, >0: led on
@@ -44,7 +50,7 @@
     void drawPoint(uint16_t x, uint16_t y, uint8_t pixel);
 
     /**
-     * draw a rect
+     * draw a rect - origin is like a graph with 0,0 at the lower-left corner
      * @param (x1, y1)   top-left position
      * @param (x2, y2)   bottom-right position, not included in the rect
      * @param pixel      0: rect off, >0: rect on
@@ -60,35 +66,49 @@
     void drawImage(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t *image);
 
     /**
-     * Set screen buffer to zero
-     */
-    void clear();
-
-    /**
      * turn off 1/16 leds and turn on another 1/16 leds
      */
     void scan();
 
-    void reverse();
+    void invertColour();
 
-    uint8_t isReversed();
+    uint8_t isInvertedColour();
 
     void on();
 
     void off();
     
-    void scrollLeft();
-    void scrollReset();
-    void scrollToPos(int pos);
+    // Passing lineIdx == -1 to the following functions resets all lines
+    // Lines are numbered starting at 0
+    void clear(int lineIdx = -1);
+    void scroll(int lineIdx, bool scrollLeft);
+    void scrollReset(int lineIdx = -1);
+    void scrollToPos(int lineIdx, int pos);
+    void setupScroll(int lineIdx, int scrollWidth, int scrollInc);
 
+    uint8_t reverseBits(uint8_t b);
+    int displayChar(int xPos, int yPos, char ch);
+    int displayLargeDigit(int curX, int curY, char ch);
+    int displayLine(int lineIdx, const char* line);
+
+    // Called frequently and regularly to handle effects like scrolling
+    void serviceEffects();
+    
 private:
+    bool getRowsToWorkOn(int lineIdx, int &startRow, int &numRows);
     DigitalOut a, b, c, d, oe, r1, stb, clk;
-    uint8_t *displaybuf;
+    uint8_t *_pDisplayBuf;
     uint16_t width;
     uint16_t height;
+    uint16_t dispBufWidth;
+    uint16_t numLines;
     uint8_t  mask;
-    uint8_t  state;
-    int _horizontalScrollPos;
+    bool _isEnabled;
+    int _hScrollPos[LED_MATRIX_LEDS_VERTICALLY];
+    int _hScrollWidth[LED_MATRIX_LEDS_VERTICALLY];
+    bool _isBusy;
+    int _charSeparation;
+    int _lineScrollInc[LED_MATRIX_MAX_LINES];
 };
 
 #endif