Forked LEDMatrix and added horizontal scrolling
Fork of LEDMatrix by
Revision 2:cd2da920cf98, committed 2016-01-14
- 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
