Forked LEDMatrix and added horizontal scrolling
Fork of LEDMatrix by
LEDMatrix.cpp
- Committer:
- Bobty
- Date:
- 2016-01-15
- Revision:
- 5:334fc002e200
- Parent:
- 4:40d4afefcd74
File content as of revision 5:334fc002e200:
/** * LED Matrix library for http://www.seeedstudio.com/depot/ultrathin-16x32-red-led-matrix-panel-p-1582.html * The LED Matrix panel has 32x16 pixels. Several panel can be combined together as a large screen. * * Coordinate & Connection (mbed -> panel 0 -> panel 1 -> ...) * (0, 0) (0, 0) * +--------+--------+--------+ +--------+--------+ * | 5 | 3 | 1 | | 1 | 0 | * | | | | | | |<----- mbed * +--------+--------+--------+ +--------+--------+ * | 4 | 2 | 0 | (64, 16) * | | | |<----- mbed * +--------+--------+--------+ * (96, 32) * Copyright (c) 2013 Seeed Technology Inc. * @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); } #else #define ASSERT(e) #endif LEDMatrix::LEDMatrix(PinName pinA, PinName pinB, PinName pinC, PinName pinD, PinName pinOE, PinName pinR1, PinName pinSTB, PinName pinCLK) : a(pinA), b(pinB), c(pinC), d(pinD), oe(pinOE), r1(pinR1), stb(pinSTB), clk(pinCLK) { this->clk = clk; this->r1 = r1; this->stb = stb; this->oe = oe; this->a = a; this->b = b; this->c = c; this->d = d; mask = 0xff; _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; _lineHighlight[i] = false; _lineInvert[i] = false; } _isBusy = false; _charSeparation = 1; _flashCounter = 0; _flashRate = 50; _flashState = false; _scrollCounter = 0; _scrollRate = 7; } void LEDMatrix::begin(uint8_t *_pDisplayBuf, uint16_t width, uint16_t height, uint16_t dispBufWidth, uint16_t numLines, int charSeparation, int flashRate, int scrollRate) { ASSERT(0 == (width % LED_MATRIX_LEDS_HORIZONTALLY)); ASSERT(0 == (scroll_width % LED_MATRIX_LEDS_HORIZONTALLY)); ASSERT(0 == (height % LED_MATRIX_LEDS_VERTICALLY)); this->_pDisplayBuf = _pDisplayBuf; this->width = width; this->height = height; this->dispBufWidth = dispBufWidth; this->numLines = numLines; this->_charSeparation = charSeparation; if (flashRate > 0) this->_flashRate = flashRate; if (scrollRate > 0) this->_scrollRate = scrollRate; 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; } _isEnabled = true; } void LEDMatrix::drawPoint(uint16_t x, uint16_t y, uint8_t pixel) { ASSERT(dispBufWidth > x); ASSERT(height > y); uint8_t *byte = _pDisplayBuf + (height - 1 - y) * dispBufWidth / 8 + x / 8; uint8_t bit = x % 8; if (pixel) { *byte |= 0x80 >> bit; } 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++) { drawPoint(x, y, pixel); } } } void LEDMatrix::drawImage(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t *image) { ASSERT(0 == ((x2 - x1) % 8)); 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 = _pDisplayBuf; // for (uint16_t i = 0; i < (dispBufWidth * height / 8); i++) // { // *ptr = 0x00; // ptr++; // } //} void LEDMatrix::invertColour() { mask = ~mask; } uint8_t LEDMatrix::isInvertedColour() { return mask; } void LEDMatrix::scan() { // On each call this function handles one row of the display static uint8_t scanRowIdx = 0; // Check if being updated if (_isBusy) return; // Check if display enabled if (!_isEnabled) return; // 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++) { // Calculate buffer position within this unit uint8_t *pBuf = pBufBase + rowIdx * (dispBufWidth / 8) * LED_MATRIX_LEDS_VERTICALLY; // Work out which display line we're in uint8_t dispMask = mask; int rowsInLine = (height/numLines); int dispLine = scanRowIdx / rowsInLine; if (dispLine < numLines) { if (_lineInvert[dispLine]) dispMask = ~dispMask; } // 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--) { // 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) ^ dispMask; // reverse: mask = 0xff, normal: mask =0x00 for (uint8_t bit = 0; bit < 8; bit++) { clk = 0; r1 = pixels & (0x80 >> bit); clk = 1; } } } // Disable display oe = 1; // 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 stb = 0; stb = 1; // Reenable display oe = 0; // Move the scan row on for next time scanRowIdx = (scanRowIdx + 1) & 0x0F; } void LEDMatrix::on() { _isEnabled = true; } void LEDMatrix::off() { _isEnabled = false; oe = 1; } 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) { // 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; // Stop invert for (int i = 0; i < LED_MATRIX_MAX_LINES; i++) _lineInvert[i] = false; } void LEDMatrix::setScrollRate(int rate) { _scrollRate = rate; } void LEDMatrix::setFlashRate(int rate) { _flashRate = rate; } 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; } // Store the scroll increment int startLine = 0; int linesToProcess = numLines; if (lineIdx != -1) { if ((lineIdx < 0) || (lineIdx >= numLines)) return; startLine = lineIdx; linesToProcess = 1; } for (int i = 0; i < linesToProcess; i++) _lineScrollInc[i+startLine] = scrollInc; } void LEDMatrix::setHighlight(int lineIdx, bool highlightOn) { // Store the highlight status int startLine = 0; int linesToProcess = numLines; if (lineIdx != -1) { if ((lineIdx < 0) || (lineIdx >= numLines)) return; startLine = lineIdx; linesToProcess = 1; } for (int i = 0; i < linesToProcess; i++) _lineHighlight[i+startLine] = highlightOn; } void LEDMatrix::serviceEffects() { // Scroll logic _scrollCounter++; if (_scrollCounter >= _scrollRate) { _scrollCounter = 0; int rowsInLine = (height / numLines); for (int i = 0; i < numLines; i++) { for (int j = 0; j < rowsInLine; j++) _hScrollPos[i*rowsInLine+j] += _lineScrollInc[i]; } } // Update flash state _flashCounter++; if (_flashCounter >= _flashRate) { _flashCounter = 0; _flashState = !_flashState; for (int i = 0; i < numLines; i++) _lineInvert[i] = _lineHighlight[i] ? _flashState : false; } } // 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; } // Display ASCII char int LEDMatrix::displayChar(int xPos, int yPos, char ch) { 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; } // Display a large digit int LEDMatrix::displayLargeDigit(int curX, int curY, char ch) { // 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; }