From 763d58f411e189836dce5cde9fe15886554717cf Mon Sep 17 00:00:00 2001 From: Adam Wonak Date: Sat, 21 Feb 2026 09:44:50 -0800 Subject: [PATCH] add additional external ppqn --- firmware/Euclidean/display.h | 6 + firmware/Gravity/display.h | 6 + src/clock.h | 287 +++++++++++++++++------------------ 3 files changed, 153 insertions(+), 146 deletions(-) diff --git a/firmware/Euclidean/display.h b/firmware/Euclidean/display.h index 6dd6d59..e679f4a 100644 --- a/firmware/Euclidean/display.h +++ b/firmware/Euclidean/display.h @@ -266,6 +266,12 @@ void DisplayMainPage() { case Clock::SOURCE_EXTERNAL_PPQN_4: subText = F("4 PPQN"); break; + case Clock::SOURCE_EXTERNAL_PPQN_2: + subText = F("2 PPQN"); + break; + case Clock::SOURCE_EXTERNAL_PPQN_1: + subText = F("1 PPQN"); + break; case Clock::SOURCE_EXTERNAL_MIDI: subText = F("MIDI"); break; diff --git a/firmware/Gravity/display.h b/firmware/Gravity/display.h index f265d91..03b8517 100644 --- a/firmware/Gravity/display.h +++ b/firmware/Gravity/display.h @@ -285,6 +285,12 @@ void DisplayMainPage() { case Clock::SOURCE_EXTERNAL_PPQN_4: subText = F("4 PPQN"); break; + case Clock::SOURCE_EXTERNAL_PPQN_2: + subText = F("2 PPQN"); + break; + case Clock::SOURCE_EXTERNAL_PPQN_1: + subText = F("1 PPQN"); + break; case Clock::SOURCE_EXTERNAL_MIDI: subText = F("MIDI"); break; diff --git a/src/clock.h b/src/clock.h index 613667a..ae121cc 100644 --- a/src/clock.h +++ b/src/clock.h @@ -4,7 +4,7 @@ * @brief Wrapper Class for clock timing functions. * @version 0.1 * @date 2025-05-04 - * + * * @copyright MIT - (c) 2025 - Adam Wonak - adam.wonak@gmail.com * */ @@ -17,7 +17,8 @@ #include "peripherials.h" #include "uClock/uClock.h" -// MIDI clock, start, stop, and continue byte definitions - based on MIDI 1.0 Standards. +// MIDI clock, start, stop, and continue byte definitions - based on MIDI 1.0 +// Standards. #define MIDI_CLOCK 0xF8 #define MIDI_START 0xFA #define MIDI_STOP 0xFC @@ -28,164 +29,158 @@ static ExtCallback extUserCallback = nullptr; static void serialEventNoop(uint8_t msg, uint8_t status) {} class Clock { - public: - static constexpr int DEFAULT_TEMPO = 120; +public: + static constexpr int DEFAULT_TEMPO = 120; - enum Source { - SOURCE_INTERNAL, - SOURCE_EXTERNAL_PPQN_24, - SOURCE_EXTERNAL_PPQN_4, - SOURCE_EXTERNAL_MIDI, - SOURCE_LAST, - }; + enum Source { + SOURCE_INTERNAL, + SOURCE_EXTERNAL_PPQN_24, + SOURCE_EXTERNAL_PPQN_4, + SOURCE_EXTERNAL_PPQN_2, + SOURCE_EXTERNAL_PPQN_1, + SOURCE_EXTERNAL_MIDI, + SOURCE_LAST, + }; - enum Pulse { - PULSE_NONE, - PULSE_PPQN_1, - PULSE_PPQN_4, - PULSE_PPQN_24, - PULSE_LAST, - }; + enum Pulse { + PULSE_NONE, + PULSE_PPQN_1, + PULSE_PPQN_4, + PULSE_PPQN_24, + PULSE_LAST, + }; - void Init() { - NeoSerial.begin(31250); + void Init() { + NeoSerial.begin(31250); - // Initialize the clock library - uClock.init(); - uClock.setClockMode(uClock.INTERNAL_CLOCK); - uClock.setOutputPPQN(uClock.PPQN_96); - uClock.setTempo(DEFAULT_TEMPO); + // Initialize the clock library + uClock.init(); + uClock.setClockMode(uClock.INTERNAL_CLOCK); + uClock.setOutputPPQN(uClock.PPQN_96); + uClock.setTempo(DEFAULT_TEMPO); - // MIDI events. - uClock.setOnClockStart(sendMIDIStart); - uClock.setOnClockStop(sendMIDIStop); - uClock.setOnSync24(sendMIDIClock); + // MIDI events. + uClock.setOnClockStart(sendMIDIStart); + uClock.setOnClockStop(sendMIDIStop); + uClock.setOnSync24(sendMIDIClock); - uClock.start(); + uClock.start(); + } + + // Handle external clock tick and call user callback when receiving clock + // trigger (PPQN_4, PPQN_24, or MIDI). + void AttachExtHandler(void (*callback)()) { + extUserCallback = callback; + attachInterrupt(digitalPinToInterrupt(EXT_PIN), callback, RISING); + } + + // Internal PPQN96 callback for all clock timer operations. + void AttachIntHandler(void (*callback)(uint32_t)) { + uClock.setOnOutputPPQN(callback); + } + + // Set the source of the clock mode. + void SetSource(Source source) { + bool was_playing = !IsPaused(); + uClock.stop(); + // If we are changing the source from MIDI, disable the serial interrupt + // handler. + if (source_ == SOURCE_EXTERNAL_MIDI) { + NeoSerial.attachInterrupt(serialEventNoop); } - - // Handle external clock tick and call user callback when receiving clock trigger (PPQN_4, PPQN_24, or MIDI). - void AttachExtHandler(void (*callback)()) { - extUserCallback = callback; - attachInterrupt(digitalPinToInterrupt(EXT_PIN), callback, RISING); + source_ = source; + switch (source) { + case SOURCE_INTERNAL: + uClock.setClockMode(uClock.INTERNAL_CLOCK); + break; + case SOURCE_EXTERNAL_PPQN_24: + uClock.setClockMode(uClock.EXTERNAL_CLOCK); + uClock.setInputPPQN(uClock.PPQN_24); + break; + case SOURCE_EXTERNAL_PPQN_4: + uClock.setClockMode(uClock.EXTERNAL_CLOCK); + uClock.setInputPPQN(uClock.PPQN_4); + break; + case SOURCE_EXTERNAL_PPQN_2: + uClock.setClockMode(uClock.EXTERNAL_CLOCK); + uClock.setInputPPQN(uClock.PPQN_2); + break; + case SOURCE_EXTERNAL_PPQN_1: + uClock.setClockMode(uClock.EXTERNAL_CLOCK); + uClock.setInputPPQN(uClock.PPQN_1); + break; + case SOURCE_EXTERNAL_MIDI: + uClock.setClockMode(uClock.EXTERNAL_CLOCK); + uClock.setInputPPQN(uClock.PPQN_24); + NeoSerial.attachInterrupt(onSerialEvent); + break; } - - // Internal PPQN96 callback for all clock timer operations. - void AttachIntHandler(void (*callback)(uint32_t)) { - uClock.setOnOutputPPQN(callback); + if (was_playing) { + uClock.start(); } + } - // Set the source of the clock mode. - void SetSource(Source source) { - bool was_playing = !IsPaused(); - uClock.stop(); - // If we are changing the source from MIDI, disable the serial interrupt handler. - if (source_ == SOURCE_EXTERNAL_MIDI) { - NeoSerial.attachInterrupt(serialEventNoop); - } - source_ = source; - switch (source) { - case SOURCE_INTERNAL: - uClock.setClockMode(uClock.INTERNAL_CLOCK); - break; - case SOURCE_EXTERNAL_PPQN_24: - uClock.setClockMode(uClock.EXTERNAL_CLOCK); - uClock.setInputPPQN(uClock.PPQN_24); - break; - case SOURCE_EXTERNAL_PPQN_4: - uClock.setClockMode(uClock.EXTERNAL_CLOCK); - uClock.setInputPPQN(uClock.PPQN_4); - break; - case SOURCE_EXTERNAL_MIDI: - uClock.setClockMode(uClock.EXTERNAL_CLOCK); - uClock.setInputPPQN(uClock.PPQN_24); - NeoSerial.attachInterrupt(onSerialEvent); - break; - } - if (was_playing) { - uClock.start(); - } + // Return true if the current selected source is externl (PPQN_4, PPQN_24, or + // MIDI). + bool ExternalSource() { + return uClock.getClockMode() == uClock.EXTERNAL_CLOCK; + } + + // Return true if the current selected source is the internal master clock. + bool InternalSource() { + return uClock.getClockMode() == uClock.INTERNAL_CLOCK; + } + + // Returns the current BPM tempo. + int Tempo() { return uClock.getTempo(); } + + // Set the clock tempo to a int between 1 and 400. + void SetTempo(int tempo) { return uClock.setTempo(tempo); } + + // Record an external clock tick received to process external/internal + // syncronization. + void Tick() { uClock.clockMe(); } + + // Start the internal clock. + void Start() { uClock.start(); } + + // Stop internal clock clock. + void Stop() { uClock.stop(); } + + // Reset all clock counters to 0. + void Reset() { uClock.resetCounters(); } + + // Returns true if the clock is not running. + bool IsPaused() { return uClock.clock_state == uClock.PAUSED; } + +private: + Source source_ = SOURCE_INTERNAL; + + static void onSerialEvent(uint8_t msg, uint8_t status) { + // Note: uClock start and stop will echo to MIDI. + switch (msg) { + case MIDI_CLOCK: + if (extUserCallback) { + extUserCallback(); + } + break; + case MIDI_STOP: + uClock.stop(); + sendMIDIStop(); + break; + case MIDI_START: + case MIDI_CONTINUE: + uClock.start(); + sendMIDIStart(); + break; } + } - // Return true if the current selected source is externl (PPQN_4, PPQN_24, or MIDI). - bool ExternalSource() { - return uClock.getClockMode() == uClock.EXTERNAL_CLOCK; - } + static void sendMIDIStart() { NeoSerial.write(MIDI_START); } - // Return true if the current selected source is the internal master clock. - bool InternalSource() { - return uClock.getClockMode() == uClock.INTERNAL_CLOCK; - } + static void sendMIDIStop() { NeoSerial.write(MIDI_STOP); } - // Returns the current BPM tempo. - int Tempo() { - return uClock.getTempo(); - } - - // Set the clock tempo to a int between 1 and 400. - void SetTempo(int tempo) { - return uClock.setTempo(tempo); - } - - // Record an external clock tick received to process external/internal syncronization. - void Tick() { - uClock.clockMe(); - } - - // Start the internal clock. - void Start() { - uClock.start(); - } - - // Stop internal clock clock. - void Stop() { - uClock.stop(); - } - - // Reset all clock counters to 0. - void Reset() { - uClock.resetCounters(); - } - - // Returns true if the clock is not running. - bool IsPaused() { - return uClock.clock_state == uClock.PAUSED; - } - - private: - Source source_ = SOURCE_INTERNAL; - - static void onSerialEvent(uint8_t msg, uint8_t status) { - // Note: uClock start and stop will echo to MIDI. - switch (msg) { - case MIDI_CLOCK: - if (extUserCallback) { - extUserCallback(); - } - break; - case MIDI_STOP: - uClock.stop(); - sendMIDIStop(); - break; - case MIDI_START: - case MIDI_CONTINUE: - uClock.start(); - sendMIDIStart(); - break; - } - } - - static void sendMIDIStart() { - NeoSerial.write(MIDI_START); - } - - static void sendMIDIStop() { - NeoSerial.write(MIDI_STOP); - } - - static void sendMIDIClock(uint32_t tick) { - NeoSerial.write(MIDI_CLOCK); - } + static void sendMIDIClock(uint32_t tick) { NeoSerial.write(MIDI_CLOCK); } }; #endif \ No newline at end of file