Forked LEDMatrix and added horizontal scrolling
Fork of LEDMatrix by
Diff: LEDMatrix.cpp
- 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; +} +