aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAlvin Wong <[email protected]>2021-05-02 21:36:06 +0800
committerAlvin Wong <[email protected]>2021-05-02 21:57:45 +0800
commit82c985d78502cb78766eb941ddf6674d5bd167c0 (patch)
tree75b52eef82b87f9df7ad0b8c50f76a24d22002c2
parent969cadc3eb483e28cd3bd41ee5e53b0f4fcf0b13 (diff)
downloadIronOS-82c985d78502cb78766eb941ddf6674d5bd167c0.tar.gz
IronOS-82c985d78502cb78766eb941ddf6674d5bd167c0.zip
Impl. menu item scroll down animation
-rw-r--r--source/Core/Drivers/OLED.cpp72
-rw-r--r--source/Core/Drivers/OLED.hpp1
-rw-r--r--source/Core/Src/gui.cpp28
3 files changed, 94 insertions, 7 deletions
diff --git a/source/Core/Drivers/OLED.cpp b/source/Core/Drivers/OLED.cpp
index 0555f838..65dce02b 100644
--- a/source/Core/Drivers/OLED.cpp
+++ b/source/Core/Drivers/OLED.cpp
@@ -59,7 +59,38 @@ FRToSI2C::I2C_REG OLED_Setup_Array[] = {
};
// Setup based on the SSD1307 and modified for the SSD1306
-const uint8_t REFRESH_COMMANDS[17] = {0x80, 0xAF, 0x80, 0x21, 0x80, 0x20, 0x80, 0x7F, 0x80, 0xC0, 0x80, 0x22, 0x80, 0x00, 0x80, 0x01, 0x40};
+const uint8_t REFRESH_COMMANDS[17] = {
+ // Set display ON:
+ 0x80,
+ 0xAF, // cmd
+
+ // Set column address:
+ // A[6:0] - Column start address = 0x20
+ // B[6:0] - Column end address = 0x7F
+ 0x80,
+ 0x21, // cmd
+ 0x80,
+ 0x20, // A
+ 0x80,
+ 0x7F, // B
+
+ // Set COM output scan direction (normal mode, COM0 to COM[N-1])
+ 0x80,
+ 0xC0,
+
+ // Set page address:
+ // A[2:0] - Page start address = 0
+ // B[2:0] - Page end address = 1
+ 0x80,
+ 0x22, // cmd
+ 0x80,
+ 0x00, // A
+ 0x80,
+ 0x01, // B
+
+ // Start of data
+ 0x40,
+};
/*
* Animation timing function that follows a bezier curve.
@@ -235,6 +266,45 @@ void OLED::useSecondaryFramebuffer(bool useSecondary) {
setFramebuffer(NULL);
}
}
+/**
+ * Plays a transition animation of scrolling downward. Note this does *not*
+ * use the secondary framebuffer.
+ *
+ * This transition relies on the previous screen data already in the OLED
+ * RAM. The caller shall not call `OLED::refresh()` before calling this
+ * method, as doing so will overwrite the previous screen data. The caller
+ * does not need to call `OLED::refresh()` after this function returns.
+ *
+ * **This function blocks until the transition has completed.**
+ */
+void OLED::transitionScrollDown() {
+ // We want to draw the updated framebuffer to the next page downward.
+ uint8_t const pageStart = screenBuffer[13];
+ uint8_t const nextPage = (pageStart + 2) % 8;
+ // Change page start address:
+ screenBuffer[13] = nextPage;
+ // Change page end address:
+ screenBuffer[15] = nextPage + 1;
+
+ refresh();
+ osDelay(TICKS_100MS / 5);
+
+ uint8_t const startLine = pageStart * 8 + 1;
+ uint8_t const scrollTo = (pageStart + 2) * 8;
+
+ // Scroll the screen by changing display start line.
+ for (uint8_t current = startLine; current <= scrollTo; current++) {
+ // Set display start line (0x40~0x7F):
+ // X[5:0] - display start line value
+ uint8_t scrollCommandByte = 0b01000000 | (current & 0b00111111);
+
+ // Also update setup command for "set display start line":
+ OLED_Setup_Array[8].val = scrollCommandByte;
+
+ FRToSI2C::I2C_RegisterWrite(DEVICEADDR_OLED, 0x80, scrollCommandByte);
+ osDelay(TICKS_100MS / 5);
+ }
+}
void OLED::setRotation(bool leftHanded) {
#ifdef OLED_FLIP
diff --git a/source/Core/Drivers/OLED.hpp b/source/Core/Drivers/OLED.hpp
index 9332c4ee..7d88ac96 100644
--- a/source/Core/Drivers/OLED.hpp
+++ b/source/Core/Drivers/OLED.hpp
@@ -81,6 +81,7 @@ public:
static void drawScrollIndicator(uint8_t p, uint8_t h); // Draws a scrolling position indicator
static void transitionSecondaryFramebuffer(bool forwardNavigation);
static void useSecondaryFramebuffer(bool useSecondary);
+ static void transitionScrollDown();
private:
static void drawChar(uint16_t charCode, FontStyle fontStyle); // Draw a character to the current cursor location
diff --git a/source/Core/Src/gui.cpp b/source/Core/Src/gui.cpp
index 2ee0bc8b..15a6a82e 100644
--- a/source/Core/Src/gui.cpp
+++ b/source/Core/Src/gui.cpp
@@ -1043,6 +1043,7 @@ void gui_Menu(const menuitem *menu) {
uint8_t scrollContentSize = 0;
bool scrollBlink = false;
bool lastValue = false;
+ bool scrollingDown = false;
ScrollMessage scrollMessage;
@@ -1068,6 +1069,10 @@ void gui_Menu(const menuitem *menu) {
while ((menu[currentScreen].draw != nullptr) && earlyExit == false) {
OLED::setCursor(0, 0);
+ if (scrollingDown) {
+ animOpenState = true;
+ }
+
// If the user has hesitated for >=3 seconds, show the long text
// Otherwise "draw" the option
if ((xTaskGetTickCount() - lastButtonTime < (TICKS_SECOND * 3)) || menu[currentScreen].description == 0) {
@@ -1089,6 +1094,18 @@ void gui_Menu(const menuitem *menu) {
lcdRefresh |= scrollMessage.drawUpdate(description, xTaskGetTickCount());
}
+ if (lcdRefresh) {
+ if (scrollingDown) {
+ OLED::transitionScrollDown();
+ scrollingDown = false;
+ animOpenState = false;
+ } else {
+ OLED::refresh(); // update the LCD
+ osDelay(40);
+ }
+ lcdRefresh = false;
+ }
+
ButtonState buttons = getButtonState();
if (buttons != lastButtonState) {
@@ -1115,7 +1132,8 @@ void gui_Menu(const menuitem *menu) {
case BUTTON_B_SHORT:
if (scrollMessage.isReset()) {
currentScreen++;
- lastValue = false;
+ scrollingDown = true;
+ lastValue = false;
} else
scrollMessage.reset();
break;
@@ -1136,6 +1154,7 @@ void gui_Menu(const menuitem *menu) {
case BUTTON_B_LONG:
if (xTaskGetTickCount() - autoRepeatTimer + autoRepeatAcceleration > PRESS_ACCEL_INTERVAL_MAX) {
currentScreen++;
+ scrollingDown = true;
autoRepeatTimer = xTaskGetTickCount();
scrollMessage.reset();
@@ -1151,11 +1170,6 @@ void gui_Menu(const menuitem *menu) {
autoRepeatAcceleration = PRESS_ACCEL_INTERVAL_MAX - PRESS_ACCEL_INTERVAL_MIN;
}
- if (lcdRefresh) {
- OLED::refresh(); // update the LCD
- osDelay(40);
- lcdRefresh = false;
- }
if ((xTaskGetTickCount() - lastButtonTime) > (TICKS_SECOND * 30)) {
// If user has not pressed any buttons in 30 seconds, exit back a menu layer
// This will trickle the user back to the main screen eventually
@@ -1163,6 +1177,8 @@ void gui_Menu(const menuitem *menu) {
scrollMessage.reset();
}
}
+
+ animOpenState = false;
}
void enterSettingsMenu() {