From 5c7fe5e88d0f2dfa0ed1ffdf10fe16560d5fef60 Mon Sep 17 00:00:00 2001 From: Dominic DiTaranto Date: Mon, 9 Mar 2026 23:35:35 -0400 Subject: [PATCH] save, load, reset, PPQN, run --- include/DisplayHandler.h | 14 ++++ include/EncoderHandler.h | 2 +- include/Settings.h | 4 + include/globals.h | 9 +++ src/DisplayHandler.cpp | 157 ++++++++++++++++++++++++++++++++++++--- src/EncoderHandler.cpp | 15 +--- src/Settings.cpp | 3 + src/main.cpp | 64 +++++++++++++--- 8 files changed, 233 insertions(+), 35 deletions(-) diff --git a/include/DisplayHandler.h b/include/DisplayHandler.h index 163a7ba..ff48d36 100644 --- a/include/DisplayHandler.h +++ b/include/DisplayHandler.h @@ -35,8 +35,20 @@ class DisplayHandler { "Mute" }; bool onOutScreen = 0; + + bool onSettingsScreen = 0; + std::array settings = { + "Save", + "Load", + "Reset", + "PPQN", + "Run", + "Exit" + }; + void renderMainPage(); void renderOutPage(); + void renderSettingsPage(); public: @@ -51,6 +63,8 @@ class DisplayHandler { void setup(); void render(); void handleClick(); + void exitSettingsScreen(); + void flashMessage(std::string msg); void moveCursor(bool dir = 1); }; diff --git a/include/EncoderHandler.h b/include/EncoderHandler.h index 72ba174..4fa10e4 100644 --- a/include/EncoderHandler.h +++ b/include/EncoderHandler.h @@ -21,7 +21,7 @@ public: uint16_t clk_last_state; void setup(); - static void gpio_callback(uint gpio, uint32_t events); + // static void gpio_callback(uint gpio, uint32_t events); void moveCursor(bool dir = 1); void update(); }; diff --git a/include/Settings.h b/include/Settings.h index ea35fb1..6497816 100644 --- a/include/Settings.h +++ b/include/Settings.h @@ -21,6 +21,10 @@ struct DeviceSettings { uint32_t version; OutputConfig configs[MAX_OUTPUTS]; ModSlot slots[16]; + bool play; + uint8_t bpm; + bool run; + uint8_t ppqnidx; }; void save(); diff --git a/include/globals.h b/include/globals.h index 3035cc6..5e85ee9 100644 --- a/include/globals.h +++ b/include/globals.h @@ -18,6 +18,8 @@ static constexpr uint8_t OUT_6_PIN = 10; static constexpr uint8_t OUT_7_PIN = 12; static constexpr uint8_t OUT_8_PIN = 14; +static constexpr uint8_t IN_RUN_PIN = 17; + static constexpr uint8_t SCREEN_SCL_PIN = 18; static constexpr uint8_t SCREEN_SDA_PIN = 19; @@ -28,6 +30,8 @@ static constexpr uint8_t ENCODER_CLK_PIN = 20; static constexpr uint8_t ENCODER_DT_PIN = 21; static constexpr uint8_t ENCODER_SW_PIN = 22; +void gpio_callback(uint gpio, uint32_t events); + // TIME BASED extern volatile bool PLAY; extern volatile uint8_t BPM; @@ -35,6 +39,11 @@ static constexpr uint32_t MINUTE_US = 60000000; static constexpr uint8_t PPQN = 96; extern volatile uint32_t MASTER_TICK; +extern volatile bool RUN; + +extern volatile uint8_t EXTPPQNIdx; +#define PPQN_OPTS_LEN 5 +extern const uint16_t PPQNOPTS[PPQN_OPTS_LEN]; enum WaveShape { SQUARE, TRIANGLE, SAW, RAMP, EXP, HALFSINE, REXP, LOG, SINE, BOUNCE, SIGMO, WOBBLE, STEPDW, STEPUP, SH, SHAPE_COUNT}; diff --git a/src/DisplayHandler.cpp b/src/DisplayHandler.cpp index eb6879f..466a3f7 100644 --- a/src/DisplayHandler.cpp +++ b/src/DisplayHandler.cpp @@ -1,15 +1,20 @@ // DisplayHandler.cpp #include "DisplayHandler.h" #include "Mod.h" +#include "Settings.h" #include "globals.h" #include "pico/stdlib.h" +#include #include +#include #include #include "hardware/i2c.h" #include "pico-ssd1306/shapeRenderer/ShapeRenderer.h" #include "pico-ssd1306/ssd1306.h" #include "pico-ssd1306/textRenderer/TextRenderer.h" +#include "textRenderer/12x16_font.h" +#include "textRenderer/8x8_font.h" pico_ssd1306::SSD1306 *display = nullptr; extern void update_BPM(bool up); @@ -292,12 +297,23 @@ void DisplayHandler::moveCursor(bool dir) { cursorPosition--; } - if (cursorPosition > mainMaxCursorPosition) { - cursorPosition = 0; - } + if (!onSettingsScreen) { + if (cursorPosition > mainMaxCursorPosition) { + cursorPosition = 0; + } - if (cursorPosition < 0) { - cursorPosition = mainMaxCursorPosition; + if (cursorPosition < 0) { + cursorPosition = mainMaxCursorPosition; + } + } else if (onSettingsScreen) { + + if (cursorPosition > 5) { + cursorPosition = 0; + } + + if (cursorPosition < 0) { + cursorPosition = 5; + } } } } @@ -308,7 +324,7 @@ void DisplayHandler::moveCursor(bool dir) { void DisplayHandler::handleClick() { cursorClick ^= true; - if (onOutScreen) { + if (onOutScreen && !onSettingsScreen) { if (currentScreen == 0) { // exit screen cursorPosition = currentOut + 1; currentOut = -1; @@ -324,8 +340,8 @@ void DisplayHandler::handleClick() { } } else { - if (currentScreen == 0) { // on main screen - if (cursorPosition == 0) { // Change BPM + if (currentScreen == 0 && !onSettingsScreen) { // on main screen + if (cursorPosition == 0) { // Change BPM } else if (cursorPosition > 0 && cursorPosition < 9) { // go to out screen currentOut = cursorPosition - 1; @@ -335,6 +351,81 @@ void DisplayHandler::handleClick() { cursorClick = 0; } else if (cursorPosition == 9) { // PLAY/PAUSE BUTTON PLAY ^= true; + } else if (cursorPosition == 10) { // GLOBAL SETTINGS + cursorPosition = 0; + onSettingsScreen = 1; + cursorClick = 0; + } + } else if (onSettingsScreen) { + if (cursorPosition == 0) { // SAVE + flashMessage("Saving..."); + + for (int i = 0; i < 8; i++) { + outputs[i]->pack(globalSettings.configs[i]); + } + + globalSettings.bpm = BPM; + globalSettings.play = PLAY; + globalSettings.run = RUN; + globalSettings.ppqnidx = EXTPPQNIdx; + + memcpy(globalSettings.slots, matrix.slots, sizeof(ModSlot) * 16); + + save(); + + exitSettingsScreen(); + } else if (cursorPosition == 1) { // LOAD + flashMessage("Loading..."); + PLAY = false; + bool loaded_defaults = load(); + + if (!loaded_defaults) { + for (int i = 0; i < 16; i++) { + matrix.slots[i] = globalSettings.slots[i]; + } + + BPM = globalSettings.bpm; + PLAY = globalSettings.play; + RUN = globalSettings.run; + EXTPPQNIdx = globalSettings.ppqnidx; + } + + for (int i = 0; i < 8; i++) { + outputs[i]->unpack(globalSettings.configs[i]); + } + + exitSettingsScreen(); + } else if (cursorPosition == 2) { // RESET + flashMessage("Resetting..."); + PLAY = false; + load_default(); + + for (int i = 0; i < 8; i++) { + outputs[i]->unpack(globalSettings.configs[i]); + } + + for (int i = 0; i < sizeof(outputs); i++) { + outputs[i]->setupPatches(); + } + + PLAY = true; + exitSettingsScreen(); + } else if (cursorPosition == 3) { // PPQN + EXTPPQNIdx++; + + if (EXTPPQNIdx >= PPQN_OPTS_LEN) { + EXTPPQNIdx = 0; + } + + } else if (cursorPosition == 4) { // RUN + RUN ^= true; + + if (RUN) { + PLAY = false; + } + + } else if (cursorPosition == 5) { // EXIT + exitSettingsScreen(); } } } @@ -342,14 +433,25 @@ void DisplayHandler::handleClick() { updateScreen = 1; } +void DisplayHandler::exitSettingsScreen() { + onOutScreen = 0; + onSettingsScreen = 0; + cursorPosition = 10; + cursorClick = 0; + currentScreen = 0; +} + void DisplayHandler::render() { if (updateScreen) { display->clear(); - if (currentScreen == 0 && currentOut == -1) { // main screen + if (!onSettingsScreen && currentScreen == 0 && + currentOut == -1) { // main screen renderMainPage(); - } else if (currentOut != -1) { + } else if (!onSettingsScreen && currentOut != -1) { renderOutPage(); + } else if (onSettingsScreen) { + renderSettingsPage(); } display->sendBuffer(); @@ -503,3 +605,38 @@ void DisplayHandler::renderOutPage() { pico_ssd1306::drawText(display, font_12x16, param_string.c_str(), 1, 45); } } + +void DisplayHandler::flashMessage(std::string msg) { + + display->clear(); + + pico_ssd1306::drawText(display, font_12x16, msg.c_str(), 1, 1); + + display->sendBuffer(); +} + +void DisplayHandler::renderSettingsPage() { + uint8_t y = 3; + for (int i = 0; i < settings.size(); i++) { + if (cursorPosition == i) { + pico_ssd1306::fillRect(display, 0, y, 50, y + 8); + pico_ssd1306::drawText(display, font_8x8, settings[i].c_str(), 1, y, + pico_ssd1306::WriteMode::SUBTRACT); + + if (settings[i] == "Run") { + std::string param = RUN ? "ON" : "OFF"; + pico_ssd1306::drawText(display, font_12x16, param.c_str(), 70, 20); + } + + if (settings[i] == "PPQN") { + std::string param = std::to_string(PPQNOPTS[EXTPPQNIdx]); + pico_ssd1306::drawText(display, font_12x16, param.c_str(), 70, 20); + } + + } else { + pico_ssd1306::drawText(display, font_8x8, settings[i].c_str(), 1, y); + } + + y += 10; + } +} diff --git a/src/EncoderHandler.cpp b/src/EncoderHandler.cpp index 77addb0..6a239fe 100644 --- a/src/EncoderHandler.cpp +++ b/src/EncoderHandler.cpp @@ -17,18 +17,6 @@ EncoderHandler::EncoderHandler(DisplayHandler *display_handler) { button_pressed = 0; } -void EncoderHandler::gpio_callback(uint gpio, uint32_t events) { - uint64_t now = to_us_since_boot(get_absolute_time()); - static uint64_t last_sw_time = 0; - - if (gpio == ENCODER_SW_PIN) { - if (now - last_sw_time > 200000) { // 200ms debounce - self->display_handler->handleClick(); - last_sw_time = now; - } - } -} - void EncoderHandler::setup() { self = this; @@ -36,8 +24,7 @@ void EncoderHandler::setup() { gpio_set_dir(ENCODER_SW_PIN, GPIO_IN); gpio_pull_up(ENCODER_SW_PIN); - gpio_set_irq_enabled_with_callback(ENCODER_SW_PIN, GPIO_IRQ_EDGE_FALL, true, - &EncoderHandler::gpio_callback); + gpio_set_irq_enabled_with_callback(ENCODER_SW_PIN, GPIO_IRQ_EDGE_FALL, true, &gpio_callback); PIO pio = pio0; uint offset = pio_add_program(pio, &quadrature_encoder_program); diff --git a/src/Settings.cpp b/src/Settings.cpp index bbbbf3c..9fc3912 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -48,6 +48,9 @@ void load_default() { s->p = 100; s->level = 100; s->shape = 0; + + BPM = 60; + PLAY = true; } } diff --git a/src/main.cpp b/src/main.cpp index 8181a3f..5e5198e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "DisplayHandler.h" @@ -22,6 +23,9 @@ volatile uint8_t BPM = 60; volatile bool PLAY = true; volatile uint32_t period_us = 0; volatile uint32_t MASTER_TICK; +volatile bool RUN = false; +const uint16_t PPQNOPTS[] = {1, 2, 4, 24, 48}; +volatile uint8_t EXTPPQNIdx = 0; ModMatrix matrix; @@ -89,6 +93,11 @@ void full_save() { outputs[i]->pack(globalSettings.configs[i]); } + globalSettings.bpm = BPM; + globalSettings.play = PLAY; + globalSettings.run = RUN; + globalSettings.ppqnidx = EXTPPQNIdx; + memcpy(globalSettings.slots, matrix.slots, sizeof(ModSlot) * 16); save(); @@ -110,16 +119,6 @@ void setup_outs() { g->setLen(period_us); g->setupPatches(); } - - // // manual setup - // out1.shape = SQUARE; - // out1.setDiv(3); - // out1.setWidth(50); - // out1.level = 100; - // - // out2.shape = SINE; - // out2.setDiv(14); - // out2.setWidth(100); } void handle_outs() { @@ -130,6 +129,40 @@ void handle_outs() { } } +void gpio_callback(uint gpio, uint32_t events) { + + if (gpio == IN_RUN_PIN) { + if (RUN) { + if (events & GPIO_IRQ_EDGE_RISE) { + PLAY = true; + } else if (events & GPIO_IRQ_EDGE_FALL) { + PLAY = false; + } + } + } + + if (gpio == ENCODER_SW_PIN) { + uint64_t now = to_us_since_boot(get_absolute_time()); + static uint64_t last_sw_time = 0; + if (now - last_sw_time > 200000) { // 200ms debounce + display_handler.handleClick(); + last_sw_time = now; + } + } +} + +void setup_ins() { + // SETUP RUN + gpio_init(IN_RUN_PIN); + gpio_set_dir(IN_RUN_PIN, GPIO_IN); + gpio_pull_down(IN_RUN_PIN); + gpio_set_irq_enabled_with_callback(IN_RUN_PIN, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &gpio_callback); + + // SETUP CLOCK + + // SETUP CV INS +} + int main() { stdio_init_all(); sleep_ms(200); @@ -145,6 +178,11 @@ int main() { for (int i = 0; i < 16; i++) { matrix.slots[i] = globalSettings.slots[i]; } + + BPM = globalSettings.bpm; + PLAY = globalSettings.play; + RUN = globalSettings.run; + EXTPPQNIdx = globalSettings.ppqnidx; } for (int i = 0; i < 8; i++) { @@ -159,6 +197,12 @@ int main() { bool lastPlayState = false; + setup_ins(); + + if (RUN) { + PLAY = false; + } + while (true) { encoder_handler.update();