From db9e5cce1fff82149a609939709a94ae02f349a8 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Mon, 22 Apr 2024 16:00:53 +1000 Subject: [PATCH] Improve handling of the display - Blank the display when locking to prevent burn-in - Delay turning the display on until *exactly* after the first lvgl flush - Init the display in the ui task to avoid blocking the rest of boot --- src/drivers/display.cpp | 28 ++++++++++++++++++++++++++-- src/drivers/include/display.hpp | 1 + src/ui/include/ui_events.hpp | 7 +++++++ src/ui/include/ui_fsm.hpp | 1 + src/ui/lvgl_task.cpp | 1 - src/ui/ui_fsm.cpp | 23 ++++++++++++++--------- 6 files changed, 49 insertions(+), 12 deletions(-) diff --git a/src/drivers/display.cpp b/src/drivers/display.cpp index c16fc148..5c686811 100644 --- a/src/drivers/display.cpp +++ b/src/drivers/display.cpp @@ -5,6 +5,7 @@ */ #include "display.hpp" +#include #include #include @@ -168,7 +169,11 @@ auto Display::Create(IGpios& expander, } Display::Display(IGpios& gpio, spi_device_handle_t handle) - : gpio_(gpio), handle_(handle), display_on_(false), brightness_(0) {} + : gpio_(gpio), + handle_(handle), + first_flush_finished_(false), + display_on_(false), + brightness_(0) {} Display::~Display() { ledc_fade_func_uninstall(); @@ -176,14 +181,28 @@ Display::~Display() { auto Display::SetDisplayOn(bool enabled) -> void { display_on_ = enabled; + if (!first_flush_finished_) { + return; + } + + if (display_on_) { + SendCommandWithData(displays::ST77XX_DISPON, nullptr, 0); + vTaskDelay(pdMS_TO_TICKS(100)); + } + int new_duty = display_on_ ? brightness_ : 0; SetDutyCycle(new_duty, true); + + if (!display_on_) { + vTaskDelay(pdMS_TO_TICKS(100)); + SendCommandWithData(displays::ST77XX_DISPOFF, nullptr, 0); + } } auto Display::SetBrightness(uint_fast8_t percent) -> void { brightness_ = std::pow(static_cast(percent) / 100.0, 2.8) * 1024.0 + 0.5; - if (display_on_) { + if (first_flush_finished_ && display_on_) { SetDutyCycle(brightness_, false); } } @@ -295,6 +314,11 @@ void Display::OnLvglFlush(lv_disp_drv_t* disp_drv, SendCommandWithData(displays::ST77XX_RAMWR, reinterpret_cast(color_map), size * 2); + if (!first_flush_finished_ && lv_disp_flush_is_last(disp_drv)) { + first_flush_finished_ = true; + SetDisplayOn(display_on_); + } + lv_disp_flush_ready(&driver_); } diff --git a/src/drivers/include/display.hpp b/src/drivers/include/display.hpp index b0aa5d58..d2e18a5c 100644 --- a/src/drivers/include/display.hpp +++ b/src/drivers/include/display.hpp @@ -52,6 +52,7 @@ class Display { IGpios& gpio_; spi_device_handle_t handle_; + bool first_flush_finished_; bool display_on_; uint_fast8_t brightness_; diff --git a/src/ui/include/ui_events.hpp b/src/ui/include/ui_events.hpp index 81e0543a..3d794edc 100644 --- a/src/ui/include/ui_events.hpp +++ b/src/ui/include/ui_events.hpp @@ -8,7 +8,9 @@ #include #include "database.hpp" +#include "gpios.hpp" #include "index.hpp" +#include "nvs.hpp" #include "screen.hpp" #include "tinyfsm.hpp" @@ -32,6 +34,11 @@ struct DumpLuaStack : tinyfsm::Event {}; namespace internal { +struct InitDisplay : tinyfsm::Event { + drivers::IGpios& gpios; + drivers::NvsStorage& nvs; +}; + struct ReindexDatabase : tinyfsm::Event {}; struct BackPressed : tinyfsm::Event {}; diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index 8eafc6e0..325aea8f 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -70,6 +70,7 @@ class UiState : public tinyfsm::Fsm { void react(const system_fsm::KeyLockChanged&); void react(const system_fsm::SamdUsbStatusChanged&); + void react(const internal::InitDisplay&); void react(const internal::DismissAlerts&); void react(const database::event::UpdateStarted&); diff --git a/src/ui/lvgl_task.cpp b/src/ui/lvgl_task.cpp index 4cf25c15..51da0179 100644 --- a/src/ui/lvgl_task.cpp +++ b/src/ui/lvgl_task.cpp @@ -84,7 +84,6 @@ auto UiTask::Main() -> void { } auto UiTask::input(std::shared_ptr input) -> void { - assert(current_screen_); input_ = input; } diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index 1305e764..ceeb194d 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -267,6 +267,15 @@ lua::Property UiState::sUsbMassStorageBusy{false}; auto UiState::InitBootSplash(drivers::IGpios& gpios, drivers::NvsStorage& nvs) -> bool { + events::Ui().Dispatch(internal::InitDisplay{ + .gpios = gpios, + .nvs = nvs, + }); + sTask.reset(UiTask::Start()); + return true; +} + +void UiState::react(const internal::InitDisplay& ev) { // Init LVGL first, since the display driver registers itself with LVGL. lv_init(); @@ -275,19 +284,15 @@ auto UiState::InitBootSplash(drivers::IGpios& gpios, drivers::NvsStorage& nvs) // HACK: correct the display size for our prototypes. // nvs.DisplaySize({161, 130}); - auto actual_size = nvs.DisplaySize(); + auto actual_size = ev.nvs.DisplaySize(); init_data.width = actual_size.first.value_or(init_data.width); init_data.height = actual_size.second.value_or(init_data.height); - - sDisplay.reset(drivers::Display::Create(gpios, init_data)); - if (sDisplay == nullptr) { - return false; - } + sDisplay.reset(drivers::Display::Create(ev.gpios, init_data)); sCurrentScreen.reset(new screens::Splash()); - sTask.reset(UiTask::Start()); - sDisplay->SetDisplayOn(!gpios.IsLocked()); - return true; + + // Display will only actually come on after LVGL finishes its first flush. + sDisplay->SetDisplayOn(!ev.gpios.IsLocked()); } void UiState::PushScreen(std::shared_ptr screen) {