summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio/audio_fsm.cpp13
-rw-r--r--src/audio/i2s_audio_output.cpp10
-rw-r--r--src/audio/include/audio_events.hpp3
-rw-r--r--src/audio/include/audio_fsm.hpp3
-rw-r--r--src/audio/include/audio_sink.hpp4
-rw-r--r--src/audio/include/i2s_audio_output.hpp4
-rw-r--r--src/drivers/gpio_expander.cpp101
-rw-r--r--src/drivers/include/gpio_expander.hpp72
-rw-r--r--src/drivers/storage.cpp4
-rw-r--r--src/events/include/event_queue.hpp11
-rw-r--r--src/system_fsm/booting.cpp4
-rw-r--r--src/system_fsm/include/system_events.hpp19
-rw-r--r--src/system_fsm/include/system_fsm.hpp1
-rw-r--r--src/system_fsm/system_fsm.cpp38
-rw-r--r--src/ui/include/ui_fsm.hpp4
-rw-r--r--src/ui/ui_fsm.cpp4
16 files changed, 218 insertions, 77 deletions
diff --git a/src/audio/audio_fsm.cpp b/src/audio/audio_fsm.cpp
index ffd0d5c3..1f4f1f44 100644
--- a/src/audio/audio_fsm.cpp
+++ b/src/audio/audio_fsm.cpp
@@ -11,6 +11,7 @@
#include "audio_decoder.hpp"
#include "audio_events.hpp"
#include "audio_task.hpp"
+#include "event_queue.hpp"
#include "fatfs_audio_input.hpp"
#include "i2s_audio_output.hpp"
#include "i2s_dac.hpp"
@@ -65,6 +66,18 @@ void AudioState::react(const system_fsm::StorageMounted& ev) {
sDatabase = ev.db;
}
+void AudioState::react(const system_fsm::KeyUpChanged& ev) {
+ if (ev.falling && sI2SOutput->AdjustVolumeUp()) {
+ events::Dispatch<VolumeChanged, ui::UiState>({});
+ }
+}
+
+void AudioState::react(const system_fsm::KeyDownChanged& ev) {
+ if (ev.falling && sI2SOutput->AdjustVolumeDown()) {
+ events::Dispatch<VolumeChanged, ui::UiState>({});
+ }
+}
+
namespace states {
void Uninitialised::react(const system_fsm::BootComplete&) {
diff --git a/src/audio/i2s_audio_output.cpp b/src/audio/i2s_audio_output.cpp
index b61259ad..77de7b43 100644
--- a/src/audio/i2s_audio_output.cpp
+++ b/src/audio/i2s_audio_output.cpp
@@ -100,20 +100,22 @@ auto I2SAudioOutput::GetAdjustedMaxAttenuation() -> int_fast8_t {
return adjusted_max;
}
-auto I2SAudioOutput::AdjustVolumeUp() -> void {
+auto I2SAudioOutput::AdjustVolumeUp() -> bool {
if (attenuation_ + left_difference_ <= pots_->GetMinAttenuation()) {
- return;
+ return false;
}
attenuation_--;
pots_->SetRelative(-1);
+ return true;
}
-auto I2SAudioOutput::AdjustVolumeDown() -> void {
+auto I2SAudioOutput::AdjustVolumeDown() -> bool {
if (attenuation_ - left_difference_ >= pots_->GetMaxAttenuation()) {
- return;
+ return false;
}
attenuation_++;
pots_->SetRelative(1);
+ return true;
}
auto I2SAudioOutput::Configure(const StreamInfo::Format& format) -> bool {
diff --git a/src/audio/include/audio_events.hpp b/src/audio/include/audio_events.hpp
index 7359e8ac..019b65a2 100644
--- a/src/audio/include/audio_events.hpp
+++ b/src/audio/include/audio_events.hpp
@@ -7,6 +7,7 @@
#pragma once
#include <stdint.h>
+#include <cstdint>
#include <string>
#include "tinyfsm.hpp"
@@ -32,4 +33,6 @@ struct InputFileOpened : tinyfsm::Event {};
struct InputFileFinished : tinyfsm::Event {};
struct AudioPipelineIdle : tinyfsm::Event {};
+struct VolumeChanged : tinyfsm::Event {};
+
} // namespace audio
diff --git a/src/audio/include/audio_fsm.hpp b/src/audio/include/audio_fsm.hpp
index 1f3b1dbd..7e84785f 100644
--- a/src/audio/include/audio_fsm.hpp
+++ b/src/audio/include/audio_fsm.hpp
@@ -40,6 +40,9 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
void react(const system_fsm::StorageMounted&);
+ void react(const system_fsm::KeyUpChanged&);
+ void react(const system_fsm::KeyDownChanged&);
+
virtual void react(const system_fsm::BootComplete&) {}
virtual void react(const PlayTrack&) {}
virtual void react(const PlayFile&) {}
diff --git a/src/audio/include/audio_sink.hpp b/src/audio/include/audio_sink.hpp
index e6538bda..ac007bf8 100644
--- a/src/audio/include/audio_sink.hpp
+++ b/src/audio/include/audio_sink.hpp
@@ -45,8 +45,8 @@ class IAudioSink {
virtual auto SetVolumeImbalance(int_fast8_t balance) -> void = 0;
virtual auto SetVolume(uint_fast8_t percent) -> void = 0;
virtual auto GetVolume() -> uint_fast8_t = 0;
- virtual auto AdjustVolumeUp() -> void = 0;
- virtual auto AdjustVolumeDown() -> void = 0;
+ virtual auto AdjustVolumeUp() -> bool = 0;
+ virtual auto AdjustVolumeDown() -> bool = 0;
virtual auto Configure(const StreamInfo::Format& format) -> bool = 0;
virtual auto Send(const cpp::span<std::byte>& data) -> void = 0;
diff --git a/src/audio/include/i2s_audio_output.hpp b/src/audio/include/i2s_audio_output.hpp
index 7c125476..5dd6cc27 100644
--- a/src/audio/include/i2s_audio_output.hpp
+++ b/src/audio/include/i2s_audio_output.hpp
@@ -34,8 +34,8 @@ class I2SAudioOutput : public IAudioSink {
auto SetVolumeImbalance(int_fast8_t balance) -> void override;
auto SetVolume(uint_fast8_t percent) -> void override;
auto GetVolume() -> uint_fast8_t override;
- auto AdjustVolumeUp() -> void override;
- auto AdjustVolumeDown() -> void override;
+ auto AdjustVolumeUp() -> bool override;
+ auto AdjustVolumeDown() -> bool override;
auto Configure(const StreamInfo::Format& format) -> bool override;
auto Send(const cpp::span<std::byte>& data) -> void override;
diff --git a/src/drivers/gpio_expander.cpp b/src/drivers/gpio_expander.cpp
index 87c5d038..5a9bb83e 100644
--- a/src/drivers/gpio_expander.cpp
+++ b/src/drivers/gpio_expander.cpp
@@ -5,35 +5,97 @@
*/
#include "gpio_expander.hpp"
+#include <stdint.h>
#include <cstdint>
+#include "driver/gpio.h"
+#include "hal/gpio_types.h"
#include "i2c.hpp"
namespace drivers {
-GpioExpander::GpioExpander() {
- ports_ = pack(kPortADefault, kPortBDefault);
- // Read and write initial values on initialisation so that we do not have a
- // strange partially-initialised state.
- // TODO: log or abort if these error; it's really bad!
- Write();
- Read();
+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;
}
-GpioExpander::~GpioExpander() {}
+/*
+ * 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 GpioExpander::with(std::function<void(GpioExpander&)> f) {
- f(*this);
- Write();
+void interrupt_isr(void* arg) {
+ GpioExpander* instance = reinterpret_cast<GpioExpander*>(arg);
+ auto listener = instance->listener().lock();
+ if (listener) {
+ std::invoke(*listener);
+ }
}
-esp_err_t GpioExpander::Write() {
- i2c_cmd_handle_t handle = i2c_cmd_link_create();
- if (handle == NULL) {
- return ESP_ERR_NO_MEM;
+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;
@@ -42,10 +104,10 @@ esp_err_t GpioExpander::Write() {
.write_ack(ports_ab.first, ports_ab.second)
.stop();
- return transaction.Execute();
+ return transaction.Execute() == ESP_OK;
}
-esp_err_t GpioExpander::Read() {
+bool GpioExpander::Read() {
uint8_t input_a, input_b;
I2CTransaction transaction;
@@ -56,8 +118,11 @@ esp_err_t GpioExpander::Read() {
.stop();
esp_err_t ret = transaction.Execute();
+ if (ret != ESP_OK) {
+ return false;
+ }
inputs_ = pack(input_a, input_b);
- return ret;
+ return true;
}
void GpioExpander::set_pin(Pin pin, bool value) {
diff --git a/src/drivers/include/gpio_expander.hpp b/src/drivers/include/gpio_expander.hpp
index 8231e140..8108d176 100644
--- a/src/drivers/include/gpio_expander.hpp
+++ b/src/drivers/include/gpio_expander.hpp
@@ -10,6 +10,7 @@
#include <atomic>
#include <functional>
+#include <memory>
#include <mutex>
#include <optional>
#include <tuple>
@@ -35,52 +36,9 @@ namespace drivers {
*/
class GpioExpander {
public:
- static auto Create() -> GpioExpander* { return new GpioExpander(); }
-
- GpioExpander();
+ static auto Create() -> GpioExpander*;
~GpioExpander();
- static const uint8_t kPca8575Address = 0x20;
- static const uint8_t kPca8575Timeout = pdMS_TO_TICKS(100);
-
- // 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
- // 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 - trs output 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 = 0b00000010;
-
- /*
- * Convenience mehod for packing the port a and b bytes into a single 16 bit
- * value.
- */
- static 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.
- */
- static std::pair<uint8_t, uint8_t> unpack(uint16_t ba) {
- return std::pair((uint8_t)ba, (uint8_t)(ba >> 8));
- }
-
/*
* Convenience function for running some arbitrary pin writing code, then
* flushing a `Write()` to the expander. Example usage:
@@ -91,19 +49,23 @@ class GpioExpander {
* });
* ```
*/
- void with(std::function<void(GpioExpander&)> f);
+ 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`.
*/
- esp_err_t Write(void);
+ auto Write(void) -> bool;
/**
* Reads from the GPIO expander, populating `inputs` with the most recent
* values.
*/
- esp_err_t Read(void);
+ auto Read(void) -> bool;
/* Maps each pin of the expander to its number in a `pack`ed uint16. */
enum Pin {
@@ -113,9 +75,9 @@ class GpioExpander {
KEY_UP = 2,
KEY_DOWN = 3,
KEY_LOCK = 4,
- DISPLAY_RESET = 5,
+ DISPLAY_RESET_ACTIVE_LOW = 5,
// UNUSED = 6,
- SD_CARD_POWER_ENABLE = 7,
+ SD_CARD_POWER_ENABLE_ACTIVE_LOW = 7,
// Port B
PHONE_DETECT = 8,
@@ -161,14 +123,26 @@ class GpioExpander {
*/
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
diff --git a/src/drivers/storage.cpp b/src/drivers/storage.cpp
index 211b1484..4f16f1d1 100644
--- a/src/drivers/storage.cpp
+++ b/src/drivers/storage.cpp
@@ -56,7 +56,7 @@ 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, 0);
+ 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();
@@ -144,7 +144,7 @@ SdStorage::~SdStorage() {
sdspi_host_remove_device(this->handle_);
sdspi_host_deinit();
- gpio_->set_pin(GpioExpander::SD_CARD_POWER_ENABLE, 0);
+ gpio_->set_pin(GpioExpander::SD_CARD_POWER_ENABLE_ACTIVE_LOW, 1);
gpio_->set_pin(GpioExpander::SD_MUX_EN_ACTIVE_LOW, 1);
gpio_->Write();
}
diff --git a/src/events/include/event_queue.hpp b/src/events/include/event_queue.hpp
index eb8dd0a0..01f37896 100644
--- a/src/events/include/event_queue.hpp
+++ b/src/events/include/event_queue.hpp
@@ -13,6 +13,7 @@
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "freertos/queue.h"
+#include "system_fsm.hpp"
#include "tinyfsm.hpp"
#include "ui_fsm.hpp"
@@ -33,6 +34,16 @@ class EventQueue {
return instance;
}
+ template <typename Event>
+ auto DispatchFromISR(const Event& ev) -> bool {
+ WorkItem* item = new WorkItem([=]() {
+ tinyfsm::FsmList<system_fsm::SystemState>::template dispatch<Event>(ev);
+ });
+ BaseType_t ret;
+ xQueueSendFromISR(system_handle_, &item, &ret);
+ return ret;
+ }
+
template <typename Event, typename Machine, typename... Machines>
auto Dispatch(const Event& ev) -> void {
WorkItem* item = new WorkItem(
diff --git a/src/system_fsm/booting.cpp b/src/system_fsm/booting.cpp
index 1e1b2959..e7505f11 100644
--- a/src/system_fsm/booting.cpp
+++ b/src/system_fsm/booting.cpp
@@ -27,6 +27,10 @@ namespace states {
static const char kTag[] = "BOOT";
+static std::function<void(void)> sGpiosCallback = []() {
+ events::EventQueue::GetInstance().DispatchFromISR(internal::GpioInterrupt{});
+};
+
auto Booting::entry() -> void {
ESP_LOGI(kTag, "beginning tangara boot");
ESP_LOGI(kTag, "installing early drivers");
diff --git a/src/system_fsm/include/system_events.hpp b/src/system_fsm/include/system_events.hpp
index ec202c69..0903cb77 100644
--- a/src/system_fsm/include/system_events.hpp
+++ b/src/system_fsm/include/system_events.hpp
@@ -47,6 +47,19 @@ struct StorageMounted : tinyfsm::Event {
struct StorageError : tinyfsm::Event {};
+struct KeyUpChanged : tinyfsm::Event {
+ bool falling;
+};
+struct KeyDownChanged : tinyfsm::Event {
+ bool falling;
+};
+struct KeyLockChanged : tinyfsm::Event {
+ bool falling;
+};
+struct HasPhonesChanged : tinyfsm::Event {
+ bool falling;
+};
+
namespace internal {
/*
@@ -55,6 +68,12 @@ namespace internal {
*/
struct ReadyToUnmount : tinyfsm::Event {};
+/*
+ * Sent when the actual unmount operation should be performed. Always dispatched
+ * by SysState in response to StoragePrepareToUnmount.
+ */
+struct GpioInterrupt : tinyfsm::Event {};
+
} // namespace internal
} // namespace system_fsm
diff --git a/src/system_fsm/include/system_fsm.hpp b/src/system_fsm/include/system_fsm.hpp
index 037c0a0e..725f2f50 100644
--- a/src/system_fsm/include/system_fsm.hpp
+++ b/src/system_fsm/include/system_fsm.hpp
@@ -38,6 +38,7 @@ class SystemState : public tinyfsm::Fsm<SystemState> {
void react(const tinyfsm::Event& ev) {}
void react(const FatalError&);
+ void react(const internal::GpioInterrupt&);
virtual void react(const DisplayReady&) {}
virtual void react(const BootComplete&) {}
diff --git a/src/system_fsm/system_fsm.cpp b/src/system_fsm/system_fsm.cpp
index c59c0908..dd5161ed 100644
--- a/src/system_fsm/system_fsm.cpp
+++ b/src/system_fsm/system_fsm.cpp
@@ -5,6 +5,8 @@
*/
#include "system_fsm.hpp"
+#include "audio_fsm.hpp"
+#include "event_queue.hpp"
#include "relative_wheel.hpp"
#include "system_events.hpp"
@@ -28,6 +30,42 @@ 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);
+
+ sGpioExpander->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);
+
+ if (key_up != prev_key_up) {
+ events::Dispatch<KeyUpChanged, audio::AudioState, ui::UiState>(
+ {.falling = prev_key_up});
+ }
+ if (key_down != prev_key_down) {
+ events::Dispatch<KeyDownChanged, audio::AudioState, ui::UiState>(
+ {.falling = prev_key_down});
+ }
+ if (key_lock != prev_key_lock) {
+ events::Dispatch<KeyLockChanged, SystemState, ui::UiState>(
+ {.falling = prev_key_lock});
+ }
+ if (has_headphones != prev_has_headphones) {
+ events::Dispatch<HasPhonesChanged, audio::AudioState>(
+ {.falling = prev_has_headphones});
+ }
+}
+
} // namespace system_fsm
FSM_INITIAL_STATE(system_fsm::SystemState, system_fsm::states::Booting)
diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp
index 8b80f162..d0bb7b2f 100644
--- a/src/ui/include/ui_fsm.hpp
+++ b/src/ui/include/ui_fsm.hpp
@@ -35,6 +35,8 @@ class UiState : public tinyfsm::Fsm<UiState> {
/* Fallback event handler. Does nothing. */
void react(const tinyfsm::Event& ev) {}
+ virtual void react(const system_fsm::KeyLockChanged&){};
+
virtual void react(const system_fsm::DisplayReady&) {}
virtual void react(const system_fsm::BootComplete&) {}
@@ -58,6 +60,8 @@ class Splash : public UiState {
class Interactive : public UiState {
void entry() override;
+
+ void react(const system_fsm::KeyLockChanged&) override;
};
class FatalError : public UiState {};
diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp
index bdc1f89f..c2ac6d8b 100644
--- a/src/ui/ui_fsm.cpp
+++ b/src/ui/ui_fsm.cpp
@@ -68,6 +68,10 @@ void Interactive::entry() {
sCurrentScreen.reset(new screens::Menu());
}
+void Interactive::react(const system_fsm::KeyLockChanged& ev) {
+ sDisplay->SetDisplayOn(ev.falling);
+}
+
} // namespace states
} // namespace ui