mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-12-25 21:41:23 +01:00
384 lines
12 KiB
C++
384 lines
12 KiB
C++
|
/* Arduino SSD1306Ascii Library
|
||
|
* Copyright (C) 2015 by William Greiman
|
||
|
*
|
||
|
* This file is part of the Arduino SSD1306Ascii Library
|
||
|
*
|
||
|
* This Library is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation, either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This Library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with the Arduino SSD1306Ascii Library. If not, see
|
||
|
* <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
#include "SSD1306Ascii.h"
|
||
|
//------------------------------------------------------------------------------
|
||
|
uint8_t SSD1306Ascii::charWidth(uint8_t c) const {
|
||
|
if (!m_font) {
|
||
|
return 0;
|
||
|
}
|
||
|
uint8_t first = readFontByte(m_font + FONT_FIRST_CHAR);
|
||
|
uint8_t count = readFontByte(m_font + FONT_CHAR_COUNT);
|
||
|
if (c < first || c >= (first + count)) {
|
||
|
return 0;
|
||
|
}
|
||
|
if (fontSize() > 1) {
|
||
|
// Proportional font.
|
||
|
return m_magFactor*readFontByte(m_font + FONT_WIDTH_TABLE + c - first);
|
||
|
}
|
||
|
// Fixed width font.
|
||
|
return m_magFactor*readFontByte(m_font + FONT_WIDTH);
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
void SSD1306Ascii::clear() {
|
||
|
#if INCLUDE_SCROLLING
|
||
|
m_pageOffset = 0;
|
||
|
setStartLine(0);
|
||
|
#endif // INCLUDE_SCROLLING
|
||
|
clear(0, displayWidth() - 1, 0 , displayRows() - 1);
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
void SSD1306Ascii::clear(uint8_t c0, uint8_t c1, uint8_t r0, uint8_t r1) {
|
||
|
// Cancel skip character pixels.
|
||
|
m_skip = 0;
|
||
|
|
||
|
// Insure only rows on display will be cleared.
|
||
|
if (r1 >= displayRows()) r1 = displayRows() - 1;
|
||
|
|
||
|
for (uint8_t r = r0; r <= r1; r++) {
|
||
|
setCursor(c0, r);
|
||
|
for (uint8_t c = c0; c <= c1; c++) {
|
||
|
// Insure clear() writes zero. result is (m_invertMask^m_invertMask).
|
||
|
ssd1306WriteRamBuf(m_invertMask);
|
||
|
}
|
||
|
}
|
||
|
setCursor(c0, r0);
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
void SSD1306Ascii::clearToEOL() {
|
||
|
clear(m_col, displayWidth() -1, m_row, m_row + fontRows() - 1);
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
void SSD1306Ascii::clearField(uint8_t col, uint8_t row, uint8_t n) {
|
||
|
clear(col, col + fieldWidth(n) - 1, row, row + fontRows() - 1);
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
void SSD1306Ascii::displayRemap(bool mode) {
|
||
|
ssd1306WriteCmd(mode ? SSD1306_SEGREMAP : SSD1306_SEGREMAP | 1);
|
||
|
ssd1306WriteCmd(mode ? SSD1306_COMSCANINC : SSD1306_COMSCANDEC);
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
size_t SSD1306Ascii::fieldWidth(uint8_t n) {
|
||
|
return n*(fontWidth() + letterSpacing());
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
uint8_t SSD1306Ascii::fontCharCount() const {
|
||
|
return m_font ? readFontByte(m_font + FONT_CHAR_COUNT) : 0;
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
char SSD1306Ascii::fontFirstChar() const {
|
||
|
return m_font ? readFontByte(m_font + FONT_FIRST_CHAR) : 0;
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
uint8_t SSD1306Ascii::fontHeight() const {
|
||
|
return m_font ? m_magFactor*readFontByte(m_font + FONT_HEIGHT) : 0;
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
uint8_t SSD1306Ascii::fontRows() const {
|
||
|
return m_font ? m_magFactor*((readFontByte(m_font + FONT_HEIGHT) + 7)/8) : 0;
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
uint16_t SSD1306Ascii::fontSize() const {
|
||
|
return (readFontByte(m_font) << 8) | readFontByte(m_font + 1);
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
uint8_t SSD1306Ascii::fontWidth() const {
|
||
|
return m_font ? m_magFactor*readFontByte(m_font + FONT_WIDTH) : 0;
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
void SSD1306Ascii::init(const DevType* dev) {
|
||
|
m_col = 0;
|
||
|
m_row = 0;
|
||
|
#ifdef __AVR__
|
||
|
const uint8_t* table = (const uint8_t*)pgm_read_word(&dev->initcmds);
|
||
|
#else // __AVR__
|
||
|
const uint8_t* table = dev->initcmds;
|
||
|
#endif // __AVR
|
||
|
uint8_t size = readFontByte(&dev->initSize);
|
||
|
m_displayWidth = readFontByte(&dev->lcdWidth);
|
||
|
m_displayHeight = readFontByte(&dev->lcdHeight);
|
||
|
m_colOffset = readFontByte(&dev->colOffset);
|
||
|
for (uint8_t i = 0; i < size; i++) {
|
||
|
ssd1306WriteCmd(readFontByte(table + i));
|
||
|
}
|
||
|
clear();
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
void SSD1306Ascii::invertDisplay(bool invert) {
|
||
|
ssd1306WriteCmd(invert ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY);
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
void SSD1306Ascii::setCol(uint8_t col) {
|
||
|
if (col < m_displayWidth) {
|
||
|
m_col = col;
|
||
|
col += m_colOffset;
|
||
|
ssd1306WriteCmd(SSD1306_SETLOWCOLUMN | (col & 0XF));
|
||
|
ssd1306WriteCmd(SSD1306_SETHIGHCOLUMN | (col >> 4));
|
||
|
}
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
void SSD1306Ascii::setContrast(uint8_t value) {
|
||
|
ssd1306WriteCmd(SSD1306_SETCONTRAST);
|
||
|
ssd1306WriteCmd(value);
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
void SSD1306Ascii::setCursor(uint8_t col, uint8_t row) {
|
||
|
setCol(col);
|
||
|
setRow(row);
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
void SSD1306Ascii::setFont(const uint8_t* font) {
|
||
|
m_font = font;
|
||
|
if (font && fontSize() == 1) {
|
||
|
m_letterSpacing = 0;
|
||
|
} else {
|
||
|
m_letterSpacing = 1;
|
||
|
}
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
void SSD1306Ascii::setRow(uint8_t row) {
|
||
|
if (row < displayRows()) {
|
||
|
m_row = row;
|
||
|
#if INCLUDE_SCROLLING
|
||
|
ssd1306WriteCmd(SSD1306_SETSTARTPAGE | ((m_row + m_pageOffset) & 7));
|
||
|
#else // INCLUDE_SCROLLING
|
||
|
ssd1306WriteCmd(SSD1306_SETSTARTPAGE | m_row);
|
||
|
#endif // INCLUDE_SCROLLING
|
||
|
}
|
||
|
}
|
||
|
#if INCLUDE_SCROLLING
|
||
|
//------------------------------------------------------------------------------
|
||
|
void SSD1306Ascii::setPageOffset(uint8_t page) {
|
||
|
m_pageOffset = page & 7;
|
||
|
setRow(m_row);
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
void SSD1306Ascii::setStartLine(uint8_t line) {
|
||
|
m_startLine = line & 0X3F;
|
||
|
ssd1306WriteCmd(SSD1306_SETSTARTLINE | m_startLine);
|
||
|
}
|
||
|
#endif // INCLUDE_SCROLLING
|
||
|
//------------------------------------------------------------------------------
|
||
|
void SSD1306Ascii::ssd1306WriteRam(uint8_t c) {
|
||
|
if (m_col < m_displayWidth) {
|
||
|
writeDisplay(c^m_invertMask, SSD1306_MODE_RAM);
|
||
|
m_col++;
|
||
|
}
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
void SSD1306Ascii::ssd1306WriteRamBuf(uint8_t c) {
|
||
|
if (m_skip) {
|
||
|
m_skip--;
|
||
|
} else if (m_col < m_displayWidth) {
|
||
|
writeDisplay(c^m_invertMask, SSD1306_MODE_RAM_BUF);
|
||
|
m_col++;
|
||
|
}
|
||
|
}
|
||
|
//------------------------------------------------------------------------------
|
||
|
GLCDFONTDECL(scaledNibble) = {
|
||
|
0X00, 0X03, 0X0C, 0X0F,
|
||
|
0X30, 0X33, 0X3C, 0X3F,
|
||
|
0XC0, 0XC3, 0XCC, 0XCF,
|
||
|
0XF0, 0XF3, 0XFC, 0XFF
|
||
|
};
|
||
|
//------------------------------------------------------------------------------
|
||
|
size_t SSD1306Ascii::strWidth(const char* str) const {
|
||
|
size_t sw = 0;
|
||
|
while (*str) {
|
||
|
uint8_t cw = charWidth(*str++);
|
||
|
if (cw == 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
sw += cw + letterSpacing();
|
||
|
}
|
||
|
return sw;
|
||
|
}
|
||
|
// //------------------------------------------------------------------------------
|
||
|
// void SSD1306Ascii::tickerInit(TickerState* state, const uint8_t* font,
|
||
|
// uint8_t row, bool mag2X, uint8_t bgnCol, uint8_t endCol) {
|
||
|
// state->font = font;
|
||
|
// state->row = row;
|
||
|
// state->mag2X = mag2X;
|
||
|
// state->bgnCol = bgnCol;
|
||
|
// state->endCol = endCol < m_displayWidth ? endCol : m_displayWidth - 1;
|
||
|
// state->nQueue = 0;
|
||
|
// }
|
||
|
// //------------------------------------------------------------------------------
|
||
|
// bool SSD1306Ascii::tickerText(TickerState* state, const char* text) {
|
||
|
// if (!text) {
|
||
|
// state->nQueue = 0;
|
||
|
// return true;
|
||
|
// }
|
||
|
// if (state->nQueue >= TICKER_QUEUE_DIM) {
|
||
|
// return false;
|
||
|
// }
|
||
|
// if (state->nQueue == 0) {
|
||
|
// state->init = true;
|
||
|
// }
|
||
|
// state->queue[state->nQueue++] = text;
|
||
|
// return true;
|
||
|
// }
|
||
|
// //------------------------------------------------------------------------------
|
||
|
// int8_t SSD1306Ascii::tickerTick(TickerState* state) {
|
||
|
// if (!state->font) {
|
||
|
// return -1;
|
||
|
// }
|
||
|
// if (!state->nQueue) {
|
||
|
// return 0;
|
||
|
// }
|
||
|
// setFont(state->font);
|
||
|
// m_magFactor = state->mag2X ? 2 : 1;
|
||
|
// if (state->init) {
|
||
|
// clear(state->bgnCol, state->endCol, state->row, state->row + fontRows() -1);
|
||
|
// state->col = state->endCol;
|
||
|
// state->skip = 0;
|
||
|
// state->init = false;
|
||
|
// }
|
||
|
// // Adjust display width to truncate pixels after endCol. Find better way?
|
||
|
// uint8_t save = m_displayWidth;
|
||
|
// m_displayWidth = state->endCol + 1;
|
||
|
|
||
|
// // Skip pixels before bgnCol.
|
||
|
// skipColumns(state->skip);
|
||
|
// setCursor(state->col, state->row);
|
||
|
// for (uint8_t i = 0; i < state->nQueue; i++) {
|
||
|
// const char* str = state->queue[i];
|
||
|
// while (*str && m_col <= state->endCol) {
|
||
|
// write(*str++);
|
||
|
// }
|
||
|
// if (m_col > state->endCol) {
|
||
|
// break;
|
||
|
// }
|
||
|
// }
|
||
|
// if (m_col <= state->endCol) {
|
||
|
// clear(m_col, m_col, state->row, state->row + fontRows() - 1);
|
||
|
// }
|
||
|
// // Restore display width.
|
||
|
// m_displayWidth = save;
|
||
|
|
||
|
// if (state->nQueue == 1 && *state->queue[0] == 0) {
|
||
|
// state->nQueue = 0;
|
||
|
// return 0;
|
||
|
// }
|
||
|
// if (state->col > state->bgnCol) {
|
||
|
// state->col--;
|
||
|
// } else {
|
||
|
// state->skip++;
|
||
|
// if (state->skip >= charSpacing(*state->queue[0])) {
|
||
|
// state->skip = 0;
|
||
|
// state->queue[0]++;
|
||
|
// if (*state->queue[0] == 0 && state->nQueue > 1) {
|
||
|
// state->nQueue--;
|
||
|
// for (uint8_t i = 0; i < state->nQueue; i++) {
|
||
|
// state->queue[i] = state->queue[i + 1];
|
||
|
// }
|
||
|
// }
|
||
|
// }
|
||
|
// }
|
||
|
// return state->nQueue;
|
||
|
// }
|
||
|
//------------------------------------------------------------------------------
|
||
|
size_t SSD1306Ascii::write(uint8_t ch) {
|
||
|
if (!m_font) {
|
||
|
return 0;
|
||
|
}
|
||
|
uint8_t w = readFontByte(m_font + FONT_WIDTH);
|
||
|
uint8_t h = readFontByte(m_font + FONT_HEIGHT);
|
||
|
uint8_t nr = (h + 7)/8;
|
||
|
uint8_t first = readFontByte(m_font + FONT_FIRST_CHAR);
|
||
|
uint8_t count = readFontByte(m_font + FONT_CHAR_COUNT);
|
||
|
const uint8_t* base = m_font + FONT_WIDTH_TABLE;
|
||
|
|
||
|
if (ch < first || ch >= (first + count)) {
|
||
|
if (ch == '\r') {
|
||
|
setCol(0);
|
||
|
return 1;
|
||
|
}
|
||
|
if (ch == '\n') {
|
||
|
setCol(0);
|
||
|
uint8_t fr = m_magFactor*nr;
|
||
|
#if INCLUDE_SCROLLING
|
||
|
uint8_t dr = displayRows();
|
||
|
uint8_t tmpRow = m_row + fr;
|
||
|
int8_t delta = tmpRow + fr - dr;
|
||
|
if (m_scrollMode == SCROLL_MODE_OFF || delta <= 0) {
|
||
|
setRow(tmpRow);
|
||
|
} else {
|
||
|
m_pageOffset = (m_pageOffset + delta) & 7;
|
||
|
m_row = dr - fr;
|
||
|
// Cursor will be positioned by clearToEOL.
|
||
|
clearToEOL();
|
||
|
if (m_scrollMode == SCROLL_MODE_AUTO) {
|
||
|
setStartLine(8*m_pageOffset);
|
||
|
}
|
||
|
}
|
||
|
#else // INCLUDE_SCROLLING
|
||
|
setRow(m_row + fr);
|
||
|
#endif // INCLUDE_SCROLLING
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
ch -= first;
|
||
|
uint8_t s = letterSpacing();
|
||
|
uint8_t thieleShift = 0;
|
||
|
if (fontSize() < 2) {
|
||
|
// Fixed width font.
|
||
|
base += nr*w*ch;
|
||
|
} else {
|
||
|
if (h & 7) {
|
||
|
thieleShift = 8 - (h & 7);
|
||
|
}
|
||
|
uint16_t index = 0;
|
||
|
for (uint8_t i = 0; i < ch; i++) {
|
||
|
index += readFontByte(base + i);
|
||
|
}
|
||
|
w = readFontByte(base + ch);
|
||
|
base += nr*index + count;
|
||
|
}
|
||
|
uint8_t scol = m_col;
|
||
|
uint8_t srow = m_row;
|
||
|
uint8_t skip = m_skip;
|
||
|
for (uint8_t r = 0; r < nr; r++) {
|
||
|
for (uint8_t m = 0; m < m_magFactor; m++) {
|
||
|
skipColumns(skip);
|
||
|
if (r || m) {
|
||
|
setCursor(scol, m_row + 1);
|
||
|
}
|
||
|
for (uint8_t c = 0; c < w; c++) {
|
||
|
uint8_t b = readFontByte(base + c + r*w);
|
||
|
if (thieleShift && (r + 1) == nr) {
|
||
|
b >>= thieleShift;
|
||
|
}
|
||
|
if (m_magFactor == 2) {
|
||
|
b = m ? b >> 4 : b & 0XF;
|
||
|
b = readFontByte(scaledNibble + b);
|
||
|
ssd1306WriteRamBuf(b);
|
||
|
}
|
||
|
ssd1306WriteRamBuf(b);
|
||
|
}
|
||
|
for (uint8_t i = 0; i < s; i++) {
|
||
|
ssd1306WriteRamBuf(0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
setRow(srow);
|
||
|
return 1;
|
||
|
}
|