From 53419fa89cb311dc13ef0e5d7717e91923cde5a4 Mon Sep 17 00:00:00 2001 From: doctea Date: Wed, 31 Jan 2024 20:59:16 +0000 Subject: [PATCH 01/10] initial working proof-of-concept of rp2040 support --- .../RP2040ClockBlink/RP2040ClockBlink.ino | 92 ++++++++++++++++++ .../RP2040UsbMasterMidiClock.ino | 96 +++++++++++++++++++ src/platforms/esp32.h | 1 + src/platforms/rp2040.h | 57 +++++++++++ src/uClock.cpp | 6 ++ 5 files changed, 252 insertions(+) create mode 100644 examples/RP2040ClockBlink/RP2040ClockBlink.ino create mode 100644 examples/RP2040UsbMasterMidiClock/RP2040UsbMasterMidiClock.ino create mode 100644 src/platforms/rp2040.h diff --git a/examples/RP2040ClockBlink/RP2040ClockBlink.ino b/examples/RP2040ClockBlink/RP2040ClockBlink.ino new file mode 100644 index 0000000..2a6dc0c --- /dev/null +++ b/examples/RP2040ClockBlink/RP2040ClockBlink.ino @@ -0,0 +1,92 @@ +/* USB MIDI Sync Box - RP2040 example that just blinks LED + * + * + * This example code is in the public domain. + * + */ + +//#define LED_BUILTIN PIN_LED_B + +#include "Adafruit_TinyUSB.h" + +#include + +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, LOW); + } else if ( !(tick % (24)) ) { // each quarter led on + bpm_blink_timer = 1; + digitalWrite(LED_BUILTIN, LOW); + } else if ( !(tick % bpm_blink_timer) ) { // get led off + digitalWrite(LED_BUILTIN, HIGH); + } +} + + +// Internal clock handlers +void onSync24Callback(uint32_t tick) { + handle_bpm_led(tick); +} + +void onClockStart() { + //MIDI_USB.sendRealTime(midi::Start); +} + +void onClockStop() { + //MIDI_USB.sendRealTime(midi::Stop); +} + +void setup() { +/*#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) + // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 + TinyUSB_Device_Init(0); +#endif*/ + + //MIDI_USB.begin(MIDI_CHANNEL_OMNI); + + // A led to count bpms + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + delay(500); + digitalWrite(LED_BUILTIN, LOW); + delay(500); + digitalWrite(LED_BUILTIN, HIGH); + delay(500); + digitalWrite(LED_BUILTIN, LOW); + delay(500); + + + Serial.begin(115200); + /*while (!Serial) + delay(1);*/ + + // Setup our clock system + + // Inits the clock + uClock.init(); + // Set the callback function for the clock output to send MIDI Sync message. + uClock.setOnSync24(onSync24Callback); + // Set the callback function for MIDI Start and Stop messages. + uClock.setOnClockStart(onClockStart); + uClock.setOnClockStop(onClockStop); + // Set the clock BPM to 126 BPM + uClock.setTempo(60); + // Starts the clock, tick-tac-tick-tac.. + //Serial.println("about to uClock.start()..."); Serial.flush(); + uClock.start(); + //Serial.println("uClock.start()ed!"); Serial.flush(); +} + +uint32_t count = 0; + +// Do it whatever to interface with Clock.stop(), Clock.start(), Clock.setTempo() and integrate your environment... +void loop() { + //MIDI_USB.read(); + //count++; + //if (millis()%1000==0) + // Serial.println("looped!"); +} diff --git a/examples/RP2040UsbMasterMidiClock/RP2040UsbMasterMidiClock.ino b/examples/RP2040UsbMasterMidiClock/RP2040UsbMasterMidiClock.ino new file mode 100644 index 0000000..cf1092a --- /dev/null +++ b/examples/RP2040UsbMasterMidiClock/RP2040UsbMasterMidiClock.ino @@ -0,0 +1,96 @@ +/* USB MIDI Sync Box - example that also prints to serial and sends USB midi as well as blinking LED + * (usb part still needs testing to make sure it works!) + * + * This example code is in the public domain. + * + */ +#include +#include + +Adafruit_USBD_MIDI usb_midi; +MIDI_CREATE_INSTANCE(Adafruit_USBD_MIDI, usb_midi, MIDI_USB); + +//#define LED_BUILTIN PIN_LED_B + +//MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI); +#include + +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, LOW); + } else if ( !(tick % (24)) ) { // each quarter led on + bpm_blink_timer = 1; + digitalWrite(LED_BUILTIN, LOW); + } else if ( !(tick % bpm_blink_timer) ) { // get led off + digitalWrite(LED_BUILTIN, HIGH); + } +} + + +// Internal clock handlers +void onSync24Callback(uint32_t tick) { + // Send MIDI_CLOCK to external gears + MIDI_USB.sendRealTime(midi::Clock); + handle_bpm_led(tick); +} + +void onClockStart() { + MIDI_USB.sendRealTime(midi::Start); +} + +void onClockStop() { + MIDI_USB.sendRealTime(midi::Stop); +} + +void setup() { +#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) + // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 + TinyUSB_Device_Init(0); +#endif + + MIDI_USB.begin(MIDI_CHANNEL_OMNI); + + // A led to count bpms + pinMode(LED_BUILTIN, OUTPUT); + + Serial.begin(115200); + while (!Serial) + delay(1); + + // wait until device mounted + /*while( !TinyUSBDevice.mounted() ) { + Serial.println("waiting for usb.."); + Serial.flush(); + delay(1); + }*/ + + // Setup our clock system + // Inits the clock + Serial.println("about to uClock.init()..."); Serial.flush(); + uClock.init(); + // Set the callback function for the clock output to send MIDI Sync message. + uClock.setOnSync24(onSync24Callback); + // Set the callback function for MIDI Start and Stop messages. + uClock.setOnClockStart(onClockStart); + uClock.setOnClockStop(onClockStop); + // Set the clock BPM to 126 BPM + uClock.setTempo(126); + // Starts the clock, tick-tac-tick-tac.. + Serial.println("about to uClock.start()..."); Serial.flush(); + uClock.start(); + Serial.println("uClock.start()ed!"); Serial.flush(); +} + +uint32_t count = 0; + +// Do it whatever to interface with Clock.stop(), Clock.start(), Clock.setTempo() and integrate your environment... +void loop() { + MIDI_USB.read(); + count++; + if (millis()%1000==0) + Serial.println("looped!!!"); +} diff --git a/src/platforms/esp32.h b/src/platforms/esp32.h index 1344923..22e80b0 100644 --- a/src/platforms/esp32.h +++ b/src/platforms/esp32.h @@ -2,6 +2,7 @@ #include #include +// esp32-specific timer #define TIMER_ID 0 hw_timer_t * _uclockTimer = NULL; // mutex control for ISR diff --git a/src/platforms/rp2040.h b/src/platforms/rp2040.h new file mode 100644 index 0000000..1c5a37d --- /dev/null +++ b/src/platforms/rp2040.h @@ -0,0 +1,57 @@ +#include +#include "FreeRTOS.h" +#include +#include +#include "pico/sync.h" + +// RPi-specific timer +struct repeating_timer timer; + +// FreeRTOS main clock task size in bytes +#define CLOCK_STACK_SIZE 5*1024 // adjust for your needs, a sequencer with heavy serial handling should be large in size +TaskHandle_t taskHandle; +// mutex to protect the shared resource +SemaphoreHandle_t _mutex; +// mutex control for task +#define ATOMIC(X) xSemaphoreTake(_mutex, portMAX_DELAY); X; xSemaphoreGive(_mutex); + +// forward declaration of uClockHandler +void uClockHandler(); + +// ISR handler -- called when tick happens +bool handlerISR(repeating_timer *timer) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + // Send a notification to task1 + vTaskNotifyGiveFromISR(taskHandle, &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + + return true; +} + +// task for user clock process +void clockTask(void *pvParameters) +{ + while (1) { + // wait for a notification from ISR + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + uClockHandler(); + } +} + +void initTimer(uint32_t init_clock) +{ + // initialize the mutex for shared resource access + _mutex = xSemaphoreCreateMutex(); + + // create the clockTask + xTaskCreate(clockTask, "clockTask", CLOCK_STACK_SIZE, NULL, 1, &taskHandle); + + // set up RPi interrupt timer + add_repeating_timer_us(init_clock, &handlerISR, NULL, &timer); +} + +void setTimer(uint32_t us_interval) { + cancel_repeating_timer(&timer); + add_repeating_timer_us(us_interval, &handlerISR, NULL, &timer); +} \ No newline at end of file diff --git a/src/uClock.cpp b/src/uClock.cpp index 3ad2473..18c6883 100755 --- a/src/uClock.cpp +++ b/src/uClock.cpp @@ -57,6 +57,12 @@ #if defined(ARDUINO_ARCH_STM32) #include "platforms/stm32.h" #endif +// +// RP2040 (Raspberry Pico) family +// +#if defined(ARDUINO_ARCH_RP2040) + #include "platforms/rp2040.h" +#endif // // Platform specific timer setup/control From 99d18452016981fa8c8993ca6dca468705782325 Mon Sep 17 00:00:00 2001 From: doctea Date: Thu, 1 Feb 2024 23:13:40 +0000 Subject: [PATCH 02/10] use interrupts instead of FreeRTOS tasks -- seems to work with both cores? --- .../RP2040ClockBlink/RP2040ClockBlink.ino | 8 +- .../RP2040UsbMasterMidiClock.ino | 34 ++++- src/platforms/rp2040.h | 137 ++++++++++++------ 3 files changed, 120 insertions(+), 59 deletions(-) diff --git a/examples/RP2040ClockBlink/RP2040ClockBlink.ino b/examples/RP2040ClockBlink/RP2040ClockBlink.ino index 2a6dc0c..63c0650 100644 --- a/examples/RP2040ClockBlink/RP2040ClockBlink.ino +++ b/examples/RP2040ClockBlink/RP2040ClockBlink.ino @@ -11,6 +11,8 @@ #include +#define ATOMIC(X) { uint32_t __interrupt_mask = save_and_disable_interrupts(); X; restore_interrupts(__interrupt_mask); } + uint8_t bpm_blink_timer = 1; void handle_bpm_led(uint32_t tick) { @@ -50,14 +52,14 @@ void setup() { // A led to count bpms pinMode(LED_BUILTIN, OUTPUT); - digitalWrite(LED_BUILTIN, HIGH); + /*digitalWrite(LED_BUILTIN, HIGH); delay(500); digitalWrite(LED_BUILTIN, LOW); delay(500); digitalWrite(LED_BUILTIN, HIGH); delay(500); digitalWrite(LED_BUILTIN, LOW); - delay(500); + delay(500);*/ Serial.begin(115200); @@ -88,5 +90,5 @@ void loop() { //MIDI_USB.read(); //count++; //if (millis()%1000==0) - // Serial.println("looped!"); + // ATOMIC(Serial.println("looped!")); } diff --git a/examples/RP2040UsbMasterMidiClock/RP2040UsbMasterMidiClock.ino b/examples/RP2040UsbMasterMidiClock/RP2040UsbMasterMidiClock.ino index cf1092a..f3445c0 100644 --- a/examples/RP2040UsbMasterMidiClock/RP2040UsbMasterMidiClock.ino +++ b/examples/RP2040UsbMasterMidiClock/RP2040UsbMasterMidiClock.ino @@ -9,11 +9,16 @@ Adafruit_USBD_MIDI usb_midi; MIDI_CREATE_INSTANCE(Adafruit_USBD_MIDI, usb_midi, MIDI_USB); +//MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI); + +#include +#define ATOMIC(X) { uint32_t __interrupt_mask = save_and_disable_interrupts(); X; restore_interrupts(__interrupt_mask); } //#define LED_BUILTIN PIN_LED_B +//#define WAIT_FOR_SERIAL +#define ENABLE_MULTICORE -//MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI); -#include +volatile uint32_t count = 0; uint8_t bpm_blink_timer = 1; void handle_bpm_led(uint32_t tick) @@ -36,6 +41,8 @@ void onSync24Callback(uint32_t tick) { // Send MIDI_CLOCK to external gears MIDI_USB.sendRealTime(midi::Clock); handle_bpm_led(tick); + + Serial.printf("ticked with %u\n", tick); } void onClockStart() { @@ -46,6 +53,16 @@ void onClockStop() { MIDI_USB.sendRealTime(midi::Stop); } +#ifdef ENABLE_MULTICORE + void setup1() { + } + + void loop1() { + if (count%1000==0) + ATOMIC(Serial.println("loop1()!"); Serial.flush()); + } +#endif + void setup() { #if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 @@ -58,8 +75,10 @@ void setup() { pinMode(LED_BUILTIN, OUTPUT); Serial.begin(115200); - while (!Serial) - delay(1); + #ifdef WAIT_FOR_SERIAL + while (!Serial) + delay(1); + #endif // wait until device mounted /*while( !TinyUSBDevice.mounted() ) { @@ -78,19 +97,18 @@ void setup() { uClock.setOnClockStart(onClockStart); uClock.setOnClockStop(onClockStop); // Set the clock BPM to 126 BPM - uClock.setTempo(126); + uClock.setTempo(60); // Starts the clock, tick-tac-tick-tac.. Serial.println("about to uClock.start()..."); Serial.flush(); uClock.start(); - Serial.println("uClock.start()ed!"); Serial.flush(); + ATOMIC(Serial.println("uClock.start()ed!"); Serial.flush();) } -uint32_t count = 0; // Do it whatever to interface with Clock.stop(), Clock.start(), Clock.setTempo() and integrate your environment... void loop() { MIDI_USB.read(); count++; if (millis()%1000==0) - Serial.println("looped!!!"); + ATOMIC(Serial.println("loop()!"); Serial.flush();); } diff --git a/src/platforms/rp2040.h b/src/platforms/rp2040.h index 1c5a37d..af94e12 100644 --- a/src/platforms/rp2040.h +++ b/src/platforms/rp2040.h @@ -1,57 +1,98 @@ #include -#include "FreeRTOS.h" -#include -#include #include "pico/sync.h" -// RPi-specific timer -struct repeating_timer timer; - -// FreeRTOS main clock task size in bytes -#define CLOCK_STACK_SIZE 5*1024 // adjust for your needs, a sequencer with heavy serial handling should be large in size -TaskHandle_t taskHandle; -// mutex to protect the shared resource -SemaphoreHandle_t _mutex; -// mutex control for task -#define ATOMIC(X) xSemaphoreTake(_mutex, portMAX_DELAY); X; xSemaphoreGive(_mutex); - -// forward declaration of uClockHandler -void uClockHandler(); - -// ISR handler -- called when tick happens -bool handlerISR(repeating_timer *timer) -{ - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - // Send a notification to task1 - vTaskNotifyGiveFromISR(taskHandle, &xHigherPriorityTaskWoken); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); - - return true; -} - -// task for user clock process -void clockTask(void *pvParameters) -{ - while (1) { - // wait for a notification from ISR - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); +#define MULTICORE + +#ifdef MULTICORE + // use interrupt version -- works for 2 cores ie can run loop1() and loop() simultaneously as well as the clock callback? + + // RPi-specific timer + struct repeating_timer timer; + + #define ATOMIC(X) { uint32_t __interrupt_mask = save_and_disable_interrupts(); X; restore_interrupts(__interrupt_mask); } + + // forward declaration of uClockHandler + void uClockHandler(); + + // ISR handler -- called when tick happens + bool handlerISR(repeating_timer *timer) + { uClockHandler(); + + return true; + } + + void initTimer(uint32_t init_clock) + { + // set up RPi interrupt timer + // todo: actually should be -init_clock so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick! + add_repeating_timer_us(init_clock, &handlerISR, NULL, &timer); + } + + void setTimer(uint32_t us_interval) { + cancel_repeating_timer(&timer); + // todo: actually should be -init_clock so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick! + add_repeating_timer_us(us_interval, &handlerISR, NULL, &timer); + } +#else + // use FreeRTOS scheduling/mutex version -- doesn't work (task starts but does not run) if using loop1() ie core 2 + + #include "FreeRTOS.h" + #include + #include + + // RPi-specific timer + struct repeating_timer timer; + + // FreeRTOS main clock task size in bytes + #define CLOCK_STACK_SIZE 5*1024 // adjust for your needs, a sequencer with heavy serial handling should be large in size + TaskHandle_t taskHandle; + // mutex to protect the shared resource + SemaphoreHandle_t _mutex; + // mutex control for task + #define ATOMIC(X) xSemaphoreTake(_mutex, portMAX_DELAY); X; xSemaphoreGive(_mutex); + + // forward declaration of uClockHandler + void uClockHandler(); + + // ISR handler -- called when tick happens + bool handlerISR(repeating_timer *timer) + { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + // Send a notification to task1 + vTaskNotifyGiveFromISR(taskHandle, &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + + return true; } -} -void initTimer(uint32_t init_clock) -{ - // initialize the mutex for shared resource access - _mutex = xSemaphoreCreateMutex(); + // task for user clock process + void clockTask(void *pvParameters) + { + while (1) { + // wait for a notification from ISR + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + uClockHandler(); + } + } + + void initTimer(uint32_t init_clock) + { + // initialize the mutex for shared resource access + _mutex = xSemaphoreCreateMutex(); - // create the clockTask - xTaskCreate(clockTask, "clockTask", CLOCK_STACK_SIZE, NULL, 1, &taskHandle); + // create the clockTask + xTaskCreate(clockTask, "clockTask", CLOCK_STACK_SIZE, NULL, 1, &taskHandle); - // set up RPi interrupt timer - add_repeating_timer_us(init_clock, &handlerISR, NULL, &timer); -} + // set up RPi interrupt timer + // todo: actually should be -init_clock so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick! + add_repeating_timer_us(init_clock, &handlerISR, NULL, &timer); + } + + void setTimer(uint32_t us_interval) { + cancel_repeating_timer(&timer); + // todo: actually should be -init_clock so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick! + add_repeating_timer_us(us_interval, &handlerISR, NULL, &timer); + } -void setTimer(uint32_t us_interval) { - cancel_repeating_timer(&timer); - add_repeating_timer_us(us_interval, &handlerISR, NULL, &timer); -} \ No newline at end of file +#endif \ No newline at end of file From fadff608037bd9db3e2bd2f5f32cdf9654cfbc54 Mon Sep 17 00:00:00 2001 From: midilab Date: Sat, 3 Feb 2024 19:49:12 -0300 Subject: [PATCH 03/10] Update RP2040UsbMasterMidiClock.ino remove usb read from loop since its a clock sync only example. --- .../RP2040UsbMasterMidiClock.ino | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/examples/RP2040UsbMasterMidiClock/RP2040UsbMasterMidiClock.ino b/examples/RP2040UsbMasterMidiClock/RP2040UsbMasterMidiClock.ino index cf1092a..c579d76 100644 --- a/examples/RP2040UsbMasterMidiClock/RP2040UsbMasterMidiClock.ino +++ b/examples/RP2040UsbMasterMidiClock/RP2040UsbMasterMidiClock.ino @@ -10,8 +10,6 @@ Adafruit_USBD_MIDI usb_midi; MIDI_CREATE_INSTANCE(Adafruit_USBD_MIDI, usb_midi, MIDI_USB); -//#define LED_BUILTIN PIN_LED_B - //MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI); #include @@ -62,11 +60,11 @@ void setup() { delay(1); // wait until device mounted - /*while( !TinyUSBDevice.mounted() ) { + while( !TinyUSBDevice.mounted() ) { Serial.println("waiting for usb.."); Serial.flush(); delay(1); - }*/ + } // Setup our clock system // Inits the clock @@ -85,12 +83,6 @@ void setup() { Serial.println("uClock.start()ed!"); Serial.flush(); } -uint32_t count = 0; - // Do it whatever to interface with Clock.stop(), Clock.start(), Clock.setTempo() and integrate your environment... void loop() { - MIDI_USB.read(); - count++; - if (millis()%1000==0) - Serial.println("looped!!!"); } From bb68563c55d9af312a9179a0bb422e957e5c4bab Mon Sep 17 00:00:00 2001 From: doctea Date: Sun, 4 Feb 2024 21:42:22 +0000 Subject: [PATCH 04/10] update comment only --- src/platforms/rp2040.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platforms/rp2040.h b/src/platforms/rp2040.h index af94e12..fa6b69e 100644 --- a/src/platforms/rp2040.h +++ b/src/platforms/rp2040.h @@ -31,7 +31,7 @@ void setTimer(uint32_t us_interval) { cancel_repeating_timer(&timer); - // todo: actually should be -init_clock so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick! + // todo: actually should be -us_interval so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick! add_repeating_timer_us(us_interval, &handlerISR, NULL, &timer); } #else @@ -91,7 +91,7 @@ void setTimer(uint32_t us_interval) { cancel_repeating_timer(&timer); - // todo: actually should be -init_clock so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick! + // todo: actually should be -us_interval so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick! add_repeating_timer_us(us_interval, &handlerISR, NULL, &timer); } From 98cb3335f4602760c90d60ef1f889a9abe494175 Mon Sep 17 00:00:00 2001 From: doctea Date: Sun, 4 Feb 2024 21:56:03 +0000 Subject: [PATCH 05/10] updated README to mention RP2040 experimental report and current known problems with it. --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 68115fd..ec05509 100755 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ The **uClock BPM Generator library** is designed to implement precise and reliable BPM clock tick calls using the microcontroller's timer hardware interruption. It is designed to be multi-architecture, portable, and easy to use within the Opensource community universe. -We have chosen PlatformIO and Arduino as our official deployment platforms. The library has been supported and tested on general **AVR boards (ATmega168/328, ATmega16u4/32u4, and ATmega2560)** as well as **ARM boards (Teensy, STM32XX, and Seedstudio XIAO M0)**. +We have chosen PlatformIO and Arduino as our official deployment platforms. The library has been supported and tested on general **AVR boards (ATmega168/328, ATmega16u4/32u4, and ATmega2560)** as well as **ARM boards (Teensy, STM32XX, and Seedstudio XIAO M0)**. It has experimental support for **RP2040 boards (Raspberry Pico, Seeed XIAO RP2040)** (see notes). The absence of real-time features necessary for creating professional-level embedded devices for music and video on Opensource community-based platforms like Arduino led to the development of uClock. By leveraging the use of timer hardware interruptions, the library can schedule and manage real-time-like processing with safe shared resource access through its API. @@ -373,3 +373,11 @@ void loop() //processYourPots(); } ``` + +## Known problems + +### RP2040 support + +- Doing a 'soft reboot' (eg from reflashing) seems to crash on startup, but starting from cold and powering on works fine. +- Using FreeRTOS multithreading fails if the second core is used (via setup1() and loop1()) - using the 'interrupts-based' version of the RP2040 uClock support seems to solve this. +- Tick ticking may be off due to repeating_timer following from the end of previous tick, rather than following the start of the previous tick. From 08e68385caf304d8e3efc4f4f501154882b547e4 Mon Sep 17 00:00:00 2001 From: doctea Date: Sun, 4 Feb 2024 22:55:57 +0000 Subject: [PATCH 06/10] updated readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ec05509..72e322a 100755 --- a/README.md +++ b/README.md @@ -378,6 +378,7 @@ void loop() ### RP2040 support +- Uses the [earlephilhower core](https://github.com/earlephilhower/arduino-pico) - Doing a 'soft reboot' (eg from reflashing) seems to crash on startup, but starting from cold and powering on works fine. - Using FreeRTOS multithreading fails if the second core is used (via setup1() and loop1()) - using the 'interrupts-based' version of the RP2040 uClock support seems to solve this. - Tick ticking may be off due to repeating_timer following from the end of previous tick, rather than following the start of the previous tick. From 6a8451f8d9be02204bb8a61d097c8d02f666eb1c Mon Sep 17 00:00:00 2001 From: doctea Date: Fri, 1 Mar 2024 22:44:08 +0000 Subject: [PATCH 07/10] add rp2040 to library.properties; minor comment --- library.properties | 2 +- src/platforms/rp2040.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library.properties b/library.properties index fa4b2e8..69b5647 100755 --- a/library.properties +++ b/library.properties @@ -6,5 +6,5 @@ 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. ESP32 and STM32) category=Timing url=https://github.com/midilab/uClock -architectures=avr,arm,samd,stm32,esp32 +architectures=avr,arm,samd,stm32,esp32,rp2040 includes=uClock.h diff --git a/src/platforms/rp2040.h b/src/platforms/rp2040.h index fa6b69e..7cf1069 100644 --- a/src/platforms/rp2040.h +++ b/src/platforms/rp2040.h @@ -1,6 +1,7 @@ #include #include "pico/sync.h" +// todo: make this a build flag, so user can choose which method to use? #define MULTICORE #ifdef MULTICORE @@ -22,8 +23,7 @@ return true; } - void initTimer(uint32_t init_clock) - { + void initTimer(uint32_t init_clock) { // set up RPi interrupt timer // todo: actually should be -init_clock so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick! add_repeating_timer_us(init_clock, &handlerISR, NULL, &timer); From e41fb3157a9d9b0fe13f1f86f6ba9fed06592d00 Mon Sep 17 00:00:00 2001 From: midilab Date: Thu, 11 Apr 2024 11:10:05 -0300 Subject: [PATCH 08/10] update version to 2.1.0: rpi2040 support --- library.json | 6 +++--- library.properties | 4 ++-- src/uClock.cpp | 4 ++-- src/uClock.h | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/library.json b/library.json index e64bcf2..1952300 100644 --- a/library.json +++ b/library.json @@ -1,7 +1,7 @@ { "name": "uClock", - "version": "2.0.0", - "description": "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)", + "version": "2.1.0", + "description": "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(RPI2040, Teensy, Seedstudio XIAO M0 and ESP32)", "keywords": "bpm, clock, timing, tick, music, generator", "repository": { @@ -22,5 +22,5 @@ "headers": "uClock.h", "dependencies": {}, "frameworks": "Arduino", - "platforms": "atmelavr,atmelmegaavr,espressif32,ststm32,teensy,atmelsam" + "platforms": "atmelavr,atmelmegaavr,espressif32,ststm32,teensy,atmelsam,raspberrypi" } diff --git a/library.properties b/library.properties index 69b5647..c03f701 100755 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=uClock -version=2.0.0 +version=2.1.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. ESP32 and STM32) +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(RPI2040, Teensy, Seedstudio XIAO M0 and ESP32) category=Timing url=https://github.com/midilab/uClock architectures=avr,arm,samd,stm32,esp32,rp2040 diff --git a/src/uClock.cpp b/src/uClock.cpp index 18c6883..8196f49 100755 --- a/src/uClock.cpp +++ b/src/uClock.cpp @@ -1,8 +1,8 @@ /*! * @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 2.0.0 + * @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(RPI2040, Teensy, Seedstudio XIAO M0 and ESP32) + * @version 2.1.0 * @author Romulo Silva * @date 10/06/2017 * @license MIT - (c) 2024 - Romulo Silva - contact@midilab.co diff --git a/src/uClock.h b/src/uClock.h index 722b385..9b438c2 100755 --- a/src/uClock.h +++ b/src/uClock.h @@ -1,8 +1,8 @@ /*! * @file uClock.h * 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 2.0.0 + * @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(RPI2040, Teensy, Seedstudio XIAO M0 and ESP32) + * @version 2.1.0 * @author Romulo Silva * @date 10/06/2017 * @license MIT - (c) 2024 - Romulo Silva - contact@midilab.co From 9d8865b024ac83abe7652292d7a4205caa0f449c Mon Sep 17 00:00:00 2001 From: midilab Date: Thu, 11 Apr 2024 11:10:47 -0300 Subject: [PATCH 09/10] remove freertos support(unstable so far) and make interrupted version the default only. update main example --- .../RP2040ClockBlink/RP2040ClockBlink.ino | 94 --------------- .../RP2040UsbUartMasterClock.ino} | 76 +++++------- .../RP2040UsbUartMasterClock/builtin_led.ino | 37 ++++++ src/platforms/rp2040.h | 110 ++++-------------- 4 files changed, 84 insertions(+), 233 deletions(-) delete mode 100644 examples/RP2040ClockBlink/RP2040ClockBlink.ino rename examples/{RP2040UsbMasterMidiClock/RP2040UsbMasterMidiClock.ino => RP2040UsbUartMasterClock/RP2040UsbUartMasterClock.ino} (52%) create mode 100644 examples/RP2040UsbUartMasterClock/builtin_led.ino diff --git a/examples/RP2040ClockBlink/RP2040ClockBlink.ino b/examples/RP2040ClockBlink/RP2040ClockBlink.ino deleted file mode 100644 index 63c0650..0000000 --- a/examples/RP2040ClockBlink/RP2040ClockBlink.ino +++ /dev/null @@ -1,94 +0,0 @@ -/* USB MIDI Sync Box - RP2040 example that just blinks LED - * - * - * This example code is in the public domain. - * - */ - -//#define LED_BUILTIN PIN_LED_B - -#include "Adafruit_TinyUSB.h" - -#include - -#define ATOMIC(X) { uint32_t __interrupt_mask = save_and_disable_interrupts(); X; restore_interrupts(__interrupt_mask); } - -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, LOW); - } else if ( !(tick % (24)) ) { // each quarter led on - bpm_blink_timer = 1; - digitalWrite(LED_BUILTIN, LOW); - } else if ( !(tick % bpm_blink_timer) ) { // get led off - digitalWrite(LED_BUILTIN, HIGH); - } -} - - -// Internal clock handlers -void onSync24Callback(uint32_t tick) { - handle_bpm_led(tick); -} - -void onClockStart() { - //MIDI_USB.sendRealTime(midi::Start); -} - -void onClockStop() { - //MIDI_USB.sendRealTime(midi::Stop); -} - -void setup() { -/*#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) - // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 - TinyUSB_Device_Init(0); -#endif*/ - - //MIDI_USB.begin(MIDI_CHANNEL_OMNI); - - // A led to count bpms - pinMode(LED_BUILTIN, OUTPUT); - /*digitalWrite(LED_BUILTIN, HIGH); - delay(500); - digitalWrite(LED_BUILTIN, LOW); - delay(500); - digitalWrite(LED_BUILTIN, HIGH); - delay(500); - digitalWrite(LED_BUILTIN, LOW); - delay(500);*/ - - - Serial.begin(115200); - /*while (!Serial) - delay(1);*/ - - // Setup our clock system - - // Inits the clock - uClock.init(); - // Set the callback function for the clock output to send MIDI Sync message. - uClock.setOnSync24(onSync24Callback); - // Set the callback function for MIDI Start and Stop messages. - uClock.setOnClockStart(onClockStart); - uClock.setOnClockStop(onClockStop); - // Set the clock BPM to 126 BPM - uClock.setTempo(60); - // Starts the clock, tick-tac-tick-tac.. - //Serial.println("about to uClock.start()..."); Serial.flush(); - uClock.start(); - //Serial.println("uClock.start()ed!"); Serial.flush(); -} - -uint32_t count = 0; - -// Do it whatever to interface with Clock.stop(), Clock.start(), Clock.setTempo() and integrate your environment... -void loop() { - //MIDI_USB.read(); - //count++; - //if (millis()%1000==0) - // ATOMIC(Serial.println("looped!")); -} diff --git a/examples/RP2040UsbMasterMidiClock/RP2040UsbMasterMidiClock.ino b/examples/RP2040UsbUartMasterClock/RP2040UsbUartMasterClock.ino similarity index 52% rename from examples/RP2040UsbMasterMidiClock/RP2040UsbMasterMidiClock.ino rename to examples/RP2040UsbUartMasterClock/RP2040UsbUartMasterClock.ino index 666bd38..e571da9 100644 --- a/examples/RP2040UsbMasterMidiClock/RP2040UsbMasterMidiClock.ino +++ b/examples/RP2040UsbUartMasterClock/RP2040UsbUartMasterClock.ino @@ -1,95 +1,74 @@ -/* USB MIDI Sync Box - example that also prints to serial and sends USB midi as well as blinking LED - * (usb part still needs testing to make sure it works!) +/* + * USB/Uart MIDI Sync Box * * This example code is in the public domain. * */ + #include #include -Adafruit_USBD_MIDI usb_midi; -MIDI_CREATE_INSTANCE(Adafruit_USBD_MIDI, usb_midi, MIDI_USB); -//MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI); - #include -#define ATOMIC(X) { uint32_t __interrupt_mask = save_and_disable_interrupts(); X; restore_interrupts(__interrupt_mask); } -//#define LED_BUILTIN PIN_LED_B -//#define WAIT_FOR_SERIAL -#define ENABLE_MULTICORE +// Instantiate the MIDI interfaces +Adafruit_USBD_MIDI usb_midi; +MIDI_CREATE_INSTANCE(Adafruit_USBD_MIDI, usb_midi, MIDI_USB); +MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI); -volatile uint32_t count = 0; +// Do your rpi 2040 has a ws2812 RGB LED? set the pin! +// otherwise keep it commented for normal LED_BUILTIN led blinking +#define WS2812_BUILTIN_LED 16 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 + if ( !(tick % (96)) || (tick == 1) ) { // first of 4 quarter pulse will flash longer bpm_blink_timer = 8; - digitalWrite(LED_BUILTIN, LOW); + ledOn(); } else if ( !(tick % (24)) ) { // each quarter led on bpm_blink_timer = 1; - digitalWrite(LED_BUILTIN, LOW); + ledOn(); } else if ( !(tick % bpm_blink_timer) ) { // get led off - digitalWrite(LED_BUILTIN, HIGH); + ledOff(); } } - // Internal clock handlers void onSync24Callback(uint32_t tick) { // Send MIDI_CLOCK to external gears + MIDI.sendRealTime(midi::Clock); MIDI_USB.sendRealTime(midi::Clock); + // blink tempo handle_bpm_led(tick); - - Serial.printf("ticked with %u\n", tick); } void onClockStart() { + MIDI.sendRealTime(midi::Start); MIDI_USB.sendRealTime(midi::Start); } void onClockStop() { + MIDI.sendRealTime(midi::Stop); MIDI_USB.sendRealTime(midi::Stop); } -#ifdef ENABLE_MULTICORE - void setup1() { - } - - void loop1() { - if (count%1000==0) - ATOMIC(Serial.println("loop1()!"); Serial.flush()); - } -#endif - void setup() { #if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 TinyUSB_Device_Init(0); #endif + // Initialize USB midi stack MIDI_USB.begin(MIDI_CHANNEL_OMNI); + // Initialize UART midi stack + MIDI.begin(MIDI_CHANNEL_OMNI); - // A led to count bpms - pinMode(LED_BUILTIN, OUTPUT); - - Serial.begin(115200); - #ifdef WAIT_FOR_SERIAL - while (!Serial) - delay(1); - #endif - - // wait until device mounted - while( !TinyUSBDevice.mounted() ) { - Serial.println("waiting for usb.."); - Serial.flush(); - delay(1); - } + // Initialize builtin led for clock timer blinking + initBlinkLed(); // Setup our clock system // Inits the clock - Serial.println("about to uClock.init()..."); Serial.flush(); uClock.init(); // Set the callback function for the clock output to send MIDI Sync message. uClock.setOnSync24(onSync24Callback); @@ -97,17 +76,14 @@ void setup() { uClock.setOnClockStart(onClockStart); uClock.setOnClockStop(onClockStop); // Set the clock BPM to 126 BPM - uClock.setTempo(60); + uClock.setTempo(126); // Starts the clock, tick-tac-tick-tac.. - Serial.println("about to uClock.start()..."); Serial.flush(); uClock.start(); - ATOMIC(Serial.println("uClock.start()ed!"); Serial.flush();) } // Do it whatever to interface with Clock.stop(), Clock.start(), Clock.setTempo() and integrate your environment... void loop() { + // handle midi input? + MIDI.read(); MIDI_USB.read(); - count++; - if (millis()%1000==0) - ATOMIC(Serial.println("loop()!"); Serial.flush();); } diff --git a/examples/RP2040UsbUartMasterClock/builtin_led.ino b/examples/RP2040UsbUartMasterClock/builtin_led.ino new file mode 100644 index 0000000..7a1eedf --- /dev/null +++ b/examples/RP2040UsbUartMasterClock/builtin_led.ino @@ -0,0 +1,37 @@ +#if defined(WS2812_BUILTIN_LED) +#include +#define NUMPIXELS 1 +Adafruit_NeoPixel pixels(NUMPIXELS, WS2812_BUILTIN_LED, NEO_GRB + NEO_KHZ800); +#endif + +// check the pinage for BUILTIN LED of your model in case LED_BUILTIN wont ligth up +// this is valid only if you're not using rgb version ws2812 (WS2812_BUILTIN_LED) +//#define LED_BUILTIN PIN_LED_B + +void initBlinkLed() { +#if defined(WS2812_BUILTIN_LED) + // use adafruit neo pixel + pixels.begin(); +#else + // normal led pin + pinMode(LED_BUILTIN, OUTPUT); +#endif +} + +void ledOn() { +#if defined(WS2812_BUILTIN_LED) + pixels.setPixelColor(0, pixels.Color(0, 0, 20)); + pixels.show(); // turn the LED on (HIGH is the voltage level) +#else + digitalWrite(LED_BUILTIN, LOW); +#endif +} + +void ledOff() { +#if defined(WS2812_BUILTIN_LED) + pixels.setPixelColor(0, pixels.Color(0, 0, 0)); + pixels.show(); +#else + digitalWrite(LED_BUILTIN, HIGH); +#endif +} \ No newline at end of file diff --git a/src/platforms/rp2040.h b/src/platforms/rp2040.h index 7cf1069..eb918c2 100644 --- a/src/platforms/rp2040.h +++ b/src/platforms/rp2040.h @@ -1,98 +1,30 @@ #include #include "pico/sync.h" -// todo: make this a build flag, so user can choose which method to use? -#define MULTICORE +// RPi-specific timer +struct repeating_timer timer; -#ifdef MULTICORE - // use interrupt version -- works for 2 cores ie can run loop1() and loop() simultaneously as well as the clock callback? +#define ATOMIC(X) { uint32_t __interrupt_mask = save_and_disable_interrupts(); X; restore_interrupts(__interrupt_mask); } - // RPi-specific timer - struct repeating_timer timer; +// forward declaration of uClockHandler +void uClockHandler(); - #define ATOMIC(X) { uint32_t __interrupt_mask = save_and_disable_interrupts(); X; restore_interrupts(__interrupt_mask); } +// ISR handler -- called when tick happens +bool handlerISR(repeating_timer *timer) +{ + uClockHandler(); - // forward declaration of uClockHandler - void uClockHandler(); + return true; +} - // ISR handler -- called when tick happens - bool handlerISR(repeating_timer *timer) - { - uClockHandler(); +void initTimer(uint32_t init_clock) { + // set up RPi interrupt timer + // todo: actually should be -init_clock so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick! + add_repeating_timer_us(init_clock, &handlerISR, NULL, &timer); +} - return true; - } - - void initTimer(uint32_t init_clock) { - // set up RPi interrupt timer - // todo: actually should be -init_clock so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick! - add_repeating_timer_us(init_clock, &handlerISR, NULL, &timer); - } - - void setTimer(uint32_t us_interval) { - cancel_repeating_timer(&timer); - // todo: actually should be -us_interval so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick! - add_repeating_timer_us(us_interval, &handlerISR, NULL, &timer); - } -#else - // use FreeRTOS scheduling/mutex version -- doesn't work (task starts but does not run) if using loop1() ie core 2 - - #include "FreeRTOS.h" - #include - #include - - // RPi-specific timer - struct repeating_timer timer; - - // FreeRTOS main clock task size in bytes - #define CLOCK_STACK_SIZE 5*1024 // adjust for your needs, a sequencer with heavy serial handling should be large in size - TaskHandle_t taskHandle; - // mutex to protect the shared resource - SemaphoreHandle_t _mutex; - // mutex control for task - #define ATOMIC(X) xSemaphoreTake(_mutex, portMAX_DELAY); X; xSemaphoreGive(_mutex); - - // forward declaration of uClockHandler - void uClockHandler(); - - // ISR handler -- called when tick happens - bool handlerISR(repeating_timer *timer) - { - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - // Send a notification to task1 - vTaskNotifyGiveFromISR(taskHandle, &xHigherPriorityTaskWoken); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); - - return true; - } - - // task for user clock process - void clockTask(void *pvParameters) - { - while (1) { - // wait for a notification from ISR - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - uClockHandler(); - } - } - - void initTimer(uint32_t init_clock) - { - // initialize the mutex for shared resource access - _mutex = xSemaphoreCreateMutex(); - - // create the clockTask - xTaskCreate(clockTask, "clockTask", CLOCK_STACK_SIZE, NULL, 1, &taskHandle); - - // set up RPi interrupt timer - // todo: actually should be -init_clock so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick! - add_repeating_timer_us(init_clock, &handlerISR, NULL, &timer); - } - - void setTimer(uint32_t us_interval) { - cancel_repeating_timer(&timer); - // todo: actually should be -us_interval so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick! - add_repeating_timer_us(us_interval, &handlerISR, NULL, &timer); - } - -#endif \ No newline at end of file +void setTimer(uint32_t us_interval) { + cancel_repeating_timer(&timer); + // todo: actually should be -us_interval so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick! + add_repeating_timer_us(us_interval, &handlerISR, NULL, &timer); +} \ No newline at end of file From ceddb190f0f6c5279d8cce59224c4bff3b937a2e Mon Sep 17 00:00:00 2001 From: midilab Date: Thu, 11 Apr 2024 11:18:57 -0300 Subject: [PATCH 10/10] remove know problems related to freertos. lets asume it also works on non earlephilhower core --- README.md | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 0f1de32..2782dd5 100755 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ The **uClock BPM Generator library** is designed to implement precise and reliable BPM clock tick calls using the microcontroller's timer hardware interruption. It is designed to be multi-architecture, portable, and easy to use within the open source community universe. -We have chosen PlatformIO and Arduino as our official deployment platforms. The library has been supported and tested on general **AVR boards (ATmega168/328, ATmega16u4/32u4, and ATmega2560)** as well as **ARM boards (Teensy, STM32XX, and Seedstudio XIAO M0)**. It has experimental support for **RP2040 boards (Raspberry Pico, Seeed XIAO RP2040)** (see notes). +We have chosen PlatformIO and Arduino as our official deployment platforms. The library has been supported and tested on general **AVR boards (ATmega168/328, ATmega16u4/32u4, and ATmega2560)** as well as **ARM boards (Teensy, STM32XX, and Seedstudio XIAO M0, Raspberry Pico, Seeed XIAO RP2040)**. The absence of real-time features necessary for creating professional-level embedded devices for music and video on open source community-based platforms like Arduino led to the development of uClock. By leveraging the use of timer hardware interruptions, the library can schedule and manage real-time-like processing with safe shared resource access through its API. @@ -370,13 +370,4 @@ void loop() //processYourLeds(); //processYourPots(); } -``` - -## Known problems - -### RP2040 support - -- Uses the [earlephilhower core](https://github.com/earlephilhower/arduino-pico) -- Doing a 'soft reboot' (eg from reflashing) seems to crash on startup, but starting from cold and powering on works fine. -- Using FreeRTOS multithreading fails if the second core is used (via setup1() and loop1()) - using the 'interrupts-based' version of the RP2040 uClock support seems to solve this. -- Tick ticking may be off due to repeating_timer following from the end of previous tick, rather than following the start of the previous tick. +``` \ No newline at end of file