1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-22 15:46:14 +01:00

Fix display scrolling on LCD and OLED

Eliminate spurious blanking of screen in mode 1, duplicated lines of text in mode 2, and non-display of more than the first screen-full of lines in mode 0.
This commit is contained in:
Neil McKechnie 2023-03-19 22:06:02 +00:00
parent 2e1a2d38e3
commit 43c7baf8f5
3 changed files with 96 additions and 103 deletions

View File

@ -1,6 +1,5 @@
/* /*
* © 2021, Chris Harlow, Neil McKechnie. All rights reserved. * © 2021, Chris Harlow, Neil McKechnie. All rights reserved.
* © 2023, Harald Barth.
* *
* This file is part of CommandStation-EX * This file is part of CommandStation-EX
* *
@ -37,7 +36,7 @@
* not held up significantly. The exception to this is when * not held up significantly. The exception to this is when
* the loop2() function is called with force=true, where * the loop2() function is called with force=true, where
* a screen update is executed to completion. This is normally * a screen update is executed to completion. This is normally
* only noMoreRowsToDisplay during start-up. * only done during start-up.
* The scroll mode is selected by defining SCROLLMODE as 0, 1 or 2 * The scroll mode is selected by defining SCROLLMODE as 0, 1 or 2
* in the config.h. * in the config.h.
* #define SCROLLMODE 0 is scroll continuous (fill screen if poss), * #define SCROLLMODE 0 is scroll continuous (fill screen if poss),
@ -52,11 +51,10 @@
Display::Display(DisplayDevice *deviceDriver) { Display::Display(DisplayDevice *deviceDriver) {
_deviceDriver = deviceDriver; _deviceDriver = deviceDriver;
// Get device dimensions in characters (e.g. 16x2). // Get device dimensions in characters (e.g. 16x2).
numCharacterColumns = _deviceDriver->getNumCols(); numScreenColumns = _deviceDriver->getNumCols();
numCharacterRows = _deviceDriver->getNumRows(); numScreenRows = _deviceDriver->getNumRows();
for (uint8_t row = 0; row < MAX_CHARACTER_ROWS; row++) for (uint8_t row = 0; row < MAX_CHARACTER_ROWS; row++)
rowBuffer[row][0] = '\0'; rowBuffer[row][0] = '\0';
topRow = ROW_INITIAL; // loop2 will fill from row 0
addDisplay(0); // Add this display as display number 0 addDisplay(0); // Add this display as display number 0
}; };
@ -70,20 +68,19 @@ void Display::_clear() {
_deviceDriver->clearNative(); _deviceDriver->clearNative();
for (uint8_t row = 0; row < MAX_CHARACTER_ROWS; row++) for (uint8_t row = 0; row < MAX_CHARACTER_ROWS; row++)
rowBuffer[row][0] = '\0'; rowBuffer[row][0] = '\0';
topRow = ROW_INITIAL; // loop2 will fill from row 0
} }
void Display::_setRow(uint8_t line) { void Display::_setRow(uint8_t line) {
hotRow = line; hotRow = line;
hotCol = 0; hotCol = 0;
rowBuffer[hotRow][0] = 0; // Clear existing text rowBuffer[hotRow][0] = '\0'; // Clear existing text
} }
size_t Display::_write(uint8_t b) { size_t Display::_write(uint8_t b) {
if (hotRow >= MAX_CHARACTER_ROWS || hotCol >= MAX_CHARACTER_COLS) return -1; if (hotRow >= MAX_CHARACTER_ROWS || hotCol >= MAX_CHARACTER_COLS) return -1;
rowBuffer[hotRow][hotCol] = b; rowBuffer[hotRow][hotCol] = b;
hotCol++; hotCol++;
rowBuffer[hotRow][hotCol] = 0; rowBuffer[hotRow][hotCol] = '\0';
return 1; return 1;
} }
@ -110,8 +107,8 @@ Display *Display::loop2(bool force) {
return NULL; return NULL;
} else { } else {
// force full screen update from the beginning. // force full screen update from the beginning.
rowFirst = ROW_INITIAL; rowFirst = 0;
rowNext = ROW_INITIAL; rowCurrent = 0;
bufferPointer = 0; bufferPointer = 0;
noMoreRowsToDisplay = false; noMoreRowsToDisplay = false;
slot = 0; slot = 0;
@ -119,109 +116,104 @@ Display *Display::loop2(bool force) {
do { do {
if (bufferPointer == 0) { if (bufferPointer == 0) {
// Find a line of data to write to the screen. // Search for non-blank row
if (rowFirst == ROW_INITIAL) rowFirst = rowNext; while (!noMoreRowsToDisplay) {
if (findNextNonBlankRow()) { if (!isCurrentRowBlank()) break;
moveToNextRow();
if (rowCurrent == rowFirst) noMoreRowsToDisplay = true;
}
if (noMoreRowsToDisplay) {
// No non-blank lines left, so draw blank line
buffer[0] = '\0';
} else {
// Non-blank line found, so copy it (including terminator) // Non-blank line found, so copy it (including terminator)
for (uint8_t i = 0; i <= MAX_CHARACTER_COLS; i++) for (uint8_t i = 0; i <= MAX_CHARACTER_COLS; i++)
buffer[i] = rowBuffer[rowNext][i]; buffer[i] = rowBuffer[rowCurrent][i];
} else {
// No non-blank lines left, so draw a blank line
buffer[0] = 0;
} }
#if SCROLLMODE==2 _deviceDriver->setRowNative(slot); // Set position for display
if (buffer[0] == 0 && needScroll){ // surpresses empty line charIndex = 0;
#else bufferPointer = &buffer[0];
if (false){
#endif
charIndex = MAX_CHARACTER_COLS;
slot--;
} else {
_deviceDriver->setRowNative(slot); // Set position for display
charIndex = 0;
bufferPointer = &buffer[0];
}
rowNext++;
} else { } else {
// Write next character, or a space to erase current position. // Write next character, or a space to erase current position.
char ch = *bufferPointer; char ch = *bufferPointer;
if (ch) { if (ch) {
_deviceDriver->writeNative(ch); _deviceDriver->writeNative(ch);
bufferPointer++; bufferPointer++;
} else { } else {
_deviceDriver->writeNative(' '); _deviceDriver->writeNative(' ');
} }
}
if (++charIndex >= MAX_CHARACTER_COLS) { if (++charIndex >= MAX_CHARACTER_COLS) {
// Screen slot completed, move to next slot on screen // Screen slot completed, move to next nonblank row
bufferPointer = 0; bufferPointer = 0;
slot++; for (;;) {
if (slot >= numCharacterRows) { moveToNextRow();
// Last slot on screen written, reset ready for next screen update. if (rowCurrent == rowFirst) {
#if SCROLLMODE==2 || SCROLLMODE==1 noMoreRowsToDisplay = true;
if (!noMoreRowsToDisplay) { break;
needScroll = true; }
} if (!isCurrentRowBlank()) break;
if (needScroll) { }
#if SCROLLMODE==2 // Move to next screen slot, if available
// SCROLLMODE 2 rotates through rowFirst and we slot++;
// (ab)use findNextBlankRow() to figure out if (slot >= numScreenRows) {
// next valid row which can be start row. // Last slot on screen written, so get ready for next screen update.
rowNext = rowFirst + 1; #if SCROLLMODE==0
noMoreRowsToDisplay = false; // Scrollmode 0 scrolls continuously. If the rows fit on the screen,
findNextNonBlankRow(); // then restart at row 0, but otherwise continue with the row
if (rowNext == ROW_INITIAL) // after the last one displayed.
rowNext = 0; if (countNonBlankRows() <= numScreenRows)
rowFirst = ROW_INITIAL; rowCurrent = 0;
rowFirst = rowCurrent;
#elif SCROLLMODE==1
// Scrollmode 1 scrolls by page, so if the last page has just completed then
// next time restart with row 0.
if (noMoreRowsToDisplay)
rowFirst = rowCurrent = 0;
#else #else
// SCROLLMODE 1 just alternates when the // Scrollmode 2 scrolls by row. If the rows don't fit on the screen,
// flag indicates that we have come to the end // then start one row further on next time. If they do fit, then
if (noMoreRowsToDisplay) // show them in order and start next page at row 0.
rowNext = 0; if (countNonBlankRows() <= numScreenRows) {
rowFirst = rowCurrent = 0;
} else {
// Find first non-blank row after the previous first row
rowCurrent = rowFirst;
do {
moveToNextRow();
} while (isCurrentRowBlank());
rowFirst = rowCurrent;
}
#endif #endif
} else { noMoreRowsToDisplay = false;
// SCROLLMODE 1 or 2 but not scroll active slot = 0;
rowNext = 0; lastScrollTime = currentMillis;
} return NULL;
#else }
// this is for SCROLLMODE 0 but what should it do?
rowNext = 0;
#endif
rowFirst = ROW_INITIAL;
noMoreRowsToDisplay = false;
slot = 0;
lastScrollTime = currentMillis;
return NULL;
} }
#if SCROLLMODE==2
if (needScroll)
noMoreRowsToDisplay = false;
#endif
} }
} while (force); } while (force);
return NULL; return NULL;
} }
bool Display::findNextNonBlankRow() { bool Display::isCurrentRowBlank() {
while (!noMoreRowsToDisplay) { return (rowBuffer[rowCurrent][0] == '\0');
if (rowNext == ROW_INITIAL)
rowNext = 0;
if (rowNext >= MAX_CHARACTER_ROWS) {
// Finished if we've looped back to start
rowNext = ROW_INITIAL;
noMoreRowsToDisplay = true;
return false;
}
if (rowBuffer[rowNext][0] != 0) {
//rowBuffer[rowNext][0] = '0' + rowNext; // usefull for debug
//rowBuffer[rowNext][1] = '0' + rowFirst; // usefull for debug
// Found non-blank row
return true;
}
rowNext = rowNext + 1;
}
return false;
} }
void Display::moveToNextRow() {
// Skip blank rows
if (++rowCurrent >= MAX_CHARACTER_ROWS)
rowCurrent = 0;
}
uint8_t Display::countNonBlankRows() {
uint8_t count = 0;
for (uint8_t rowNumber=0; rowNumber<MAX_CHARACTER_ROWS; rowNumber++) {
if (rowBuffer[rowNumber][0] != '\0')
count++;
}
return count;
}

View File

@ -40,7 +40,6 @@ public:
static const int MAX_CHARACTER_ROWS = 8; static const int MAX_CHARACTER_ROWS = 8;
static const int MAX_CHARACTER_COLS = MAX_MSG_SIZE; static const int MAX_CHARACTER_COLS = MAX_MSG_SIZE;
static const long DISPLAY_SCROLL_TIME = 3000; // 3 seconds static const long DISPLAY_SCROLL_TIME = 3000; // 3 seconds
static const uint8_t ROW_INITIAL = 255;
private: private:
DisplayDevice *_deviceDriver; DisplayDevice *_deviceDriver;
@ -48,17 +47,15 @@ private:
unsigned long lastScrollTime = 0; unsigned long lastScrollTime = 0;
uint8_t hotRow = 0; uint8_t hotRow = 0;
uint8_t hotCol = 0; uint8_t hotCol = 0;
uint8_t topRow = 0;
uint8_t slot = 0; uint8_t slot = 0;
uint8_t rowFirst = ROW_INITIAL; uint8_t rowFirst = 0;
uint8_t rowNext = ROW_INITIAL; uint8_t rowCurrent = 0;
uint8_t charIndex = 0; uint8_t charIndex = 0;
char buffer[MAX_CHARACTER_COLS + 1]; char buffer[MAX_CHARACTER_COLS + 1];
char* bufferPointer = 0; char* bufferPointer = 0;
bool noMoreRowsToDisplay = false; bool noMoreRowsToDisplay = false;
bool needScroll = false; uint16_t numScreenRows;
uint16_t numCharacterRows; uint16_t numScreenColumns = MAX_CHARACTER_COLS;
uint16_t numCharacterColumns = MAX_CHARACTER_COLS;
char rowBuffer[MAX_CHARACTER_ROWS][MAX_CHARACTER_COLS+1]; char rowBuffer[MAX_CHARACTER_ROWS][MAX_CHARACTER_COLS+1];
@ -70,7 +67,10 @@ public:
void _refresh() override; void _refresh() override;
void _displayLoop() override; void _displayLoop() override;
Display *loop2(bool force); Display *loop2(bool force);
bool findNextNonBlankRow(); bool findNonBlankRow();
bool isCurrentRowBlank();
void moveToNextRow();
uint8_t countNonBlankRows();
}; };

View File

@ -4,7 +4,8 @@
#include "StringFormatter.h" #include "StringFormatter.h"
#define VERSION "4.2.31" #define VERSION "4.2.32"
// 4.2.32 - Fix LCD/Display bugfixes from 4.2.29
// 4.2.31 - Removes EXRAIL statup from top of file. (BREAKING CHANGE !!) // 4.2.31 - Removes EXRAIL statup from top of file. (BREAKING CHANGE !!)
// Just add AUTOSTART to the top of your myAutomation.h to restore this function. // Just add AUTOSTART to the top of your myAutomation.h to restore this function.
// 4.2.30 - Fixes/enhancements to EX-IOExpander device driver. // 4.2.30 - Fixes/enhancements to EX-IOExpander device driver.