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:
parent
2e1a2d38e3
commit
43c7baf8f5
180
Display.cpp
180
Display.cpp
|
@ -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;
|
||||||
|
}
|
||||||
|
|
16
Display.h
16
Display.h
|
@ -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();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user