Clean up gpios interface
This commit is contained in:
@@ -22,9 +22,8 @@ namespace audio {
|
||||
|
||||
static const char kTag[] = "audio_fsm";
|
||||
|
||||
drivers::GpioExpander* AudioState::sGpioExpander;
|
||||
drivers::IGpios* AudioState::sIGpios;
|
||||
std::shared_ptr<drivers::I2SDac> AudioState::sDac;
|
||||
std::shared_ptr<drivers::DigitalPot> AudioState::sPots;
|
||||
std::weak_ptr<database::Database> AudioState::sDatabase;
|
||||
|
||||
std::unique_ptr<FatfsAudioInput> AudioState::sFileSource;
|
||||
@@ -33,20 +32,19 @@ std::vector<std::unique_ptr<IAudioElement>> AudioState::sPipeline;
|
||||
|
||||
std::deque<AudioState::EnqueuedItem> AudioState::sTrackQueue;
|
||||
|
||||
auto AudioState::Init(drivers::GpioExpander* gpio_expander,
|
||||
auto AudioState::Init(drivers::IGpios* gpio_expander,
|
||||
std::weak_ptr<database::Database> database) -> bool {
|
||||
sGpioExpander = gpio_expander;
|
||||
sIGpios = gpio_expander;
|
||||
|
||||
auto dac = drivers::I2SDac::create(gpio_expander);
|
||||
if (!dac) {
|
||||
return false;
|
||||
}
|
||||
sDac.reset(dac.value());
|
||||
sPots.reset(new drivers::DigitalPot(gpio_expander));
|
||||
sDatabase = database;
|
||||
|
||||
sFileSource.reset(new FatfsAudioInput());
|
||||
sI2SOutput.reset(new I2SAudioOutput(sGpioExpander, sDac, sPots));
|
||||
sI2SOutput.reset(new I2SAudioOutput(sIGpios, sDac));
|
||||
|
||||
// Perform initial pipeline configuration.
|
||||
// TODO(jacqueline): Factor this out once we have any kind of dynamic
|
||||
|
||||
@@ -12,13 +12,12 @@
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
|
||||
#include "digital_pot.hpp"
|
||||
#include "esp_err.h"
|
||||
#include "freertos/portmacro.h"
|
||||
|
||||
#include "audio_element.hpp"
|
||||
#include "freertos/projdefs.h"
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
#include "i2s_dac.hpp"
|
||||
#include "result.hpp"
|
||||
#include "stream_info.hpp"
|
||||
@@ -27,15 +26,13 @@ static const char* kTag = "I2SOUT";
|
||||
|
||||
namespace audio {
|
||||
|
||||
I2SAudioOutput::I2SAudioOutput(drivers::GpioExpander* expander,
|
||||
std::weak_ptr<drivers::I2SDac> dac,
|
||||
std::weak_ptr<drivers::DigitalPot> pots)
|
||||
I2SAudioOutput::I2SAudioOutput(drivers::IGpios* expander,
|
||||
std::weak_ptr<drivers::I2SDac> dac)
|
||||
: expander_(expander),
|
||||
dac_(dac.lock()),
|
||||
pots_(pots.lock()),
|
||||
current_config_(),
|
||||
left_difference_(0),
|
||||
attenuation_(pots_->GetMaxAttenuation()) {
|
||||
attenuation_() {
|
||||
SetVolume(25); // For testing
|
||||
dac_->SetSource(buffer());
|
||||
}
|
||||
@@ -51,70 +48,33 @@ auto I2SAudioOutput::SetInUse(bool in_use) -> void {
|
||||
} else {
|
||||
dac_->Stop();
|
||||
}
|
||||
pots_->SetZeroCrossDetect(in_use);
|
||||
}
|
||||
|
||||
auto I2SAudioOutput::SetVolumeImbalance(int_fast8_t balance) -> void {
|
||||
int_fast8_t new_difference = balance - left_difference_;
|
||||
left_difference_ = balance;
|
||||
if (attenuation_ + new_difference <= pots_->GetMinAttenuation()) {
|
||||
// Volume is currently very high, so shift the left channel down.
|
||||
pots_->SetRelative(drivers::DigitalPot::Channel::kLeft, -new_difference);
|
||||
} else if (attenuation_ - new_difference >= pots_->GetMaxAttenuation()) {
|
||||
// Volume is currently very low, so shift the left channel up.
|
||||
pots_->SetRelative(drivers::DigitalPot::Channel::kLeft, new_difference);
|
||||
} else {
|
||||
ESP_LOGE(kTag, "volume imbalance higher than attenuation range");
|
||||
}
|
||||
// TODO.
|
||||
}
|
||||
|
||||
auto I2SAudioOutput::SetVolume(uint_fast8_t percent) -> void {
|
||||
percent = 100 - percent;
|
||||
int_fast8_t target_attenuation =
|
||||
static_cast<int_fast8_t>(static_cast<float>(GetAdjustedMaxAttenuation()) /
|
||||
100.0f * static_cast<float>(percent));
|
||||
target_attenuation -= pots_->GetMinAttenuation();
|
||||
int_fast8_t difference = target_attenuation - attenuation_;
|
||||
pots_->SetRelative(difference);
|
||||
attenuation_ = target_attenuation;
|
||||
ESP_LOGI(kTag, "adjusting attenuation by %idB to %idB", difference,
|
||||
attenuation_);
|
||||
// TODO.
|
||||
}
|
||||
|
||||
auto I2SAudioOutput::GetVolume() -> uint_fast8_t {
|
||||
// Convert to percentage.
|
||||
uint_fast8_t percent = static_cast<uint_fast8_t>(
|
||||
static_cast<float>(attenuation_) /
|
||||
static_cast<float>(GetAdjustedMaxAttenuation()) * 100.0f);
|
||||
// Invert to get from attenuation to volume.
|
||||
return 100 - percent;
|
||||
// TODO.
|
||||
return 100;
|
||||
}
|
||||
|
||||
auto I2SAudioOutput::GetAdjustedMaxAttenuation() -> int_fast8_t {
|
||||
// Clip to account for imbalance.
|
||||
int_fast8_t adjusted_max =
|
||||
pots_->GetMaxAttenuation() - std::abs(left_difference_);
|
||||
// Shift to be zero minimum.
|
||||
adjusted_max -= pots_->GetMinAttenuation();
|
||||
|
||||
return adjusted_max;
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto I2SAudioOutput::AdjustVolumeUp() -> bool {
|
||||
if (attenuation_ + left_difference_ <= pots_->GetMinAttenuation()) {
|
||||
return false;
|
||||
}
|
||||
attenuation_--;
|
||||
pots_->SetRelative(-1);
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
auto I2SAudioOutput::AdjustVolumeDown() -> bool {
|
||||
if (attenuation_ - left_difference_ >= pots_->GetMaxAttenuation()) {
|
||||
return false;
|
||||
}
|
||||
attenuation_++;
|
||||
pots_->SetRelative(1);
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "database.hpp"
|
||||
#include "display.hpp"
|
||||
#include "fatfs_audio_input.hpp"
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
#include "i2s_audio_output.hpp"
|
||||
#include "i2s_dac.hpp"
|
||||
#include "storage.hpp"
|
||||
@@ -27,7 +27,7 @@ namespace audio {
|
||||
|
||||
class AudioState : public tinyfsm::Fsm<AudioState> {
|
||||
public:
|
||||
static auto Init(drivers::GpioExpander* gpio_expander,
|
||||
static auto Init(drivers::IGpios* gpio_expander,
|
||||
std::weak_ptr<database::Database>) -> bool;
|
||||
|
||||
virtual ~AudioState() {}
|
||||
@@ -54,9 +54,8 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
|
||||
virtual void react(const AudioPipelineIdle&) {}
|
||||
|
||||
protected:
|
||||
static drivers::GpioExpander* sGpioExpander;
|
||||
static drivers::IGpios* sIGpios;
|
||||
static std::shared_ptr<drivers::I2SDac> sDac;
|
||||
static std::shared_ptr<drivers::DigitalPot> sPots;
|
||||
static std::weak_ptr<database::Database> sDatabase;
|
||||
|
||||
static std::unique_ptr<FatfsAudioInput> sFileSource;
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
#include "chunk.hpp"
|
||||
#include "result.hpp"
|
||||
|
||||
#include "digital_pot.hpp"
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
#include "i2s_dac.hpp"
|
||||
#include "stream_info.hpp"
|
||||
|
||||
@@ -24,9 +23,7 @@ namespace audio {
|
||||
|
||||
class I2SAudioOutput : public IAudioSink {
|
||||
public:
|
||||
I2SAudioOutput(drivers::GpioExpander* expander,
|
||||
std::weak_ptr<drivers::I2SDac> dac,
|
||||
std::weak_ptr<drivers::DigitalPot> pots);
|
||||
I2SAudioOutput(drivers::IGpios* expander, std::weak_ptr<drivers::I2SDac> dac);
|
||||
~I2SAudioOutput();
|
||||
|
||||
auto SetInUse(bool) -> void override;
|
||||
@@ -46,9 +43,8 @@ class I2SAudioOutput : public IAudioSink {
|
||||
private:
|
||||
auto GetAdjustedMaxAttenuation() -> int_fast8_t;
|
||||
|
||||
drivers::GpioExpander* expander_;
|
||||
drivers::IGpios* expander_;
|
||||
std::shared_ptr<drivers::I2SDac> dac_;
|
||||
std::shared_ptr<drivers::DigitalPot> pots_;
|
||||
|
||||
std::optional<StreamInfo::Pcm> current_config_;
|
||||
int_fast8_t left_difference_;
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
idf_component_register(
|
||||
SRCS "touchwheel.cpp" "i2s_dac.cpp" "gpio_expander.cpp" "battery.cpp" "storage.cpp" "i2c.cpp"
|
||||
"spi.cpp" "display.cpp" "display_init.cpp" "samd.cpp" "relative_wheel.cpp" "digital_pot.cpp"
|
||||
SRCS "touchwheel.cpp" "i2s_dac.cpp" "gpios.cpp" "battery.cpp" "storage.cpp" "i2c.cpp"
|
||||
"spi.cpp" "display.cpp" "display_init.cpp" "samd.cpp" "relative_wheel.cpp"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES "esp_adc" "fatfs" "result" "lvgl" "span" "tasks")
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
#include "digital_pot.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace drivers {
|
||||
|
||||
using GpioExpander::VOL_LEFT;
|
||||
using GpioExpander::VOL_RIGHT;
|
||||
using GpioExpander::VOL_UP_DOWN;
|
||||
using GpioExpander::VOL_Z_CROSS;
|
||||
|
||||
DigitalPot::DigitalPot(GpioExpander* gpios) : gpios_(gpios) {
|
||||
gpios_->set_pin(VOL_Z_CROSS, true); // Active-low
|
||||
gpios_->set_pin(VOL_UP_DOWN, true);
|
||||
gpios_->set_pin(VOL_LEFT, false);
|
||||
gpios_->set_pin(VOL_RIGHT, false);
|
||||
gpios_->Write();
|
||||
|
||||
// Power-on reset sets attenuation to maximum anyway, but we want to be safe
|
||||
// and not blow anyone's ears out.
|
||||
for (int i = 0; i < 32; i++) {
|
||||
gpios_->set_pin(VOL_LEFT, true);
|
||||
gpios_->set_pin(VOL_RIGHT, true);
|
||||
gpios_->Write();
|
||||
gpios_->set_pin(VOL_LEFT, false);
|
||||
gpios_->set_pin(VOL_RIGHT, false);
|
||||
gpios_->Write();
|
||||
}
|
||||
}
|
||||
|
||||
auto DigitalPot::SetRelative(int_fast8_t change) -> void {
|
||||
if (change == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpios_->set_pin(VOL_UP_DOWN, change > 0);
|
||||
gpios_->Write();
|
||||
|
||||
for (int i = 0; i < std::abs(change); i++) {
|
||||
gpios_->set_pin(VOL_LEFT, true);
|
||||
gpios_->set_pin(VOL_RIGHT, true);
|
||||
gpios_->Write();
|
||||
gpios_->set_pin(VOL_LEFT, false);
|
||||
gpios_->set_pin(VOL_RIGHT, false);
|
||||
gpios_->Write();
|
||||
}
|
||||
}
|
||||
|
||||
auto DigitalPot::SetRelative(Channel ch, int_fast8_t change) -> void {
|
||||
if (change == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
GpioExpander::Pin pin = (ch == Channel::kLeft) ? VOL_LEFT : VOL_RIGHT;
|
||||
gpios_->set_pin(VOL_UP_DOWN, change > 0);
|
||||
gpios_->Write();
|
||||
|
||||
for (int i = 0; i < std::abs(change); i++) {
|
||||
gpios_->set_pin(pin, true);
|
||||
gpios_->Write();
|
||||
gpios_->set_pin(pin, false);
|
||||
gpios_->Write();
|
||||
}
|
||||
}
|
||||
|
||||
auto DigitalPot::SetZeroCrossDetect(bool enabled) -> void {
|
||||
gpios_->set_pin(VOL_Z_CROSS, !enabled); // Active-low
|
||||
gpios_->Write();
|
||||
}
|
||||
|
||||
auto DigitalPot::GetMaxAttenuation() -> int_fast8_t {
|
||||
return 31;
|
||||
}
|
||||
|
||||
auto DigitalPot::GetMinAttenuation() -> int_fast8_t {
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace drivers
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "lvgl/lvgl.h"
|
||||
|
||||
#include "display_init.hpp"
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
#include "soc/soc.h"
|
||||
#include "tasks.hpp"
|
||||
|
||||
@@ -84,7 +84,7 @@ extern "C" void FlushDataCallback(lv_disp_drv_t* disp_drv,
|
||||
instance->OnLvglFlush(disp_drv, area, color_map);
|
||||
}
|
||||
|
||||
auto Display::Create(GpioExpander* expander,
|
||||
auto Display::Create(IGpios* expander,
|
||||
const displays::InitialisationData& init_data)
|
||||
-> Display* {
|
||||
ESP_LOGI(kTag, "Init I/O pins");
|
||||
@@ -181,7 +181,7 @@ auto Display::Create(GpioExpander* expander,
|
||||
return display.release();
|
||||
}
|
||||
|
||||
Display::Display(GpioExpander* gpio, spi_device_handle_t handle)
|
||||
Display::Display(IGpios* gpio, spi_device_handle_t handle)
|
||||
: gpio_(gpio),
|
||||
handle_(handle),
|
||||
worker_task_(tasks::Worker::Start<tasks::Type::kUiFlush>()),
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
#include "gpio_expander.hpp"
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "hal/gpio_types.h"
|
||||
#include "i2c.hpp"
|
||||
|
||||
namespace drivers {
|
||||
|
||||
static const uint8_t kPca8575Address = 0x20;
|
||||
|
||||
// Port A:
|
||||
// 0 - sd card mux switch
|
||||
// 1 - sd card mux enable (active low)
|
||||
// 2 - key up
|
||||
// 3 - key down
|
||||
// 4 - key lock
|
||||
// 5 - display reset (active low)
|
||||
// 6 - NC
|
||||
// 7 - sd card power (active low)
|
||||
// Default to SD card off, inputs high.
|
||||
static const uint8_t kPortADefault = 0b10111110;
|
||||
|
||||
// Port B:
|
||||
// 0 - 3.5mm jack detect (active low)
|
||||
// 1 - headphone amp power enable
|
||||
// 2 - volume zero-cross detection
|
||||
// 3 - volume direction
|
||||
// 4 - volume left channel
|
||||
// 5 - volume right channel
|
||||
// 6 - NC
|
||||
// 7 - NC
|
||||
// Default input high, trs output low
|
||||
static const uint8_t kPortBDefault = 0b00000011;
|
||||
|
||||
/*
|
||||
* Convenience mehod for packing the port a and b bytes into a single 16 bit
|
||||
* value.
|
||||
*/
|
||||
constexpr uint16_t pack(uint8_t a, uint8_t b) {
|
||||
return ((uint16_t)b) << 8 | a;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convenience mehod for unpacking the result of `pack` back into two single
|
||||
* byte port datas.
|
||||
*/
|
||||
constexpr std::pair<uint8_t, uint8_t> unpack(uint16_t ba) {
|
||||
return std::pair((uint8_t)ba, (uint8_t)(ba >> 8));
|
||||
}
|
||||
|
||||
void interrupt_isr(void* arg) {
|
||||
GpioExpander* instance = reinterpret_cast<GpioExpander*>(arg);
|
||||
auto listener = instance->listener().lock();
|
||||
if (listener) {
|
||||
std::invoke(*listener);
|
||||
}
|
||||
}
|
||||
|
||||
auto GpioExpander::Create() -> GpioExpander* {
|
||||
GpioExpander* instance = new GpioExpander();
|
||||
// Read and write initial values on initialisation so that we do not have a
|
||||
// strange partially-initialised state.
|
||||
if (!instance->Write() || !instance->Read()) {
|
||||
return nullptr;
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
GpioExpander::GpioExpander()
|
||||
: ports_(pack(kPortADefault, kPortBDefault)), inputs_(0), listener_() {
|
||||
gpio_config_t config{
|
||||
.pin_bit_mask = static_cast<uint64_t>(1) << GPIO_NUM_34,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_NEGEDGE,
|
||||
};
|
||||
gpio_config(&config);
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED |
|
||||
ESP_INTR_FLAG_IRAM);
|
||||
gpio_isr_handler_add(GPIO_NUM_34, &interrupt_isr, this);
|
||||
}
|
||||
|
||||
GpioExpander::~GpioExpander() {
|
||||
gpio_isr_handler_remove(GPIO_NUM_34);
|
||||
gpio_uninstall_isr_service();
|
||||
}
|
||||
|
||||
bool GpioExpander::Write() {
|
||||
std::pair<uint8_t, uint8_t> ports_ab = unpack(ports());
|
||||
|
||||
I2CTransaction transaction;
|
||||
transaction.start()
|
||||
.write_addr(kPca8575Address, I2C_MASTER_WRITE)
|
||||
.write_ack(ports_ab.first, ports_ab.second)
|
||||
.stop();
|
||||
|
||||
return transaction.Execute() == ESP_OK;
|
||||
}
|
||||
|
||||
bool GpioExpander::Read() {
|
||||
uint8_t input_a, input_b;
|
||||
|
||||
I2CTransaction transaction;
|
||||
transaction.start()
|
||||
.write_addr(kPca8575Address, I2C_MASTER_READ)
|
||||
.read(&input_a, I2C_MASTER_ACK)
|
||||
.read(&input_b, I2C_MASTER_LAST_NACK)
|
||||
.stop();
|
||||
|
||||
esp_err_t ret = transaction.Execute();
|
||||
if (ret != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
inputs_ = pack(input_a, input_b);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GpioExpander::set_pin(Pin pin, bool value) {
|
||||
if (value) {
|
||||
ports_ |= (1 << pin);
|
||||
} else {
|
||||
ports_ &= ~(1 << pin);
|
||||
}
|
||||
}
|
||||
|
||||
bool GpioExpander::get_input(Pin pin) const {
|
||||
return (inputs_ & (1 << pin)) > 0;
|
||||
}
|
||||
|
||||
} // namespace drivers
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "hal/gpio_types.h"
|
||||
#include "hal/i2c_types.h"
|
||||
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
#include "hal/i2s_types.h"
|
||||
#include "i2c.hpp"
|
||||
#include "soc/clk_tree_defs.h"
|
||||
@@ -32,7 +32,7 @@ namespace drivers {
|
||||
static const char* kTag = "i2s_dac";
|
||||
static const i2s_port_t kI2SPort = I2S_NUM_0;
|
||||
|
||||
auto I2SDac::create(GpioExpander* expander) -> std::optional<I2SDac*> {
|
||||
auto I2SDac::create(IGpios* expander) -> std::optional<I2SDac*> {
|
||||
i2s_chan_handle_t i2s_handle;
|
||||
i2s_chan_config_t channel_config =
|
||||
I2S_CHANNEL_DEFAULT_CONFIG(kI2SPort, I2S_ROLE_MASTER);
|
||||
@@ -78,7 +78,7 @@ auto I2SDac::create(GpioExpander* expander) -> std::optional<I2SDac*> {
|
||||
return dac.release();
|
||||
}
|
||||
|
||||
I2SDac::I2SDac(GpioExpander* gpio, i2s_chan_handle_t i2s_handle)
|
||||
I2SDac::I2SDac(IGpios* gpio, i2s_chan_handle_t i2s_handle)
|
||||
: gpio_(gpio),
|
||||
i2s_handle_(i2s_handle),
|
||||
i2s_active_(false),
|
||||
@@ -87,8 +87,7 @@ I2SDac::I2SDac(GpioExpander* gpio, i2s_chan_handle_t i2s_handle)
|
||||
slot_config_(I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
|
||||
I2S_SLOT_MODE_STEREO)) {
|
||||
clock_config_.clk_src = I2S_CLK_SRC_PLL_160M;
|
||||
gpio_->set_pin(GpioExpander::AMP_EN, false);
|
||||
gpio_->Write();
|
||||
gpio_->WriteSync(IGpios::Pin::kAmplifierEnable, false);
|
||||
}
|
||||
|
||||
I2SDac::~I2SDac() {
|
||||
@@ -97,8 +96,7 @@ I2SDac::~I2SDac() {
|
||||
}
|
||||
|
||||
auto I2SDac::Start() -> void {
|
||||
gpio_->set_pin(GpioExpander::AMP_EN, true);
|
||||
gpio_->Write();
|
||||
gpio_->WriteSync(IGpios::Pin::kAmplifierEnable, true);
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
i2s_channel_enable(i2s_handle_);
|
||||
@@ -110,8 +108,7 @@ auto I2SDac::Stop() -> void {
|
||||
i2s_channel_disable(i2s_handle_);
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
|
||||
gpio_->set_pin(GpioExpander::AMP_EN, false);
|
||||
gpio_->Write();
|
||||
gpio_->WriteSync(IGpios::Pin::kAmplifierEnable, false);
|
||||
|
||||
i2s_active_ = false;
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <functional>
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "result.hpp"
|
||||
|
||||
#include "gpio_expander.hpp"
|
||||
|
||||
namespace drivers {
|
||||
|
||||
/*
|
||||
* Driver for a two-channel digital potentiometer, with steps measured in
|
||||
* decibels.
|
||||
*/
|
||||
class DigitalPot {
|
||||
public:
|
||||
explicit DigitalPot(GpioExpander* gpios);
|
||||
~DigitalPot() {}
|
||||
|
||||
// Not copyable or movable.
|
||||
DigitalPot(const DigitalPot&) = delete;
|
||||
DigitalPot& operator=(const DigitalPot&) = delete;
|
||||
|
||||
enum class Channel {
|
||||
kLeft,
|
||||
kRight,
|
||||
};
|
||||
|
||||
auto SetRelative(int_fast8_t change) -> void;
|
||||
auto SetRelative(Channel ch, int_fast8_t change) -> void;
|
||||
|
||||
auto SetZeroCrossDetect(bool enabled) -> void;
|
||||
|
||||
auto GetMaxAttenuation() -> int_fast8_t;
|
||||
auto GetMinAttenuation() -> int_fast8_t;
|
||||
|
||||
private:
|
||||
GpioExpander* gpios_;
|
||||
};
|
||||
|
||||
} // namespace drivers
|
||||
@@ -16,7 +16,7 @@
|
||||
#include "tasks.hpp"
|
||||
|
||||
#include "display_init.hpp"
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
|
||||
namespace drivers {
|
||||
|
||||
@@ -30,10 +30,10 @@ class Display {
|
||||
* over SPI. This never fails, since unfortunately these display don't give
|
||||
* us back any kind of signal to tell us we're actually using them correctly.
|
||||
*/
|
||||
static auto Create(GpioExpander* expander,
|
||||
static auto Create(IGpios* expander,
|
||||
const displays::InitialisationData& init_data) -> Display*;
|
||||
|
||||
Display(GpioExpander* gpio, spi_device_handle_t handle);
|
||||
Display(IGpios* gpio, spi_device_handle_t handle);
|
||||
~Display();
|
||||
|
||||
auto SetDisplayOn(bool) -> void;
|
||||
@@ -48,7 +48,7 @@ class Display {
|
||||
Display& operator=(const Display&) = delete;
|
||||
|
||||
private:
|
||||
GpioExpander* gpio_;
|
||||
IGpios* gpio_;
|
||||
spi_device_handle_t handle_;
|
||||
|
||||
std::unique_ptr<tasks::Worker> worker_task_;
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "driver/i2c.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
||||
namespace drivers {
|
||||
|
||||
/**
|
||||
* Wrapper for interfacing with the PCA8575 GPIO expander. Includes basic
|
||||
* low-level pin setting methods, as well as higher level convenience functions
|
||||
* for reading, writing, and atomically interacting with the SPI chip select
|
||||
* pins.
|
||||
*
|
||||
* Each method of this class can be called safely from any thread, and all
|
||||
* updates are guaranteed to be atomic. Any access to chip select related pins
|
||||
* should be done whilst holding `cs_lock` (preferably via the helper methods).
|
||||
*/
|
||||
class GpioExpander {
|
||||
public:
|
||||
static auto Create() -> GpioExpander*;
|
||||
~GpioExpander();
|
||||
|
||||
/*
|
||||
* Convenience function for running some arbitrary pin writing code, then
|
||||
* flushing a `Write()` to the expander. Example usage:
|
||||
*
|
||||
* ```
|
||||
* gpio_.with([&](auto& gpio) {
|
||||
* gpio.set_pin(AUDIO_POWER_ENABLE, true);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
template <typename F>
|
||||
auto with(F fn) -> void {
|
||||
std::invoke(fn);
|
||||
Write();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ports on the GPIO expander to the values currently represented
|
||||
* in `ports`.
|
||||
*/
|
||||
auto Write(void) -> bool;
|
||||
|
||||
/**
|
||||
* Reads from the GPIO expander, populating `inputs` with the most recent
|
||||
* values.
|
||||
*/
|
||||
auto Read(void) -> bool;
|
||||
|
||||
/* Maps each pin of the expander to its number in a `pack`ed uint16. */
|
||||
enum Pin {
|
||||
// Port A
|
||||
SD_MUX_SWITCH = 0,
|
||||
SD_MUX_EN_ACTIVE_LOW = 1,
|
||||
KEY_UP = 2,
|
||||
KEY_DOWN = 3,
|
||||
KEY_LOCK = 4,
|
||||
DISPLAY_RESET_ACTIVE_LOW = 5,
|
||||
// UNUSED = 6,
|
||||
SD_CARD_POWER_ENABLE_ACTIVE_LOW = 7,
|
||||
|
||||
// Port B
|
||||
PHONE_DETECT = 8,
|
||||
AMP_EN = 9,
|
||||
VOL_Z_CROSS = 10,
|
||||
VOL_UP_DOWN = 11,
|
||||
VOL_LEFT = 12,
|
||||
VOL_RIGHT = 13,
|
||||
// UNUSED = 14,
|
||||
// UNUSED = 15,
|
||||
};
|
||||
|
||||
/* Nicer value names for use with the SD_MUX_SWITCH pin. */
|
||||
enum SdController {
|
||||
SD_MUX_ESP = 0,
|
||||
SD_MUX_SAMD = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the current driven status of each of the ports. The first byte is
|
||||
* port a, and the second byte is port b.
|
||||
*/
|
||||
std::atomic<uint16_t>& ports() { return ports_; }
|
||||
|
||||
/*
|
||||
* Sets a single specific pin to the given value. `true` corresponds to
|
||||
* HIGH, and `false` corresponds to LOW.
|
||||
*
|
||||
* Calls to this method will be buffered in memory until a call to `Write()`
|
||||
* is made.
|
||||
*/
|
||||
void set_pin(Pin pin, bool value);
|
||||
|
||||
/**
|
||||
* Returns the input status of each of the ports. The first byte is port a,
|
||||
* and the second byte is port b.
|
||||
*/
|
||||
const std::atomic<uint16_t>& inputs() const { return inputs_; }
|
||||
|
||||
/* Returns the most recently cached value of the given pin. Only valid for
|
||||
* pins used as inputs; to check what value we're driving a pin, use
|
||||
* `ports()`.
|
||||
*/
|
||||
bool get_input(Pin pin) const;
|
||||
|
||||
auto listener() -> std::weak_ptr<std::function<void(void)>>& {
|
||||
return listener_;
|
||||
}
|
||||
|
||||
auto set_listener(const std::weak_ptr<std::function<void(void)>>& l) -> void {
|
||||
listener_ = l;
|
||||
}
|
||||
|
||||
// Not copyable or movable. There should usually only ever be once instance
|
||||
// of this class, and that instance will likely have a static lifetime.
|
||||
GpioExpander(const GpioExpander&) = delete;
|
||||
GpioExpander& operator=(const GpioExpander&) = delete;
|
||||
|
||||
private:
|
||||
GpioExpander();
|
||||
|
||||
std::atomic<uint16_t> ports_;
|
||||
std::atomic<uint16_t> inputs_;
|
||||
|
||||
std::weak_ptr<std::function<void(void)>> listener_;
|
||||
};
|
||||
|
||||
} // namespace drivers
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "result.hpp"
|
||||
#include "span.hpp"
|
||||
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
#include "sys/_stdint.h"
|
||||
|
||||
namespace drivers {
|
||||
@@ -32,9 +32,9 @@ namespace drivers {
|
||||
*/
|
||||
class I2SDac {
|
||||
public:
|
||||
static auto create(GpioExpander* expander) -> std::optional<I2SDac*>;
|
||||
static auto create(IGpios* expander) -> std::optional<I2SDac*>;
|
||||
|
||||
I2SDac(GpioExpander* gpio, i2s_chan_handle_t i2s_handle);
|
||||
I2SDac(IGpios* gpio, i2s_chan_handle_t i2s_handle);
|
||||
~I2SDac();
|
||||
|
||||
auto Start() -> void;
|
||||
@@ -70,7 +70,7 @@ class I2SDac {
|
||||
I2SDac& operator=(const I2SDac&) = delete;
|
||||
|
||||
private:
|
||||
GpioExpander* gpio_;
|
||||
IGpios* gpio_;
|
||||
i2s_chan_handle_t i2s_handle_;
|
||||
bool i2s_active_;
|
||||
std::optional<uint8_t> active_page_;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "esp_err.h"
|
||||
#include "result.hpp"
|
||||
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
#include "touchwheel.hpp"
|
||||
|
||||
namespace drivers {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "ff.h"
|
||||
#include "result.hpp"
|
||||
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
|
||||
namespace drivers {
|
||||
|
||||
@@ -31,9 +31,9 @@ class SdStorage {
|
||||
FAILED_TO_MOUNT,
|
||||
};
|
||||
|
||||
static auto Create(GpioExpander* gpio) -> cpp::result<SdStorage*, Error>;
|
||||
static auto Create(IGpios* gpio) -> cpp::result<SdStorage*, Error>;
|
||||
|
||||
SdStorage(GpioExpander* gpio,
|
||||
SdStorage(IGpios* gpio,
|
||||
esp_err_t (*do_transaction)(sdspi_dev_handle_t, sdmmc_command_t*),
|
||||
sdspi_dev_handle_t handle_,
|
||||
std::unique_ptr<sdmmc_host_t> host_,
|
||||
@@ -52,7 +52,7 @@ class SdStorage {
|
||||
SdStorage& operator=(const SdStorage&) = delete;
|
||||
|
||||
private:
|
||||
GpioExpander* gpio_;
|
||||
IGpios* gpio_;
|
||||
|
||||
esp_err_t (*do_transaction_)(sdspi_dev_handle_t, sdmmc_command_t*) = nullptr;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "esp_err.h"
|
||||
#include "result.hpp"
|
||||
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
|
||||
namespace drivers {
|
||||
|
||||
|
||||
+8
-10
@@ -23,7 +23,7 @@
|
||||
#include "hal/spi_types.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
|
||||
static const char* kTag = "SDSTORAGE";
|
||||
static const uint8_t kMaxOpenFiles = 8;
|
||||
@@ -55,11 +55,10 @@ static esp_err_t do_transaction(sdspi_dev_handle_t handle,
|
||||
}
|
||||
} // namespace callback
|
||||
|
||||
auto SdStorage::Create(GpioExpander* gpio) -> cpp::result<SdStorage*, Error> {
|
||||
gpio->set_pin(GpioExpander::SD_CARD_POWER_ENABLE_ACTIVE_LOW, 0);
|
||||
gpio->set_pin(GpioExpander::SD_MUX_EN_ACTIVE_LOW, 0);
|
||||
gpio->set_pin(GpioExpander::SD_MUX_SWITCH, GpioExpander::SD_MUX_ESP);
|
||||
gpio->Write();
|
||||
auto SdStorage::Create(IGpios* gpio) -> cpp::result<SdStorage*, Error> {
|
||||
gpio->WriteSync(IGpios::Pin::kSdPowerDisable, 0);
|
||||
gpio->WriteSync(IGpios::Pin::kSdMuxSwitch, IGpios::SD_MUX_ESP);
|
||||
gpio->WriteSync(IGpios::Pin::kSdMuxDisable, 0);
|
||||
|
||||
sdspi_dev_handle_t handle;
|
||||
std::unique_ptr<sdmmc_host_t> host;
|
||||
@@ -114,7 +113,7 @@ auto SdStorage::Create(GpioExpander* gpio) -> cpp::result<SdStorage*, Error> {
|
||||
std::move(card), fs);
|
||||
}
|
||||
|
||||
SdStorage::SdStorage(GpioExpander* gpio,
|
||||
SdStorage::SdStorage(IGpios* gpio,
|
||||
esp_err_t (*do_transaction)(sdspi_dev_handle_t,
|
||||
sdmmc_command_t*),
|
||||
sdspi_dev_handle_t handle,
|
||||
@@ -144,9 +143,8 @@ SdStorage::~SdStorage() {
|
||||
sdspi_host_remove_device(this->handle_);
|
||||
sdspi_host_deinit();
|
||||
|
||||
gpio_->set_pin(GpioExpander::SD_CARD_POWER_ENABLE_ACTIVE_LOW, 1);
|
||||
gpio_->set_pin(GpioExpander::SD_MUX_EN_ACTIVE_LOW, 1);
|
||||
gpio_->Write();
|
||||
gpio_->WriteSync(IGpios::Pin::kSdPowerDisable, 1);
|
||||
gpio_->WriteSync(IGpios::Pin::kSdMuxDisable, 1);
|
||||
}
|
||||
|
||||
auto SdStorage::HandleTransaction(sdspi_dev_handle_t handle,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
#include "i2c.hpp"
|
||||
#include "i2c_fixture.hpp"
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace drivers {
|
||||
|
||||
TEST_CASE("dac configuration", "[integration]") {
|
||||
I2CFixture i2c;
|
||||
GpioExpander expander;
|
||||
IGpios expander;
|
||||
cpp::result<AudioDac*, AudioDac::Error> dac_res = AudioDac::create(&expander);
|
||||
REQUIRE(dac_res.has_value());
|
||||
std::unique_ptr<AudioDac> dac(dac_res.value());
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
@@ -15,17 +15,16 @@ namespace drivers {
|
||||
|
||||
TEST_CASE("gpio expander", "[integration]") {
|
||||
I2CFixture i2c;
|
||||
GpioExpander expander;
|
||||
IGpios expander;
|
||||
SECTION("with() writes when ") {
|
||||
// Initial value.
|
||||
expander.Read();
|
||||
REQUIRE(expander.get_input(GpioExpander::KEY_DOWN) == true);
|
||||
REQUIRE(expander.get_input(IGpios::KEY_DOWN) == true);
|
||||
|
||||
expander.with(
|
||||
[&](auto& gpio) { gpio.set_pin(GpioExpander::KEY_DOWN, false); });
|
||||
expander.with([&](auto& gpio) { gpio.set_pin(IGpios::KEY_DOWN, false); });
|
||||
|
||||
expander.Read();
|
||||
REQUIRE(expander.get_input(GpioExpander::KEY_DOWN) == false);
|
||||
REQUIRE(expander.get_input(IGpios::KEY_DOWN) == false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
#include "i2c.hpp"
|
||||
#include "i2c_fixture.hpp"
|
||||
#include "spi.hpp"
|
||||
@@ -29,7 +29,7 @@ static const std::string kTestFilePath =
|
||||
TEST_CASE("sd card storage", "[integration]") {
|
||||
I2CFixture i2c;
|
||||
SpiFixture spi;
|
||||
GpioExpander expander;
|
||||
IGpios expander;
|
||||
|
||||
{
|
||||
std::unique_ptr<SdStorage> result(SdStorage::create(&expander).value());
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "event_queue.hpp"
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
#include "lvgl/lvgl.h"
|
||||
#include "relative_wheel.hpp"
|
||||
#include "spi.hpp"
|
||||
@@ -42,12 +42,12 @@ auto Booting::entry() -> void {
|
||||
|
||||
// These drivers are the bare minimum to even show an error. If these fail,
|
||||
// then the system is completely hosed.
|
||||
sGpioExpander.reset(drivers::GpioExpander::Create());
|
||||
assert(sGpioExpander != nullptr);
|
||||
sGpios.reset(drivers::Gpios::Create());
|
||||
assert(sGpios != nullptr);
|
||||
|
||||
// Start bringing up LVGL now, since we have all of its prerequisites.
|
||||
ESP_LOGI(kTag, "starting ui");
|
||||
if (!ui::UiState::Init(sGpioExpander.get())) {
|
||||
if (!ui::UiState::Init(sGpios.get())) {
|
||||
events::Dispatch<FatalError, SystemState, ui::UiState, audio::AudioState>(
|
||||
FatalError());
|
||||
return;
|
||||
@@ -68,7 +68,7 @@ auto Booting::entry() -> void {
|
||||
// state machines and inform them that the system is ready.
|
||||
|
||||
ESP_LOGI(kTag, "starting audio");
|
||||
if (!audio::AudioState::Init(sGpioExpander.get(), sDatabase)) {
|
||||
if (!audio::AudioState::Init(sGpios.get(), sDatabase)) {
|
||||
events::Dispatch<FatalError, SystemState, ui::UiState, audio::AudioState>(
|
||||
FatalError());
|
||||
return;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "battery.hpp"
|
||||
#include "database.hpp"
|
||||
#include "display.hpp"
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
#include "relative_wheel.hpp"
|
||||
#include "samd.hpp"
|
||||
#include "storage.hpp"
|
||||
@@ -48,7 +48,7 @@ class SystemState : public tinyfsm::Fsm<SystemState> {
|
||||
virtual void react(const StorageError&) {}
|
||||
|
||||
protected:
|
||||
static std::shared_ptr<drivers::GpioExpander> sGpioExpander;
|
||||
static std::shared_ptr<drivers::Gpios> sGpios;
|
||||
static std::shared_ptr<drivers::Samd> sSamd;
|
||||
|
||||
static std::shared_ptr<drivers::TouchWheel> sTouch;
|
||||
|
||||
@@ -27,7 +27,7 @@ static const char kTag[] = "RUN";
|
||||
void Running::entry() {
|
||||
ESP_LOGI(kTag, "mounting sd card");
|
||||
vTaskDelay(pdMS_TO_TICKS(250));
|
||||
auto storage_res = drivers::SdStorage::Create(sGpioExpander.get());
|
||||
auto storage_res = drivers::SdStorage::Create(sGpios.get());
|
||||
if (storage_res.has_error()) {
|
||||
ESP_LOGW(kTag, "failed to mount!");
|
||||
events::Dispatch<StorageError, SystemState, audio::AudioState, ui::UiState>(
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
namespace system_fsm {
|
||||
|
||||
std::shared_ptr<drivers::GpioExpander> SystemState::sGpioExpander;
|
||||
std::shared_ptr<drivers::Gpios> SystemState::sGpios;
|
||||
std::shared_ptr<drivers::Samd> SystemState::sSamd;
|
||||
|
||||
std::shared_ptr<drivers::TouchWheel> SystemState::sTouch;
|
||||
@@ -32,21 +32,17 @@ void SystemState::react(const FatalError& err) {
|
||||
|
||||
void SystemState::react(const internal::GpioInterrupt& ev) {
|
||||
ESP_LOGI("sys", "gpios changed");
|
||||
bool prev_key_up = sGpioExpander->get_input(drivers::GpioExpander::KEY_UP);
|
||||
bool prev_key_down =
|
||||
sGpioExpander->get_input(drivers::GpioExpander::KEY_DOWN);
|
||||
bool prev_key_lock =
|
||||
sGpioExpander->get_input(drivers::GpioExpander::KEY_LOCK);
|
||||
bool prev_has_headphones =
|
||||
sGpioExpander->get_input(drivers::GpioExpander::PHONE_DETECT);
|
||||
bool prev_key_up = sGpios->Get(drivers::Gpios::Pin::kKeyUp);
|
||||
bool prev_key_down = sGpios->Get(drivers::Gpios::Pin::kKeyDown);
|
||||
bool prev_key_lock = sGpios->Get(drivers::Gpios::Pin::kKeyLock);
|
||||
bool prev_has_headphones = sGpios->Get(drivers::Gpios::Pin::kPhoneDetect);
|
||||
|
||||
sGpioExpander->Read();
|
||||
sGpios->Read();
|
||||
|
||||
bool key_up = sGpioExpander->get_input(drivers::GpioExpander::KEY_UP);
|
||||
bool key_down = sGpioExpander->get_input(drivers::GpioExpander::KEY_DOWN);
|
||||
bool key_lock = sGpioExpander->get_input(drivers::GpioExpander::KEY_LOCK);
|
||||
bool has_headphones =
|
||||
sGpioExpander->get_input(drivers::GpioExpander::PHONE_DETECT);
|
||||
bool key_up = sGpios->Get(drivers::Gpios::Pin::kKeyUp);
|
||||
bool key_down = sGpios->Get(drivers::Gpios::Pin::kKeyDown);
|
||||
bool key_lock = sGpios->Get(drivers::Gpios::Pin::kKeyLock);
|
||||
bool has_headphones = sGpios->Get(drivers::Gpios::Pin::kPhoneDetect);
|
||||
|
||||
if (key_up != prev_key_up) {
|
||||
events::Dispatch<KeyUpChanged, audio::AudioState, ui::UiState>(
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace ui {
|
||||
|
||||
class UiState : public tinyfsm::Fsm<UiState> {
|
||||
public:
|
||||
static auto Init(drivers::GpioExpander* gpio_expander) -> bool;
|
||||
static auto Init(drivers::IGpios* gpio_expander) -> bool;
|
||||
|
||||
virtual ~UiState() {}
|
||||
|
||||
@@ -41,7 +41,7 @@ class UiState : public tinyfsm::Fsm<UiState> {
|
||||
virtual void react(const system_fsm::BootComplete&) {}
|
||||
|
||||
protected:
|
||||
static drivers::GpioExpander* sGpioExpander;
|
||||
static drivers::IGpios* sIGpios;
|
||||
static std::shared_ptr<drivers::TouchWheel> sTouchWheel;
|
||||
static std::shared_ptr<drivers::RelativeWheel> sRelativeWheel;
|
||||
static std::shared_ptr<drivers::Display> sDisplay;
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
#include "widgets/lv_label.h"
|
||||
|
||||
#include "display.hpp"
|
||||
#include "gpio_expander.hpp"
|
||||
#include "gpios.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
|
||||
+3
-3
@@ -18,15 +18,15 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
drivers::GpioExpander* UiState::sGpioExpander;
|
||||
drivers::IGpios* UiState::sIGpios;
|
||||
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) -> bool {
|
||||
sGpioExpander = gpio_expander;
|
||||
auto UiState::Init(drivers::IGpios* gpio_expander) -> bool {
|
||||
sIGpios = gpio_expander;
|
||||
|
||||
lv_init();
|
||||
sDisplay.reset(
|
||||
|
||||
Reference in New Issue
Block a user