From 2a568846bd8f1c9e23e86e7570557eed6f18cf9f Mon Sep 17 00:00:00 2001 From: jacqueline Date: Wed, 7 Jun 2023 10:30:33 +1000 Subject: [PATCH] Cute brightness fade to avoid ugly startup :) --- src/drivers/display.cpp | 20 +++++++++++--- src/drivers/include/display.hpp | 6 +++++ src/system_fsm/booting.cpp | 37 ++++++++++---------------- src/ui/include/ui_fsm.hpp | 17 ++++-------- src/ui/ui_fsm.cpp | 46 ++++++++++++++++++++++----------- 5 files changed, 72 insertions(+), 54 deletions(-) diff --git a/src/drivers/display.cpp b/src/drivers/display.cpp index 7a49e02b..dd4cecb8 100644 --- a/src/drivers/display.cpp +++ b/src/drivers/display.cpp @@ -123,9 +123,11 @@ auto Display::Create(GpioExpander* expander, .hpoint = 0}; ESP_ERROR_CHECK(ledc_channel_config(&led_channel)); - ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 4096)); + ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0)); ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0)); + ledc_fade_func_install(0); + // Next, init the SPI device spi_device_interface_config_t spi_cfg = { .command_bits = 0, // No command phase @@ -180,9 +182,21 @@ auto Display::Create(GpioExpander* expander, Display::Display(GpioExpander* gpio, spi_device_handle_t handle) : gpio_(gpio), handle_(handle), - worker_task_(tasks::Worker::Start()) {} + worker_task_(tasks::Worker::Start()), + display_on_(false), + brightness_(4096) {} + +Display::~Display() { + ledc_fade_func_uninstall(); +} -Display::~Display() {} +auto Display::SetDisplayOn(bool enabled) -> void { + display_on_ = enabled; + + int new_duty = display_on_ ? brightness_ : 0; + ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, new_duty, 250); + ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT); +} void Display::SendInitialisationSequence(const uint8_t* data) { // Hold onto the bus for the entire sequence so that we're not interrupted diff --git a/src/drivers/include/display.hpp b/src/drivers/include/display.hpp index d2e0c14b..b394dd9e 100644 --- a/src/drivers/include/display.hpp +++ b/src/drivers/include/display.hpp @@ -6,6 +6,7 @@ #pragma once +#include #include #include @@ -35,6 +36,8 @@ class Display { Display(GpioExpander* gpio, spi_device_handle_t handle); ~Display(); + auto SetDisplayOn(bool) -> void; + /* Driver callback invoked by LVGL when there is new data to display. */ void OnLvglFlush(lv_disp_drv_t* disp_drv, const lv_area_t* area, @@ -46,6 +49,9 @@ class Display { std::unique_ptr worker_task_; + bool display_on_; + uint32_t brightness_; + lv_disp_draw_buf_t buffers_; lv_disp_drv_t driver_; lv_disp_t* display_ = nullptr; diff --git a/src/system_fsm/booting.cpp b/src/system_fsm/booting.cpp index e18a8e8d..1ad8c02d 100644 --- a/src/system_fsm/booting.cpp +++ b/src/system_fsm/booting.cpp @@ -31,7 +31,7 @@ console::AppConsole* Booting::sAppConsole; auto Booting::entry() -> void { ESP_LOGI(kTag, "beginning tangara boot"); - ESP_LOGI(kTag, "installing bare minimum drivers"); + ESP_LOGI(kTag, "installing early drivers"); // I2C and SPI are both always needed. We can't even power down or show an // error without these. @@ -44,39 +44,28 @@ auto Booting::entry() -> void { assert(sGpioExpander != nullptr); // Start bringing up LVGL now, since we have all of its prerequisites. - ESP_LOGI(kTag, "installing ui drivers"); - lv_init(); - sDisplay.reset(drivers::Display::Create(sGpioExpander.get(), - drivers::displays::kST7735R)); - assert(sDisplay != nullptr); - sTouch.reset(drivers::TouchWheel::Create()); - if (sTouch != nullptr) { - sRelativeTouch.reset(new drivers::RelativeWheel(sTouch.get())); + ESP_LOGI(kTag, "starting ui"); + if (!ui::UiState::Init(sGpioExpander.get())) { + events::Dispatch( + FatalError()); + return; } - // The UI FSM now has everything it needs to start setting up. Do this now, - // so that we can properly show the user any errors that appear later. - ui::UiState::Init(sGpioExpander.get(), sRelativeTouch, sDisplay); - events::Dispatch(DisplayReady()); - - // These drivers are required for normal operation, but aren't critical for - // booting. We will transition to the error state if these aren't present. - ESP_LOGI(kTag, "installing required drivers"); + // Install everything else that is certain to be needed. + ESP_LOGI(kTag, "installing remaining drivers"); sSamd.reset(drivers::Samd::Create()); + sBattery.reset(drivers::Battery::Create()); - if (!sSamd || !sRelativeTouch) { + if (!sSamd || !sBattery) { events::Dispatch( FatalError()); return; } - // These drivers are initialised on boot, but are recoverable (if weird) if - // they fail. - ESP_LOGI(kTag, "installing optional drivers"); - sBattery.reset(drivers::Battery::Create()); + // At this point we've done all of the essential boot tasks. Start remaining + // state machines and inform them that the system is ready. - // All drivers are now loaded, so we can finish initing the other state - // machines. + ESP_LOGI(kTag, "starting audio"); if (!audio::AudioState::Init(sGpioExpander.get(), sDatabase)) { events::Dispatch( FatalError()); diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index da6263b7..8b80f162 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -21,9 +21,7 @@ namespace ui { class UiState : public tinyfsm::Fsm { public: - static auto Init(drivers::GpioExpander* gpio_expander, - const std::weak_ptr& touchwheel, - const std::weak_ptr& display) -> void; + static auto Init(drivers::GpioExpander* gpio_expander) -> bool; virtual ~UiState() {} @@ -42,23 +40,18 @@ class UiState : public tinyfsm::Fsm { protected: static drivers::GpioExpander* sGpioExpander; - static std::weak_ptr sTouchWheel; - static std::weak_ptr sDisplay; + static std::shared_ptr sTouchWheel; + static std::shared_ptr sRelativeWheel; + static std::shared_ptr sDisplay; static std::shared_ptr sCurrentScreen; }; namespace states { -class PreBoot : public UiState { - public: - void react(const system_fsm::DisplayReady&) override; - using UiState::react; -}; - class Splash : public UiState { public: - void entry() override; + void exit() override; void react(const system_fsm::BootComplete&) override; using UiState::react; }; diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index 5c59cf22..bdc1f89f 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -5,6 +5,8 @@ */ #include "ui_fsm.hpp" +#include +#include "core/lv_obj.h" #include "display.hpp" #include "lvgl_task.hpp" #include "relative_wheel.hpp" @@ -17,33 +19,47 @@ namespace ui { drivers::GpioExpander* UiState::sGpioExpander; -std::weak_ptr UiState::sTouchWheel; -std::weak_ptr UiState::sDisplay; +std::shared_ptr UiState::sTouchWheel; +std::shared_ptr UiState::sRelativeWheel; +std::shared_ptr UiState::sDisplay; std::shared_ptr UiState::sCurrentScreen; -auto UiState::Init(drivers::GpioExpander* gpio_expander, - const std::weak_ptr& touchwheel, - const std::weak_ptr& display) -> void { - assert(!touchwheel.expired()); - assert(!display.expired()); +auto UiState::Init(drivers::GpioExpander* gpio_expander) -> bool { sGpioExpander = gpio_expander; - sTouchWheel = touchwheel; - sDisplay = display; + + lv_init(); + sDisplay.reset( + drivers::Display::Create(gpio_expander, drivers::displays::kST7735R)); + if (sDisplay == nullptr) { + return false; + } + + sTouchWheel.reset(drivers::TouchWheel::Create()); + if (sTouchWheel != nullptr) { + sRelativeWheel.reset(new drivers::RelativeWheel(sTouchWheel.get())); + } sCurrentScreen.reset(new screens::Splash()); - StartLvgl(sTouchWheel, sDisplay); + // Start the UI task even if init ultimately failed, so that we can show some + // kind of error screen to the user. + StartLvgl(sRelativeWheel, sDisplay); + + if (sTouchWheel == nullptr) { + return false; + } + return true; } namespace states { -void PreBoot::react(const system_fsm::DisplayReady& ev) { - transit(); +void Splash::exit() { + if (sDisplay != nullptr) { + sDisplay->SetDisplayOn(true); + } } -void Splash::entry() {} - void Splash::react(const system_fsm::BootComplete& ev) { transit(); } @@ -55,4 +71,4 @@ void Interactive::entry() { } // namespace states } // namespace ui -FSM_INITIAL_STATE(ui::UiState, ui::states::PreBoot) +FSM_INITIAL_STATE(ui::UiState, ui::states::Splash)