From 152943f514ee110c84b36cdd689961994cbbaba5 Mon Sep 17 00:00:00 2001 From: midilab Date: Tue, 9 May 2023 04:55:26 -0400 Subject: [PATCH 1/7] added experimental support for stm32 boards using Hal library. organize platform independent code into a folder for easy time new ports and mantainance --- .../STM32UartMasterMidiClock.ino | 75 +++++++++ library.properties | 6 +- src/platforms/avr.h | 62 ++++++++ src/platforms/esp32.h | 29 ++++ src/platforms/samd.h | 27 ++++ src/platforms/stm32.h | 65 ++++++++ src/platforms/teensy.h | 25 +++ src/uClock.cpp | 148 ++---------------- src/uClock.h | 5 - 9 files changed, 302 insertions(+), 140 deletions(-) create mode 100644 examples/STM32UartMasterMidiClock/STM32UartMasterMidiClock.ino create mode 100644 src/platforms/avr.h create mode 100644 src/platforms/esp32.h create mode 100644 src/platforms/samd.h create mode 100644 src/platforms/stm32.h create mode 100644 src/platforms/teensy.h diff --git a/examples/STM32UartMasterMidiClock/STM32UartMasterMidiClock.ino b/examples/STM32UartMasterMidiClock/STM32UartMasterMidiClock.ino new file mode 100644 index 0000000..18ad367 --- /dev/null +++ b/examples/STM32UartMasterMidiClock/STM32UartMasterMidiClock.ino @@ -0,0 +1,75 @@ +/* Uart MIDI Sync Box + * + * This example demonstrates how to send MIDI data via Uart + * interface on STM32 family. + * + * This example code is in the public domain. + * + * ... + * + */ +#include + +// MIDI clock, start and stop byte definitions - based on MIDI 1.0 Standards. +#define MIDI_CLOCK 0xF8 +#define MIDI_START 0xFA +#define MIDI_STOP 0xFC + +// the blue led +#define LED_BUILTIN 2 + +uint8_t bpm_blink_timer = 1; +void handle_bpm_led(uint32_t tick) +{ + // BPM led indicator + if ( !(tick % (96)) || (tick == 1) ) { // first compass step will flash longer + bpm_blink_timer = 8; + digitalWrite(LED_BUILTIN, HIGH); + } else if ( !(tick % (24)) ) { // each quarter led on + bpm_blink_timer = 1; + digitalWrite(LED_BUILTIN, HIGH); + } else if ( !(tick % bpm_blink_timer) ) { // get led off + digitalWrite(LED_BUILTIN, LOW); + } +} + +// Internal clock handlers +void ClockOut96PPQN(uint32_t tick) { + // Send MIDI_CLOCK to external gears + Serial.write(MIDI_CLOCK); + handle_bpm_led(tick); +} + +void onClockStart() { + Serial.write(MIDI_START); +} + +void onClockStop() { + Serial.write(MIDI_STOP); +} + +void setup() { + // Initialize serial communication at 31250 bits per second, the default MIDI serial speed communication: + Serial.begin(31250); + + // A led to count bpms + pinMode(LED_BUILTIN, OUTPUT); + + // Setup our clock system + // Inits the clock + uClock.init(); + // Set the callback function for the clock output to send MIDI Sync message. + uClock.setClock96PPQNOutput(ClockOut96PPQN); + // Set the callback function for MIDI Start and Stop messages. + uClock.setOnClockStartOutput(onClockStart); + uClock.setOnClockStopOutput(onClockStop); + // Set the clock BPM to 126 BPM + uClock.setTempo(126); + // Starts the clock, tick-tac-tick-tac... + uClock.start(); +} + +// Do it whatever to interface with Clock.stop(), Clock.start(), Clock.setTempo() and integrate your environment... +void loop() { + +} diff --git a/library.properties b/library.properties index d70c307..2a4655d 100755 --- a/library.properties +++ b/library.properties @@ -1,10 +1,10 @@ name=uClock -version=1.2.0 +version=1.3.0 author=Romulo Silva maintainer=Romulo Silva sentence=BPM clock generator for Arduino platform. -paragraph=A Library to implement BPM clock tick calls using hardware interruption. Supported and tested on AVR boards(ATmega168/328, ATmega16u4/32u4 and ATmega2560) and ARM boards(Teensy, Seedstudio XIAO M0 and ESP32) +paragraph=A Library to implement BPM clock tick calls using hardware interruption. Supported and tested on AVR boards(ATmega168/328, ATmega16u4/32u4 and ATmega2560) and ARM boards(Teensy, Seedstudio XIAO M0. ESP32 and STM32) category=Timing url=https://github.com/midilab/uClock -architectures=avr,arm,samd,esp32 +architectures=avr,arm,samd,stm32,esp32 includes=uClock.h diff --git a/src/platforms/avr.h b/src/platforms/avr.h new file mode 100644 index 0000000..c96db5f --- /dev/null +++ b/src/platforms/avr.h @@ -0,0 +1,62 @@ +#include + +#define ATOMIC(X) noInterrupts(); X; interrupts(); + +// want a different avr clock support? +// TODO: we should do this using macro guards for avrs different clocks +#define AVR_CLOCK_FREQ 16000000 + +void initTimer(uint32_t init_clock) +{ + ATOMIC( + // 16bits Timer1 init + // begin at 120bpm (48.0007680122882 Hz) + TCCR1A = 0; // set entire TCCR1A register to 0 + TCCR1B = 0; // same for TCCR1B + TCNT1 = 0; // initialize counter value to 0 + // set compare match register for 48.0007680122882 Hz increments + OCR1A = 41665; // = 16000000 / (8 * 48.0007680122882) - 1 (must be <65536) + // turn on CTC mode + TCCR1B |= (1 << WGM12); + // Set CS12, CS11 and CS10 bits for 8 prescaler + TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10); + // enable timer compare interrupt + TIMSK1 |= (1 << OCIE1A); + ) +} + +void setTimer(uint32_t us_interval) +{ + float tick_hertz_interval = 1/((float)us_interval/1000000); + + uint32_t ocr; + uint8_t tccr = 0; + + // 16bits avr timer setup + if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 1 )) < 65535) { + // Set CS12, CS11 and CS10 bits for 1 prescaler + tccr |= (0 << CS12) | (0 << CS11) | (1 << CS10); + } else if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 8 )) < 65535) { + // Set CS12, CS11 and CS10 bits for 8 prescaler + tccr |= (0 << CS12) | (1 << CS11) | (0 << CS10); + } else if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 64 )) < 65535) { + // Set CS12, CS11 and CS10 bits for 64 prescaler + tccr |= (0 << CS12) | (1 << CS11) | (1 << CS10); + } else if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 256 )) < 65535) { + // Set CS12, CS11 and CS10 bits for 256 prescaler + tccr |= (1 << CS12) | (0 << CS11) | (0 << CS10); + } else if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 1024 )) < 65535) { + // Set CS12, CS11 and CS10 bits for 1024 prescaler + tccr |= (1 << CS12) | (0 << CS11) | (1 << CS10); + } else { + // tempo not achiavable + return; + } + + ATOMIC( + TCCR1B = 0; + OCR1A = ocr-1; + TCCR1B |= (1 << WGM12); + TCCR1B |= tccr; + ) +} \ No newline at end of file diff --git a/src/platforms/esp32.h b/src/platforms/esp32.h new file mode 100644 index 0000000..5da8080 --- /dev/null +++ b/src/platforms/esp32.h @@ -0,0 +1,29 @@ +#include + +#define TIMER_ID 0 + +hw_timer_t * _uclockTimer = NULL; +portMUX_TYPE _uclockTimerMux = portMUX_INITIALIZER_UNLOCKED; +#define ATOMIC(X) portENTER_CRITICAL_ISR(&_uclockTimerMux); X; portEXIT_CRITICAL_ISR(&_uclockTimerMux); + +// forward declaration of ISR +void ARDUINO_ISR_ATTR uclockISR(); + +void initTimer(uint32_t init_clock) +{ + _uclockTimer = timerBegin(TIMER_ID, 80, true); + + // attach to generic uclock ISR + timerAttachInterrupt(_uclockTimer, &uclockISR, true); + + // init clock tick time + timerAlarmWrite(_uclockTimer, init_clock, true); + + // activate it! + timerAlarmEnable(_uclockTimer); +} + +void setTimer(uint32_t us_interval) +{ + timerAlarmWrite(_uclockTimer, us_interval, true); +} \ No newline at end of file diff --git a/src/platforms/samd.h b/src/platforms/samd.h new file mode 100644 index 0000000..87f9fca --- /dev/null +++ b/src/platforms/samd.h @@ -0,0 +1,27 @@ +#include + +// 24 bits timer +#include +// uses TimerTcc0 +// 16 bits timer +//#include +// uses TimerTc3 +#define ATOMIC(X) noInterrupts(); X; interrupts(); + +IntervalTimer _uclockTimer; + +// forward declaration of ISR +void uclockISR(); + +void initTimer(uint32_t init_clock) +{ + TimerTcc0.initialize(init_clock); + + // attach to generic uclock ISR + TimerTcc0.attachInterrupt(uclockISR); +} + +void setTimer(uint32_t us_interval) +{ + TimerTcc0.setPeriod(us_interval); +} \ No newline at end of file diff --git a/src/platforms/stm32.h b/src/platforms/stm32.h new file mode 100644 index 0000000..45436fe --- /dev/null +++ b/src/platforms/stm32.h @@ -0,0 +1,65 @@ +#include + +static TIM_HandleTypeDef s_TimerInstance = {0}; + +typedef void (*timer_callback_t)(void); +timer_callback_t timer_callback = NULL; + +#define ATOMIC(X) noInterrupts(); X; interrupts(); + +// forward declaration of ISR +void uclockISR(); + +void timer_attachInterrupt(TIM_TypeDef *tim, uint32_t microseconds, timer_callback_t callback) +{ + // Enable timer clock + if (tim == TIM2) __HAL_RCC_TIM2_CLK_ENABLE(); + else if (tim == TIM3) __HAL_RCC_TIM3_CLK_ENABLE(); + else if (tim == TIM4) __HAL_RCC_TIM4_CLK_ENABLE(); + + // Calculate the prescaler value + uint32_t prescaler = (SystemCoreClock / 1000000UL) - 1; + + // Calculate the period value + uint32_t period = (microseconds * 2UL) - 1UL; + + // Set up the timer instance + s_TimerInstance.Instance = tim; + s_TimerInstance.Init.Prescaler = prescaler; + s_TimerInstance.Init.CounterMode = TIM_COUNTERMODE_UP; + s_TimerInstance.Init.Period = period; + s_TimerInstance.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + + // Configure the timer instance + HAL_TIM_Base_Init(&s_TimerInstance); + HAL_TIM_Base_Start_IT(&s_TimerInstance); + + // Save the callback function + timer_callback = callback; +} + +void TIM2_IRQHandler() +{ + // Call the callback function + timer_callback(); + + // Clear the interrupt flag + __HAL_TIM_CLEAR_FLAG(&s_TimerInstance, TIM_FLAG_UPDATE); +} + +void initTimer(uint32_t us_interval) +{ + // Set up the timer to call the callback function every us_interval microseconds + timer_attachInterrupt(TIM2, us_interval, uclockISR); +} + +void setTimer(uint32_t us_interval) +{ + // Calculate the period value + uint32_t period = (us_interval * 2UL) - 1UL; + + // Update the timer instance with the new period value + s_TimerInstance.Init.Period = period; + HAL_TIM_Base_Init(&s_TimerInstance); +} + diff --git a/src/platforms/teensy.h b/src/platforms/teensy.h new file mode 100644 index 0000000..764b7e8 --- /dev/null +++ b/src/platforms/teensy.h @@ -0,0 +1,25 @@ +#include + +#define ATOMIC(X) noInterrupts(); X; interrupts(); + +IntervalTimer _uclockTimer; + +// forward declaration of ISR +void uclockISR(); + +void initTimer(uint32_t init_clock) +{ + _uclockTimer.begin(uclockISR, init_clock); + + // Set the interrupt priority level, controlling which other interrupts + // this timer is allowed to interrupt. Lower numbers are higher priority, + // with 0 the highest and 255 the lowest. Most other interrupts default to 128. + // As a general guideline, interrupt routines that run longer should be given + // lower priority (higher numerical values). + _uclockTimer.priority(80); +} + +void setTimer(uint32_t us_interval) +{ + _uclockTimer.update(us_interval); +} \ No newline at end of file diff --git a/src/uClock.cpp b/src/uClock.cpp index 3eddb64..b03a50d 100755 --- a/src/uClock.cpp +++ b/src/uClock.cpp @@ -2,7 +2,7 @@ * @file uClock.cpp * Project BPM clock generator for Arduino * @brief A Library to implement BPM clock tick calls using hardware interruption. Supported and tested on AVR boards(ATmega168/328, ATmega16u4/32u4 and ATmega2560) and ARM boards(Teensy, Seedstudio XIAO M0 and ESP32) - * @version 1.2.0 + * @version 1.3.0 * @author Romulo Silva * @date 10/06/2017 * @license MIT - (c) 2022 - Romulo Silva - contact@midilab.co @@ -27,109 +27,46 @@ */ #include "uClock.h" -// Timer setup for work clock // // Teensyduino port // #if defined(TEENSYDUINO) - IntervalTimer _uclockTimer; + #include "platforms/teensy.h" #endif // // Seedstudio XIAO M0 port // #if defined(SEEED_XIAO_M0) - // 24 bits timer - #include - // uses TimerTcc0 - // 16 bits timer - //#include - // uses TimerTc3 + #include "platforms/samd.h" #endif // // ESP32 family // #if defined(ARDUINO_ARCH_ESP32) || defined(ESP32) - hw_timer_t * _uclockTimer = NULL; - #define TIMER_ID 0 + #include "platforms/esp32.h" #endif - -// -// multicore archs // -#if defined(ARDUINO_ARCH_ESP32) || defined(ESP32) - portMUX_TYPE _uclockTimerMux = portMUX_INITIALIZER_UNLOCKED; - #define ATOMIC(X) portENTER_CRITICAL_ISR(&_uclockTimerMux); X; portEXIT_CRITICAL_ISR(&_uclockTimerMux); +// STM32XX family? // -// singlecore archs -// -#else - #define ATOMIC(X) noInterrupts(); X; interrupts(); +#if defined(ARDUINO_ARCH_STM32) + #include "platforms/stm32.h" #endif -#if defined(ARDUINO_ARCH_AVR) void uclockInitTimer() { - ATOMIC( - // 16bits Timer1 init - // begin at 120bpm (48.0007680122882 Hz) - TCCR1A = 0; // set entire TCCR1A register to 0 - TCCR1B = 0; // same for TCCR1B - TCNT1 = 0; // initialize counter value to 0 - // set compare match register for 48.0007680122882 Hz increments - OCR1A = 41665; // = 16000000 / (8 * 48.0007680122882) - 1 (must be <65536) - // turn on CTC mode - TCCR1B |= (1 << WGM12); - // Set CS12, CS11 and CS10 bits for 8 prescaler - TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10); - // enable timer compare interrupt - TIMSK1 |= (1 << OCIE1A); - ) + // begin at 120bpm (20833us) + const uint32_t init_clock = 20833; + // called from architecture specific module + initTimer(init_clock); } -#else - // forward declaration of ISR - #if defined(ARDUINO_ARCH_ESP32) || defined(ESP32) - void ARDUINO_ISR_ATTR uclockISR(); - #else - void uclockISR(); - #endif - -void uclockInitTimer() +void setTimerTempo(float bpm) { - // begin at 120bpm (20833us) - const uint16_t init_clock = 20833; - #if defined(TEENSYDUINO) - _uclockTimer.begin(uclockISR, init_clock); - - // Set the interrupt priority level, controlling which other interrupts - // this timer is allowed to interrupt. Lower numbers are higher priority, - // with 0 the highest and 255 the lowest. Most other interrupts default to 128. - // As a general guideline, interrupt routines that run longer should be given - // lower priority (higher numerical values). - _uclockTimer.priority(0); - #endif - - #if defined(SEEED_XIAO_M0) - TimerTcc0.initialize(init_clock); - - // attach to generic uclock ISR - TimerTcc0.attachInterrupt(uclockISR); - #endif - - #if defined(ARDUINO_ARCH_ESP32) || defined(ESP32) - _uclockTimer = timerBegin(TIMER_ID, 80, true); - - // attach to generic uclock ISR - timerAttachInterrupt(_uclockTimer, &uclockISR, true); - - // init clock tick time - timerAlarmWrite(_uclockTimer, init_clock, true); - - // activate it! - timerAlarmEnable(_uclockTimer); - #endif + // convert bpm float into 96 ppqn resolution microseconds interval + uint32_t tick_us_interval = (60000000 / 24 / bpm); + // called from architecture specific module + setTimer(tick_us_interval); } -#endif namespace umodular { namespace clock { @@ -208,59 +145,6 @@ void uClockClass::pause() } } -void uClockClass::setTimerTempo(float bpm) -{ - // 96 ppqn resolution - uint32_t tick_us_interval = (60000000 / 24 / bpm); - -#if defined(ARDUINO_ARCH_AVR) - float tick_hertz_interval = 1/((float)tick_us_interval/1000000); - - uint32_t ocr; - uint8_t tccr = 0; - - // 16bits avr timer setup - if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 1 )) < 65535) { - // Set CS12, CS11 and CS10 bits for 1 prescaler - tccr |= (0 << CS12) | (0 << CS11) | (1 << CS10); - } else if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 8 )) < 65535) { - // Set CS12, CS11 and CS10 bits for 8 prescaler - tccr |= (0 << CS12) | (1 << CS11) | (0 << CS10); - } else if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 64 )) < 65535) { - // Set CS12, CS11 and CS10 bits for 64 prescaler - tccr |= (0 << CS12) | (1 << CS11) | (1 << CS10); - } else if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 256 )) < 65535) { - // Set CS12, CS11 and CS10 bits for 256 prescaler - tccr |= (1 << CS12) | (0 << CS11) | (0 << CS10); - } else if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 1024 )) < 65535) { - // Set CS12, CS11 and CS10 bits for 1024 prescaler - tccr |= (1 << CS12) | (0 << CS11) | (1 << CS10); - } else { - // tempo not achiavable - return; - } - - ATOMIC( - TCCR1B = 0; - OCR1A = ocr-1; - TCCR1B |= (1 << WGM12); - TCCR1B |= tccr; - ) -#else - #if defined(TEENSYDUINO) - _uclockTimer.update(tick_us_interval); - #endif - - #if defined(SEEED_XIAO_M0) - TimerTcc0.setPeriod(tick_us_interval); - #endif - - #if defined(ARDUINO_ARCH_ESP32) || defined(ESP32) - timerAlarmWrite(_uclockTimer, tick_us_interval, true); - #endif -#endif -} - void uClockClass::setTempo(float bpm) { if (mode == EXTERNAL_CLOCK) { diff --git a/src/uClock.h b/src/uClock.h index 09b7a74..ad717fe 100755 --- a/src/uClock.h +++ b/src/uClock.h @@ -43,10 +43,6 @@ namespace umodular { namespace clock { #define MIN_BPM 1 #define MAX_BPM 300 -// want a different avr clock support? -// TODO: we should do this using macro guards for avrs different clocks -#define AVR_CLOCK_FREQ 16000000 - #define PHASE_FACTOR 16 #define PLL_X 220 @@ -58,7 +54,6 @@ class uClockClass { private: - void setTimerTempo(float bpm); float inline freqToBpm(uint32_t freq); void (*onClock96PPQNCallback)(uint32_t tick); From 3a1e52589e38f59d863d9b28953a5db990c392a7 Mon Sep 17 00:00:00 2001 From: midilab Date: Tue, 9 May 2023 05:24:50 -0400 Subject: [PATCH 2/7] fix missing AVR platform include --- src/uClock.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/uClock.cpp b/src/uClock.cpp index b03a50d..a6f8c8a 100755 --- a/src/uClock.cpp +++ b/src/uClock.cpp @@ -27,6 +27,12 @@ */ #include "uClock.h" +// +// General Arduino AVRs port +// +#if defined(ARDUINO_ARCH_AVR) + #include "platforms/avr.h" +#endif // // Teensyduino port // From 841a6ee68d5883cc47f4a32d5c5740aa24ee7de2 Mon Sep 17 00:00:00 2001 From: midilab Date: Tue, 9 May 2023 05:42:58 -0400 Subject: [PATCH 3/7] implmemtation review --- src/platforms/avr.h | 2 +- src/platforms/stm32.h | 38 ++++++++++++++++---------------------- src/uClock.cpp | 4 ++-- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/platforms/avr.h b/src/platforms/avr.h index c96db5f..0a8dac9 100644 --- a/src/platforms/avr.h +++ b/src/platforms/avr.h @@ -3,7 +3,7 @@ #define ATOMIC(X) noInterrupts(); X; interrupts(); // want a different avr clock support? -// TODO: we should do this using macro guards for avrs different clocks +// TODO: we should do this using macro guards for avrs different clocks freqeuncy setup at compile time #define AVR_CLOCK_FREQ 16000000 void initTimer(uint32_t init_clock) diff --git a/src/platforms/stm32.h b/src/platforms/stm32.h index 45436fe..5464935 100644 --- a/src/platforms/stm32.h +++ b/src/platforms/stm32.h @@ -1,16 +1,13 @@ #include -static TIM_HandleTypeDef s_TimerInstance = {0}; - -typedef void (*timer_callback_t)(void); -timer_callback_t timer_callback = NULL; +static TIM_HandleTypeDef _uclockTimer = {0}; #define ATOMIC(X) noInterrupts(); X; interrupts(); // forward declaration of ISR void uclockISR(); -void timer_attachInterrupt(TIM_TypeDef *tim, uint32_t microseconds, timer_callback_t callback) +void timer_attachInterrupt(TIM_TypeDef *tim, uint32_t us_interval) { // Enable timer clock if (tim == TIM2) __HAL_RCC_TIM2_CLK_ENABLE(); @@ -21,36 +18,33 @@ void timer_attachInterrupt(TIM_TypeDef *tim, uint32_t microseconds, timer_callba uint32_t prescaler = (SystemCoreClock / 1000000UL) - 1; // Calculate the period value - uint32_t period = (microseconds * 2UL) - 1UL; + uint32_t period = (us_interval * 2UL) - 1UL; // Set up the timer instance - s_TimerInstance.Instance = tim; - s_TimerInstance.Init.Prescaler = prescaler; - s_TimerInstance.Init.CounterMode = TIM_COUNTERMODE_UP; - s_TimerInstance.Init.Period = period; - s_TimerInstance.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + _uclockTimer.Instance = tim; + _uclockTimer.Init.Prescaler = prescaler; + _uclockTimer.Init.CounterMode = TIM_COUNTERMODE_UP; + _uclockTimer.Init.Period = period; + _uclockTimer.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // Configure the timer instance - HAL_TIM_Base_Init(&s_TimerInstance); - HAL_TIM_Base_Start_IT(&s_TimerInstance); - - // Save the callback function - timer_callback = callback; + HAL_TIM_Base_Init(&_uclockTimer); + HAL_TIM_Base_Start_IT(&_uclockTimer); } void TIM2_IRQHandler() { - // Call the callback function - timer_callback(); + // Call the uClock ISR handler + uclockISR(); // Clear the interrupt flag - __HAL_TIM_CLEAR_FLAG(&s_TimerInstance, TIM_FLAG_UPDATE); + __HAL_TIM_CLEAR_FLAG(&_uclockTimer, TIM_FLAG_UPDATE); } void initTimer(uint32_t us_interval) { // Set up the timer to call the callback function every us_interval microseconds - timer_attachInterrupt(TIM2, us_interval, uclockISR); + timer_attachInterrupt(TIM2, us_interval); } void setTimer(uint32_t us_interval) @@ -59,7 +53,7 @@ void setTimer(uint32_t us_interval) uint32_t period = (us_interval * 2UL) - 1UL; // Update the timer instance with the new period value - s_TimerInstance.Init.Period = period; - HAL_TIM_Base_Init(&s_TimerInstance); + _uclockTimer.Init.Period = period; + HAL_TIM_Base_Init(&_uclockTimer); } diff --git a/src/uClock.cpp b/src/uClock.cpp index a6f8c8a..eb6ac75 100755 --- a/src/uClock.cpp +++ b/src/uClock.cpp @@ -34,7 +34,7 @@ #include "platforms/avr.h" #endif // -// Teensyduino port +// Teensyduino ARMs port // #if defined(TEENSYDUINO) #include "platforms/teensy.h" @@ -52,7 +52,7 @@ #include "platforms/esp32.h" #endif // -// STM32XX family? +// STM32XX family // #if defined(ARDUINO_ARCH_STM32) #include "platforms/stm32.h" From 8904afff61207f62578d2b1aa5d9dd25727ec4e4 Mon Sep 17 00:00:00 2001 From: midilab Date: Sat, 13 May 2023 05:16:03 -0400 Subject: [PATCH 4/7] change implmementation from HAL to SMT32Duino official examples for timer usage --- .../STM32UartMasterMidiClock.ino | 8 +-- src/platforms/stm32.h | 60 ++++++------------- 2 files changed, 21 insertions(+), 47 deletions(-) diff --git a/examples/STM32UartMasterMidiClock/STM32UartMasterMidiClock.ino b/examples/STM32UartMasterMidiClock/STM32UartMasterMidiClock.ino index 18ad367..6c8a03f 100644 --- a/examples/STM32UartMasterMidiClock/STM32UartMasterMidiClock.ino +++ b/examples/STM32UartMasterMidiClock/STM32UartMasterMidiClock.ino @@ -36,21 +36,21 @@ void handle_bpm_led(uint32_t tick) // Internal clock handlers void ClockOut96PPQN(uint32_t tick) { // Send MIDI_CLOCK to external gears - Serial.write(MIDI_CLOCK); + //Serial.write(MIDI_CLOCK); handle_bpm_led(tick); } void onClockStart() { - Serial.write(MIDI_START); + //Serial.write(MIDI_START); } void onClockStop() { - Serial.write(MIDI_STOP); + //Serial.write(MIDI_STOP); } void setup() { // Initialize serial communication at 31250 bits per second, the default MIDI serial speed communication: - Serial.begin(31250); + //Serial.begin(31250); // A led to count bpms pinMode(LED_BUILTIN, OUTPUT); diff --git a/src/platforms/stm32.h b/src/platforms/stm32.h index 5464935..857bfbe 100644 --- a/src/platforms/stm32.h +++ b/src/platforms/stm32.h @@ -1,59 +1,33 @@ #include -static TIM_HandleTypeDef _uclockTimer = {0}; +#if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION < 0x01090000) + #error "Due to API change, this library is compatible with STM32_CORE_VERSION >= 0x01090000. Please update your stm32duino core." +#endif + +#if defined(TIM1) + TIM_TypeDef * TimerInstance = TIM1; +#else + TIM_TypeDef * TimerInstance = TIM2; +#endif + +// Instantiate HardwareTimer object. Thanks to 'new' instanciation, HardwareTimer is not destructed when setup() function is finished. +HardwareTimer * _uclockTimer = new HardwareTimer(TimerInstance); #define ATOMIC(X) noInterrupts(); X; interrupts(); // forward declaration of ISR void uclockISR(); -void timer_attachInterrupt(TIM_TypeDef *tim, uint32_t us_interval) -{ - // Enable timer clock - if (tim == TIM2) __HAL_RCC_TIM2_CLK_ENABLE(); - else if (tim == TIM3) __HAL_RCC_TIM3_CLK_ENABLE(); - else if (tim == TIM4) __HAL_RCC_TIM4_CLK_ENABLE(); - - // Calculate the prescaler value - uint32_t prescaler = (SystemCoreClock / 1000000UL) - 1; - - // Calculate the period value - uint32_t period = (us_interval * 2UL) - 1UL; - - // Set up the timer instance - _uclockTimer.Instance = tim; - _uclockTimer.Init.Prescaler = prescaler; - _uclockTimer.Init.CounterMode = TIM_COUNTERMODE_UP; - _uclockTimer.Init.Period = period; - _uclockTimer.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; - - // Configure the timer instance - HAL_TIM_Base_Init(&_uclockTimer); - HAL_TIM_Base_Start_IT(&_uclockTimer); -} - -void TIM2_IRQHandler() -{ - // Call the uClock ISR handler - uclockISR(); - - // Clear the interrupt flag - __HAL_TIM_CLEAR_FLAG(&_uclockTimer, TIM_FLAG_UPDATE); -} - void initTimer(uint32_t us_interval) { - // Set up the timer to call the callback function every us_interval microseconds - timer_attachInterrupt(TIM2, us_interval); + _uclockTimer->setOverflow(us_interval, MICROSEC_FORMAT); + _uclockTimer->attachInterrupt(uclockISR); + _uclockTimer->resume(); } void setTimer(uint32_t us_interval) { - // Calculate the period value - uint32_t period = (us_interval * 2UL) - 1UL; - - // Update the timer instance with the new period value - _uclockTimer.Init.Period = period; - HAL_TIM_Base_Init(&_uclockTimer); + _uclockTimer->setOverflow(us_interval, MICROSEC_FORMAT); + _uclockTimer->refresh(); } From a15a07012881362ffa1f8c097f861b4e26ff759c Mon Sep 17 00:00:00 2001 From: Jackson-Devices <123213412+Jackson-Devices@users.noreply.github.com> Date: Mon, 15 May 2023 02:12:07 +0100 Subject: [PATCH 5/7] Update STM32UartMasterMidiClock.ino Working MIDI out tested on Nucleo-F401RE and Nucleo-F072RB boards --- .../STM32UartMasterMidiClock.ino | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/examples/STM32UartMasterMidiClock/STM32UartMasterMidiClock.ino b/examples/STM32UartMasterMidiClock/STM32UartMasterMidiClock.ino index 6c8a03f..969e403 100644 --- a/examples/STM32UartMasterMidiClock/STM32UartMasterMidiClock.ino +++ b/examples/STM32UartMasterMidiClock/STM32UartMasterMidiClock.ino @@ -1,12 +1,19 @@ -/* Uart MIDI Sync Box +/* Uart MIDI out * * This example demonstrates how to send MIDI data via Uart * interface on STM32 family. * * This example code is in the public domain. + * + * Requires STM32Duino board manager to be installed. * - * ... - * + * Define HardwareSerial using any available UART/USART. + * Nucleo boards have UART/USART pins that are used by the ST-LINK interface (unless using solder bridging). + * + * Tested on Nucleo-F401RE and Nucleo-F072RB (PA9=D8 PA10=D2 on the Arduino pins) + * + * Code by midilab contact@midilab.co + * Example modified by Jackson Devices contact@jacksondevices.com */ #include @@ -15,8 +22,7 @@ #define MIDI_START 0xFA #define MIDI_STOP 0xFC -// the blue led -#define LED_BUILTIN 2 +HardwareSerial Serial1(PA10, PA9); uint8_t bpm_blink_timer = 1; void handle_bpm_led(uint32_t tick) @@ -35,24 +41,26 @@ void handle_bpm_led(uint32_t tick) // Internal clock handlers void ClockOut96PPQN(uint32_t tick) { - // Send MIDI_CLOCK to external gears - //Serial.write(MIDI_CLOCK); + // Send MIDI_CLOCK to external gear + Serial1.write(MIDI_CLOCK); handle_bpm_led(tick); } void onClockStart() { - //Serial.write(MIDI_START); + // Send MIDI_START to external gear + Serial1.write(MIDI_START); } void onClockStop() { - //Serial.write(MIDI_STOP); + // Send MIDI_STOP to external gear + Serial1.write(MIDI_STOP); } void setup() { - // Initialize serial communication at 31250 bits per second, the default MIDI serial speed communication: - //Serial.begin(31250); + // Initialize Serial1 communication at 31250 bits per second, the default MIDI Serial1 speed communication: + Serial1.begin(31250); - // A led to count bpms + // An led to display BPM pinMode(LED_BUILTIN, OUTPUT); // Setup our clock system @@ -64,7 +72,7 @@ void setup() { uClock.setOnClockStartOutput(onClockStart); uClock.setOnClockStopOutput(onClockStop); // Set the clock BPM to 126 BPM - uClock.setTempo(126); + uClock.setTempo(120); // Starts the clock, tick-tac-tick-tac... uClock.start(); } From 69b9467cc19427083fa1bebb1f86c20b0a2763ca Mon Sep 17 00:00:00 2001 From: midilab Date: Mon, 15 May 2023 06:37:58 -0400 Subject: [PATCH 6/7] comment notes updates --- src/platforms/stm32.h | 4 ++-- src/uClock.cpp | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/platforms/stm32.h b/src/platforms/stm32.h index 857bfbe..5d4b906 100644 --- a/src/platforms/stm32.h +++ b/src/platforms/stm32.h @@ -1,7 +1,7 @@ #include #if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION < 0x01090000) - #error "Due to API change, this library is compatible with STM32_CORE_VERSION >= 0x01090000. Please update your stm32duino core." + #error "Due to API change, this library is compatible with STM32_CORE_VERSION >= 0x01090000. Please update/install stm32duino core." #endif #if defined(TIM1) @@ -10,7 +10,7 @@ TIM_TypeDef * TimerInstance = TIM2; #endif -// Instantiate HardwareTimer object. Thanks to 'new' instanciation, HardwareTimer is not destructed when setup() function is finished. +// instantiate HardwareTimer object HardwareTimer * _uclockTimer = new HardwareTimer(TimerInstance); #define ATOMIC(X) noInterrupts(); X; interrupts(); diff --git a/src/uClock.cpp b/src/uClock.cpp index eb6ac75..f204a4f 100755 --- a/src/uClock.cpp +++ b/src/uClock.cpp @@ -58,20 +58,23 @@ #include "platforms/stm32.h" #endif +// +// Platform specific timer setup/control +// +// initTimer(uint32_t us_interval) and setTimer(uint32_t us_interval) +// are called from architecture specific module included at the +// header of this file void uclockInitTimer() { // begin at 120bpm (20833us) - const uint32_t init_clock = 20833; - // called from architecture specific module - initTimer(init_clock); + initTimer(20833); } void setTimerTempo(float bpm) { // convert bpm float into 96 ppqn resolution microseconds interval - uint32_t tick_us_interval = (60000000 / 24 / bpm); - // called from architecture specific module - setTimer(tick_us_interval); + uint32_t us_interval = (60000000 / 24 / bpm); + setTimer(us_interval); } namespace umodular { namespace clock { From ff9c6a93fa648d102c395c373d7ec88c906d6fcd Mon Sep 17 00:00:00 2001 From: midilab Date: Mon, 15 May 2023 07:02:47 -0400 Subject: [PATCH 7/7] fix missing name.c for usb-midi definitions on teensy arm platforms --- examples/TeensyUsbMasterMidiClock/name.c | 19 +++++++++++++++++++ examples/TeensyUsbSlaveMidiClock/name.c | 19 +++++++++++++++++++ .../TeensyUsbSlaveMidiClockMonitor/name.c | 19 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 examples/TeensyUsbMasterMidiClock/name.c create mode 100644 examples/TeensyUsbSlaveMidiClock/name.c create mode 100644 examples/TeensyUsbSlaveMidiClockMonitor/name.c diff --git a/examples/TeensyUsbMasterMidiClock/name.c b/examples/TeensyUsbMasterMidiClock/name.c new file mode 100644 index 0000000..0e26fb3 --- /dev/null +++ b/examples/TeensyUsbMasterMidiClock/name.c @@ -0,0 +1,19 @@ +// To give your project a unique name, this code must be +// placed into a .c file (its own tab). It can not be in +// a .cpp file or your main sketch (the .ino file). + +#include "usb_names.h" + +// Edit these lines to create your own name. The length must +// match the number of characters in your custom name. + +#define MIDI_NAME {'u','c','l','o','c','k','_','1'} +#define MIDI_NAME_LEN 8 + +// Do not change this part. This exact format is required by USB. + +struct usb_string_descriptor_struct usb_string_product_name = { + 2 + MIDI_NAME_LEN * 2, + 3, + MIDI_NAME +}; diff --git a/examples/TeensyUsbSlaveMidiClock/name.c b/examples/TeensyUsbSlaveMidiClock/name.c new file mode 100644 index 0000000..0e26fb3 --- /dev/null +++ b/examples/TeensyUsbSlaveMidiClock/name.c @@ -0,0 +1,19 @@ +// To give your project a unique name, this code must be +// placed into a .c file (its own tab). It can not be in +// a .cpp file or your main sketch (the .ino file). + +#include "usb_names.h" + +// Edit these lines to create your own name. The length must +// match the number of characters in your custom name. + +#define MIDI_NAME {'u','c','l','o','c','k','_','1'} +#define MIDI_NAME_LEN 8 + +// Do not change this part. This exact format is required by USB. + +struct usb_string_descriptor_struct usb_string_product_name = { + 2 + MIDI_NAME_LEN * 2, + 3, + MIDI_NAME +}; diff --git a/examples/TeensyUsbSlaveMidiClockMonitor/name.c b/examples/TeensyUsbSlaveMidiClockMonitor/name.c new file mode 100644 index 0000000..0e26fb3 --- /dev/null +++ b/examples/TeensyUsbSlaveMidiClockMonitor/name.c @@ -0,0 +1,19 @@ +// To give your project a unique name, this code must be +// placed into a .c file (its own tab). It can not be in +// a .cpp file or your main sketch (the .ino file). + +#include "usb_names.h" + +// Edit these lines to create your own name. The length must +// match the number of characters in your custom name. + +#define MIDI_NAME {'u','c','l','o','c','k','_','1'} +#define MIDI_NAME_LEN 8 + +// Do not change this part. This exact format is required by USB. + +struct usb_string_descriptor_struct usb_string_product_name = { + 2 + MIDI_NAME_LEN * 2, + 3, + MIDI_NAME +};