Forked LEDMatrix and added horizontal scrolling
Fork of LEDMatrix by
LEDMatrix.cpp
- Committer:
- Bobty
- Date:
- 2016-01-15
- Revision:
- 4:40d4afefcd74
- Parent:
- 3:1e06e89bc0c9
- Child:
- 5:334fc002e200
File content as of revision 4:40d4afefcd74:
/**
* 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;
_lineAlert[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;
bool invertRow = false;
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;
}
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::setAlert(int lineIdx, bool alertOn)
{
// Store the alert 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++)
_lineAlert[i+startLine] = alertOn;
}
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] = _lineAlert[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;
}
