Cute brightness fade to avoid ugly startup :)

custom
jacqueline 2 years ago
parent 610991455d
commit 2a568846bd
  1. 20
      src/drivers/display.cpp
  2. 6
      src/drivers/include/display.hpp
  3. 37
      src/system_fsm/booting.cpp
  4. 17
      src/ui/include/ui_fsm.hpp
  5. 46
      src/ui/ui_fsm.cpp

@ -123,9 +123,11 @@ auto Display::Create(GpioExpander* expander,
.hpoint = 0}; .hpoint = 0};
ESP_ERROR_CHECK(ledc_channel_config(&led_channel)); 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)); ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0));
ledc_fade_func_install(0);
// Next, init the SPI device // Next, init the SPI device
spi_device_interface_config_t spi_cfg = { spi_device_interface_config_t spi_cfg = {
.command_bits = 0, // No command phase .command_bits = 0, // No command phase
@ -180,9 +182,21 @@ auto Display::Create(GpioExpander* expander,
Display::Display(GpioExpander* gpio, spi_device_handle_t handle) Display::Display(GpioExpander* gpio, spi_device_handle_t handle)
: gpio_(gpio), : gpio_(gpio),
handle_(handle), handle_(handle),
worker_task_(tasks::Worker::Start<tasks::Type::kUiFlush>()) {} worker_task_(tasks::Worker::Start<tasks::Type::kUiFlush>()),
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) { void Display::SendInitialisationSequence(const uint8_t* data) {
// Hold onto the bus for the entire sequence so that we're not interrupted // Hold onto the bus for the entire sequence so that we're not interrupted

@ -6,6 +6,7 @@
#pragma once #pragma once
#include <stdint.h>
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
@ -35,6 +36,8 @@ class Display {
Display(GpioExpander* gpio, spi_device_handle_t handle); Display(GpioExpander* gpio, spi_device_handle_t handle);
~Display(); ~Display();
auto SetDisplayOn(bool) -> void;
/* Driver callback invoked by LVGL when there is new data to display. */ /* Driver callback invoked by LVGL when there is new data to display. */
void OnLvglFlush(lv_disp_drv_t* disp_drv, void OnLvglFlush(lv_disp_drv_t* disp_drv,
const lv_area_t* area, const lv_area_t* area,
@ -46,6 +49,9 @@ class Display {
std::unique_ptr<tasks::Worker> worker_task_; std::unique_ptr<tasks::Worker> worker_task_;
bool display_on_;
uint32_t brightness_;
lv_disp_draw_buf_t buffers_; lv_disp_draw_buf_t buffers_;
lv_disp_drv_t driver_; lv_disp_drv_t driver_;
lv_disp_t* display_ = nullptr; lv_disp_t* display_ = nullptr;

@ -31,7 +31,7 @@ console::AppConsole* Booting::sAppConsole;
auto Booting::entry() -> void { auto Booting::entry() -> void {
ESP_LOGI(kTag, "beginning tangara boot"); 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 // I2C and SPI are both always needed. We can't even power down or show an
// error without these. // error without these.
@ -44,39 +44,28 @@ auto Booting::entry() -> void {
assert(sGpioExpander != nullptr); assert(sGpioExpander != nullptr);
// Start bringing up LVGL now, since we have all of its prerequisites. // Start bringing up LVGL now, since we have all of its prerequisites.
ESP_LOGI(kTag, "installing ui drivers"); ESP_LOGI(kTag, "starting ui");
lv_init(); if (!ui::UiState::Init(sGpioExpander.get())) {
sDisplay.reset(drivers::Display::Create(sGpioExpander.get(), events::Dispatch<FatalError, SystemState, ui::UiState, audio::AudioState>(
drivers::displays::kST7735R)); FatalError());
assert(sDisplay != nullptr); return;
sTouch.reset(drivers::TouchWheel::Create());
if (sTouch != nullptr) {
sRelativeTouch.reset(new drivers::RelativeWheel(sTouch.get()));
} }
// The UI FSM now has everything it needs to start setting up. Do this now, // Install everything else that is certain to be needed.
// so that we can properly show the user any errors that appear later. ESP_LOGI(kTag, "installing remaining drivers");
ui::UiState::Init(sGpioExpander.get(), sRelativeTouch, sDisplay);
events::Dispatch<DisplayReady, ui::UiState>(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");
sSamd.reset(drivers::Samd::Create()); sSamd.reset(drivers::Samd::Create());
sBattery.reset(drivers::Battery::Create());
if (!sSamd || !sRelativeTouch) { if (!sSamd || !sBattery) {
events::Dispatch<FatalError, SystemState, ui::UiState, audio::AudioState>( events::Dispatch<FatalError, SystemState, ui::UiState, audio::AudioState>(
FatalError()); FatalError());
return; return;
} }
// These drivers are initialised on boot, but are recoverable (if weird) if // At this point we've done all of the essential boot tasks. Start remaining
// they fail. // state machines and inform them that the system is ready.
ESP_LOGI(kTag, "installing optional drivers");
sBattery.reset(drivers::Battery::Create());
// All drivers are now loaded, so we can finish initing the other state ESP_LOGI(kTag, "starting audio");
// machines.
if (!audio::AudioState::Init(sGpioExpander.get(), sDatabase)) { if (!audio::AudioState::Init(sGpioExpander.get(), sDatabase)) {
events::Dispatch<FatalError, SystemState, ui::UiState, audio::AudioState>( events::Dispatch<FatalError, SystemState, ui::UiState, audio::AudioState>(
FatalError()); FatalError());

@ -21,9 +21,7 @@ namespace ui {
class UiState : public tinyfsm::Fsm<UiState> { class UiState : public tinyfsm::Fsm<UiState> {
public: public:
static auto Init(drivers::GpioExpander* gpio_expander, static auto Init(drivers::GpioExpander* gpio_expander) -> bool;
const std::weak_ptr<drivers::RelativeWheel>& touchwheel,
const std::weak_ptr<drivers::Display>& display) -> void;
virtual ~UiState() {} virtual ~UiState() {}
@ -42,23 +40,18 @@ class UiState : public tinyfsm::Fsm<UiState> {
protected: protected:
static drivers::GpioExpander* sGpioExpander; static drivers::GpioExpander* sGpioExpander;
static std::weak_ptr<drivers::RelativeWheel> sTouchWheel; static std::shared_ptr<drivers::TouchWheel> sTouchWheel;
static std::weak_ptr<drivers::Display> sDisplay; static std::shared_ptr<drivers::RelativeWheel> sRelativeWheel;
static std::shared_ptr<drivers::Display> sDisplay;
static std::shared_ptr<Screen> sCurrentScreen; static std::shared_ptr<Screen> sCurrentScreen;
}; };
namespace states { namespace states {
class PreBoot : public UiState {
public:
void react(const system_fsm::DisplayReady&) override;
using UiState::react;
};
class Splash : public UiState { class Splash : public UiState {
public: public:
void entry() override; void exit() override;
void react(const system_fsm::BootComplete&) override; void react(const system_fsm::BootComplete&) override;
using UiState::react; using UiState::react;
}; };

@ -5,6 +5,8 @@
*/ */
#include "ui_fsm.hpp" #include "ui_fsm.hpp"
#include <memory>
#include "core/lv_obj.h"
#include "display.hpp" #include "display.hpp"
#include "lvgl_task.hpp" #include "lvgl_task.hpp"
#include "relative_wheel.hpp" #include "relative_wheel.hpp"
@ -17,32 +19,46 @@
namespace ui { namespace ui {
drivers::GpioExpander* UiState::sGpioExpander; drivers::GpioExpander* UiState::sGpioExpander;
std::weak_ptr<drivers::RelativeWheel> UiState::sTouchWheel; std::shared_ptr<drivers::TouchWheel> UiState::sTouchWheel;
std::weak_ptr<drivers::Display> UiState::sDisplay; std::shared_ptr<drivers::RelativeWheel> UiState::sRelativeWheel;
std::shared_ptr<drivers::Display> UiState::sDisplay;
std::shared_ptr<Screen> UiState::sCurrentScreen; std::shared_ptr<Screen> UiState::sCurrentScreen;
auto UiState::Init(drivers::GpioExpander* gpio_expander, auto UiState::Init(drivers::GpioExpander* gpio_expander) -> bool {
const std::weak_ptr<drivers::RelativeWheel>& touchwheel,
const std::weak_ptr<drivers::Display>& display) -> void {
assert(!touchwheel.expired());
assert(!display.expired());
sGpioExpander = gpio_expander; 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()); 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 { namespace states {
void PreBoot::react(const system_fsm::DisplayReady& ev) { void Splash::exit() {
transit<Splash>(); if (sDisplay != nullptr) {
sDisplay->SetDisplayOn(true);
}
} }
void Splash::entry() {}
void Splash::react(const system_fsm::BootComplete& ev) { void Splash::react(const system_fsm::BootComplete& ev) {
transit<Interactive>(); transit<Interactive>();
@ -55,4 +71,4 @@ void Interactive::entry() {
} // namespace states } // namespace states
} // namespace ui } // namespace ui
FSM_INITIAL_STATE(ui::UiState, ui::states::PreBoot) FSM_INITIAL_STATE(ui::UiState, ui::states::Splash)

Loading…
Cancel
Save