From 371f0a20cad4dfcb3237db6f72a7e35403950938 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Fri, 30 Jun 2023 20:48:40 +1000 Subject: [PATCH] Clean up gpios interface --- src/audio/audio_fsm.cpp | 10 +- src/audio/i2s_audio_output.cpp | 64 ++-------- src/audio/include/audio_fsm.hpp | 7 +- src/audio/include/i2s_audio_output.hpp | 10 +- src/drivers/CMakeLists.txt | 4 +- src/drivers/digital_pot.cpp | 85 -------------- src/drivers/display.cpp | 6 +- src/drivers/gpio_expander.cpp | 140 ---------------------- src/drivers/i2s_dac.cpp | 15 +-- src/drivers/include/digital_pot.hpp | 49 -------- src/drivers/include/display.hpp | 8 +- src/drivers/include/gpio_expander.hpp | 148 ------------------------ src/drivers/include/i2s_dac.hpp | 8 +- src/drivers/include/relative_wheel.hpp | 2 +- src/drivers/include/storage.hpp | 8 +- src/drivers/include/touchwheel.hpp | 2 +- src/drivers/storage.cpp | 18 ++- src/drivers/test/test_dac.cpp | 4 +- src/drivers/test/test_gpio_expander.cpp | 11 +- src/drivers/test/test_storage.cpp | 4 +- src/system_fsm/booting.cpp | 10 +- src/system_fsm/include/system_fsm.hpp | 4 +- src/system_fsm/running.cpp | 2 +- src/system_fsm/system_fsm.cpp | 24 ++-- src/ui/include/ui_fsm.hpp | 4 +- src/ui/lvgl_task.cpp | 2 +- src/ui/ui_fsm.cpp | 6 +- 27 files changed, 88 insertions(+), 567 deletions(-) delete mode 100644 src/drivers/digital_pot.cpp delete mode 100644 src/drivers/gpio_expander.cpp delete mode 100644 src/drivers/include/digital_pot.hpp delete mode 100644 src/drivers/include/gpio_expander.hpp diff --git a/src/audio/audio_fsm.cpp b/src/audio/audio_fsm.cpp index 1f4f1f44..36133626 100644 --- a/src/audio/audio_fsm.cpp +++ b/src/audio/audio_fsm.cpp @@ -22,9 +22,8 @@ namespace audio { static const char kTag[] = "audio_fsm"; -drivers::GpioExpander* AudioState::sGpioExpander; +drivers::IGpios* AudioState::sIGpios; std::shared_ptr AudioState::sDac; -std::shared_ptr AudioState::sPots; std::weak_ptr AudioState::sDatabase; std::unique_ptr AudioState::sFileSource; @@ -33,20 +32,19 @@ std::vector> AudioState::sPipeline; std::deque AudioState::sTrackQueue; -auto AudioState::Init(drivers::GpioExpander* gpio_expander, +auto AudioState::Init(drivers::IGpios* gpio_expander, std::weak_ptr 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 diff --git a/src/audio/i2s_audio_output.cpp b/src/audio/i2s_audio_output.cpp index 77de7b43..5f705dd1 100644 --- a/src/audio/i2s_audio_output.cpp +++ b/src/audio/i2s_audio_output.cpp @@ -12,13 +12,12 @@ #include #include -#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 dac, - std::weak_ptr pots) +I2SAudioOutput::I2SAudioOutput(drivers::IGpios* expander, + std::weak_ptr 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(static_cast(GetAdjustedMaxAttenuation()) / - 100.0f * static_cast(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( - static_cast(attenuation_) / - static_cast(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; } diff --git a/src/audio/include/audio_fsm.hpp b/src/audio/include/audio_fsm.hpp index 7e84785f..1a52375b 100644 --- a/src/audio/include/audio_fsm.hpp +++ b/src/audio/include/audio_fsm.hpp @@ -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 { public: - static auto Init(drivers::GpioExpander* gpio_expander, + static auto Init(drivers::IGpios* gpio_expander, std::weak_ptr) -> bool; virtual ~AudioState() {} @@ -54,9 +54,8 @@ class AudioState : public tinyfsm::Fsm { virtual void react(const AudioPipelineIdle&) {} protected: - static drivers::GpioExpander* sGpioExpander; + static drivers::IGpios* sIGpios; static std::shared_ptr sDac; - static std::shared_ptr sPots; static std::weak_ptr sDatabase; static std::unique_ptr sFileSource; diff --git a/src/audio/include/i2s_audio_output.hpp b/src/audio/include/i2s_audio_output.hpp index 5dd6cc27..583a5d6a 100644 --- a/src/audio/include/i2s_audio_output.hpp +++ b/src/audio/include/i2s_audio_output.hpp @@ -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 dac, - std::weak_ptr pots); + I2SAudioOutput(drivers::IGpios* expander, std::weak_ptr 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 dac_; - std::shared_ptr pots_; std::optional current_config_; int_fast8_t left_difference_; diff --git a/src/drivers/CMakeLists.txt b/src/drivers/CMakeLists.txt index fff61f5f..151a3afc 100644 --- a/src/drivers/CMakeLists.txt +++ b/src/drivers/CMakeLists.txt @@ -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}) diff --git a/src/drivers/digital_pot.cpp b/src/drivers/digital_pot.cpp deleted file mode 100644 index b20d982d..00000000 --- a/src/drivers/digital_pot.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2023 jacqueline - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "digital_pot.hpp" - -#include - -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 diff --git a/src/drivers/display.cpp b/src/drivers/display.cpp index 94f27bb6..d1ea367c 100644 --- a/src/drivers/display.cpp +++ b/src/drivers/display.cpp @@ -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()), diff --git a/src/drivers/gpio_expander.cpp b/src/drivers/gpio_expander.cpp deleted file mode 100644 index 5a9bb83e..00000000 --- a/src/drivers/gpio_expander.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2023 jacqueline - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "gpio_expander.hpp" -#include - -#include - -#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 unpack(uint16_t ba) { - return std::pair((uint8_t)ba, (uint8_t)(ba >> 8)); -} - -void interrupt_isr(void* arg) { - GpioExpander* instance = reinterpret_cast(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(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 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 diff --git a/src/drivers/i2s_dac.cpp b/src/drivers/i2s_dac.cpp index dbbb58f1..78ffdea3 100644 --- a/src/drivers/i2s_dac.cpp +++ b/src/drivers/i2s_dac.cpp @@ -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 { +auto I2SDac::create(IGpios* expander) -> std::optional { 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 { 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; } diff --git a/src/drivers/include/digital_pot.hpp b/src/drivers/include/digital_pot.hpp deleted file mode 100644 index e2ca00b1..00000000 --- a/src/drivers/include/digital_pot.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2023 jacqueline - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include -#include - -#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 diff --git a/src/drivers/include/display.hpp b/src/drivers/include/display.hpp index 4b63e1c4..23bbbab9 100644 --- a/src/drivers/include/display.hpp +++ b/src/drivers/include/display.hpp @@ -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 worker_task_; diff --git a/src/drivers/include/gpio_expander.hpp b/src/drivers/include/gpio_expander.hpp deleted file mode 100644 index 8108d176..00000000 --- a/src/drivers/include/gpio_expander.hpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2023 jacqueline - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include - -#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 - 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& 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& 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>& { - return listener_; - } - - auto set_listener(const std::weak_ptr>& 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 ports_; - std::atomic inputs_; - - std::weak_ptr> listener_; -}; - -} // namespace drivers diff --git a/src/drivers/include/i2s_dac.hpp b/src/drivers/include/i2s_dac.hpp index 388d09fa..39eb9c4c 100644 --- a/src/drivers/include/i2s_dac.hpp +++ b/src/drivers/include/i2s_dac.hpp @@ -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; + static auto create(IGpios* expander) -> std::optional; - 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 active_page_; diff --git a/src/drivers/include/relative_wheel.hpp b/src/drivers/include/relative_wheel.hpp index 8d74d551..6edc006a 100644 --- a/src/drivers/include/relative_wheel.hpp +++ b/src/drivers/include/relative_wheel.hpp @@ -13,7 +13,7 @@ #include "esp_err.h" #include "result.hpp" -#include "gpio_expander.hpp" +#include "gpios.hpp" #include "touchwheel.hpp" namespace drivers { diff --git a/src/drivers/include/storage.hpp b/src/drivers/include/storage.hpp index daee13c4..a9269261 100644 --- a/src/drivers/include/storage.hpp +++ b/src/drivers/include/storage.hpp @@ -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; + static auto Create(IGpios* gpio) -> cpp::result; - 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 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; diff --git a/src/drivers/include/touchwheel.hpp b/src/drivers/include/touchwheel.hpp index 5c3442d2..f42b575b 100644 --- a/src/drivers/include/touchwheel.hpp +++ b/src/drivers/include/touchwheel.hpp @@ -12,7 +12,7 @@ #include "esp_err.h" #include "result.hpp" -#include "gpio_expander.hpp" +#include "gpios.hpp" namespace drivers { diff --git a/src/drivers/storage.cpp b/src/drivers/storage.cpp index 4f16f1d1..e3dd8f83 100644 --- a/src/drivers/storage.cpp +++ b/src/drivers/storage.cpp @@ -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 { - 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 { + 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 host; @@ -114,7 +113,7 @@ auto SdStorage::Create(GpioExpander* gpio) -> cpp::result { 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, diff --git a/src/drivers/test/test_dac.cpp b/src/drivers/test/test_dac.cpp index edf9e1e9..e8d8dd94 100644 --- a/src/drivers/test/test_dac.cpp +++ b/src/drivers/test/test_dac.cpp @@ -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 dac_res = AudioDac::create(&expander); REQUIRE(dac_res.has_value()); std::unique_ptr dac(dac_res.value()); diff --git a/src/drivers/test/test_gpio_expander.cpp b/src/drivers/test/test_gpio_expander.cpp index 2a31d9c7..972bcf09 100644 --- a/src/drivers/test/test_gpio_expander.cpp +++ b/src/drivers/test/test_gpio_expander.cpp @@ -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); } } diff --git a/src/drivers/test/test_storage.cpp b/src/drivers/test/test_storage.cpp index 90f2843a..c785fa01 100644 --- a/src/drivers/test/test_storage.cpp +++ b/src/drivers/test/test_storage.cpp @@ -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 result(SdStorage::create(&expander).value()); diff --git a/src/system_fsm/booting.cpp b/src/system_fsm/booting.cpp index e7505f11..41adb906 100644 --- a/src/system_fsm/booting.cpp +++ b/src/system_fsm/booting.cpp @@ -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()); 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()); return; diff --git a/src/system_fsm/include/system_fsm.hpp b/src/system_fsm/include/system_fsm.hpp index 725f2f50..f6a52019 100644 --- a/src/system_fsm/include/system_fsm.hpp +++ b/src/system_fsm/include/system_fsm.hpp @@ -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 { virtual void react(const StorageError&) {} protected: - static std::shared_ptr sGpioExpander; + static std::shared_ptr sGpios; static std::shared_ptr sSamd; static std::shared_ptr sTouch; diff --git a/src/system_fsm/running.cpp b/src/system_fsm/running.cpp index 87c25440..1822a071 100644 --- a/src/system_fsm/running.cpp +++ b/src/system_fsm/running.cpp @@ -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( diff --git a/src/system_fsm/system_fsm.cpp b/src/system_fsm/system_fsm.cpp index dd5161ed..9483088e 100644 --- a/src/system_fsm/system_fsm.cpp +++ b/src/system_fsm/system_fsm.cpp @@ -12,7 +12,7 @@ namespace system_fsm { -std::shared_ptr SystemState::sGpioExpander; +std::shared_ptr SystemState::sGpios; std::shared_ptr SystemState::sSamd; std::shared_ptr 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( diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index d0bb7b2f..4de9344c 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -21,7 +21,7 @@ namespace ui { class UiState : public tinyfsm::Fsm { 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 { virtual void react(const system_fsm::BootComplete&) {} protected: - static drivers::GpioExpander* sGpioExpander; + static drivers::IGpios* sIGpios; static std::shared_ptr sTouchWheel; static std::shared_ptr sRelativeWheel; static std::shared_ptr sDisplay; diff --git a/src/ui/lvgl_task.cpp b/src/ui/lvgl_task.cpp index d70782b2..37cde858 100644 --- a/src/ui/lvgl_task.cpp +++ b/src/ui/lvgl_task.cpp @@ -42,7 +42,7 @@ #include "widgets/lv_label.h" #include "display.hpp" -#include "gpio_expander.hpp" +#include "gpios.hpp" namespace ui { diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index c2ac6d8b..709778c7 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -18,15 +18,15 @@ namespace ui { -drivers::GpioExpander* UiState::sGpioExpander; +drivers::IGpios* UiState::sIGpios; 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) -> bool { - sGpioExpander = gpio_expander; +auto UiState::Init(drivers::IGpios* gpio_expander) -> bool { + sIGpios = gpio_expander; lv_init(); sDisplay.reset(