Cute brightness fade to avoid ugly startup :)
This commit is contained in:
+17
-3
@@ -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<tasks::Type::kUiFlush>()) {}
|
||||
worker_task_(tasks::Worker::Start<tasks::Type::kUiFlush>()),
|
||||
display_on_(false),
|
||||
brightness_(4096) {}
|
||||
|
||||
Display::~Display() {}
|
||||
Display::~Display() {
|
||||
ledc_fade_func_uninstall();
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
@@ -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<tasks::Worker> worker_task_;
|
||||
|
||||
bool display_on_;
|
||||
uint32_t brightness_;
|
||||
|
||||
lv_disp_draw_buf_t buffers_;
|
||||
lv_disp_drv_t driver_;
|
||||
lv_disp_t* display_ = nullptr;
|
||||
|
||||
+16
-27
@@ -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()));
|
||||
}
|
||||
|
||||
// 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, 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());
|
||||
|
||||
if (!sSamd || !sRelativeTouch) {
|
||||
ESP_LOGI(kTag, "starting ui");
|
||||
if (!ui::UiState::Init(sGpioExpander.get())) {
|
||||
events::Dispatch<FatalError, SystemState, ui::UiState, audio::AudioState>(
|
||||
FatalError());
|
||||
return;
|
||||
}
|
||||
|
||||
// These drivers are initialised on boot, but are recoverable (if weird) if
|
||||
// they fail.
|
||||
ESP_LOGI(kTag, "installing optional 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());
|
||||
|
||||
// All drivers are now loaded, so we can finish initing the other state
|
||||
// machines.
|
||||
if (!sSamd || !sBattery) {
|
||||
events::Dispatch<FatalError, SystemState, ui::UiState, audio::AudioState>(
|
||||
FatalError());
|
||||
return;
|
||||
}
|
||||
|
||||
// At this point we've done all of the essential boot tasks. Start remaining
|
||||
// state machines and inform them that the system is ready.
|
||||
|
||||
ESP_LOGI(kTag, "starting audio");
|
||||
if (!audio::AudioState::Init(sGpioExpander.get(), sDatabase)) {
|
||||
events::Dispatch<FatalError, SystemState, ui::UiState, audio::AudioState>(
|
||||
FatalError());
|
||||
|
||||
@@ -21,9 +21,7 @@ namespace ui {
|
||||
|
||||
class UiState : public tinyfsm::Fsm<UiState> {
|
||||
public:
|
||||
static auto Init(drivers::GpioExpander* gpio_expander,
|
||||
const std::weak_ptr<drivers::RelativeWheel>& touchwheel,
|
||||
const std::weak_ptr<drivers::Display>& display) -> void;
|
||||
static auto Init(drivers::GpioExpander* gpio_expander) -> bool;
|
||||
|
||||
virtual ~UiState() {}
|
||||
|
||||
@@ -42,23 +40,18 @@ class UiState : public tinyfsm::Fsm<UiState> {
|
||||
|
||||
protected:
|
||||
static drivers::GpioExpander* sGpioExpander;
|
||||
static std::weak_ptr<drivers::RelativeWheel> sTouchWheel;
|
||||
static std::weak_ptr<drivers::Display> sDisplay;
|
||||
static std::shared_ptr<drivers::TouchWheel> sTouchWheel;
|
||||
static std::shared_ptr<drivers::RelativeWheel> sRelativeWheel;
|
||||
static std::shared_ptr<drivers::Display> sDisplay;
|
||||
|
||||
static std::shared_ptr<Screen> 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;
|
||||
};
|
||||
|
||||
+31
-15
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
|
||||
#include "ui_fsm.hpp"
|
||||
#include <memory>
|
||||
#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<drivers::RelativeWheel> UiState::sTouchWheel;
|
||||
std::weak_ptr<drivers::Display> UiState::sDisplay;
|
||||
std::shared_ptr<drivers::TouchWheel> UiState::sTouchWheel;
|
||||
std::shared_ptr<drivers::RelativeWheel> UiState::sRelativeWheel;
|
||||
std::shared_ptr<drivers::Display> UiState::sDisplay;
|
||||
|
||||
std::shared_ptr<Screen> UiState::sCurrentScreen;
|
||||
|
||||
auto UiState::Init(drivers::GpioExpander* gpio_expander,
|
||||
const std::weak_ptr<drivers::RelativeWheel>& touchwheel,
|
||||
const std::weak_ptr<drivers::Display>& 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<Splash>();
|
||||
void Splash::exit() {
|
||||
if (sDisplay != nullptr) {
|
||||
sDisplay->SetDisplayOn(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Splash::entry() {}
|
||||
|
||||
void Splash::react(const system_fsm::BootComplete& ev) {
|
||||
transit<Interactive>();
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user