Merge pull request #9 from midilab/xiao-support

seedstudio xiao m0 support
pull/11/head v1.1.0
midilab 2 years ago committed by GitHub
commit 220d6753f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      README.md
  2. 6
      examples/AcidStepSequencer/AcidStepSequencer.ino
  3. 10
      examples/LeonardoUsbSlaveMidiClockMonitor/LeonardoUsbSlaveMidiClockMonitor.ino
  4. 2
      examples/MidiClock/MidiClock.ino
  5. 10
      examples/TeensyUsbMasterMidiClock/TeensyUsbMasterMidiClock.ino
  6. 8
      examples/TeensyUsbSlaveMidiClock/TeensyUsbSlaveMidiClock.ino
  7. 8
      examples/TeensyUsbSlaveMidiClockMonitor/TeensyUsbSlaveMidiClockMonitor.ino
  8. 81
      examples/XiaoUsbMasterMidiClock/XiaoUsbMasterMidiClock.ino
  9. 6
      library.properties
  10. 105
      src/uClock.cpp
  11. 49
      src/uClock.h

@ -1,6 +1,6 @@
# uClock
**BPM clock generator for Arduino and Teensy** is a library to implement BPM clock tick calls using **hardware interruption** for tight and solid timing clock ticks. Tested on ATmega168/328, ATmega16u4/32u4, ATmega2560 and Teensy LC.
**BPM clock generator for Arduino and Teensy** is a library to implement BPM clock tick calls using **hardware interruption** for tight and solid timing clock ticks. Tested on ATmega168/328, ATmega16u4/32u4, ATmega2560, Teensy ARM boards and Seedstudio XIAO M0.
Generate your self tight BPM clock for music, audio/video productions, performances or installations. You can clock your MIDI setup or sync different protocols as you wish.
@ -34,7 +34,7 @@ Here is an example on how to create a simple MIDI Sync Box on Arduino boards
#define MIDI_STOP 0xFC
// The callback function wich will be called by Clock each Pulse of 96PPQN clock resolution.
void ClockOut96PPQN(uint32_t * tick) {
void ClockOut96PPQN(uint32_t tick) {
// Send MIDI_CLOCK to external gears
Serial.write(MIDI_CLOCK);
}
@ -81,7 +81,7 @@ An example on how to create a simple MIDI Sync Box on Teensy boards and USB Midi
#include <uClock.h>
// The callback function wich will be called by Clock each Pulse of 96PPQN clock resolution.
void ClockOut96PPQN(uint32_t * tick) {
void ClockOut96PPQN(uint32_t tick) {
// Send MIDI_CLOCK to external gears
usbMIDI.sendRealTime(usbMIDI.Clock);
}
@ -185,13 +185,13 @@ void sendMidiMessage(uint8_t command, uint8_t byte1, uint8_t byte2)
}
// The callback function wich will be called by uClock each Pulse of 16PPQN clock resolution. Each call represents exactly one step.
void ClockOut16PPQN(uint32_t * tick)
void ClockOut16PPQN(uint32_t tick)
{
uint16_t step;
uint16_t length = NOTE_LENGTH;
// get actual step.
_step = *tick % _step_length;
_step = tick % _step_length;
// send note on only if this step are not in rest mode
if ( _sequencer[_step].rest == false ) {
@ -223,7 +223,7 @@ void ClockOut16PPQN(uint32_t * tick)
}
// The callback function wich will be called by uClock each Pulse of 96PPQN clock resolution.
void ClockOut96PPQN(uint32_t * tick)
void ClockOut96PPQN(uint32_t tick)
{
// Send MIDI_CLOCK to external hardware
Serial.write(MIDI_CLOCK);

@ -64,13 +64,13 @@ void sendMidiMessage(uint8_t command, uint8_t byte1, uint8_t byte2)
}
// The callback function wich will be called by uClock each Pulse of 16PPQN clock resolution. Each call represents exactly one step.
void ClockOut16PPQN(uint32_t * tick)
void ClockOut16PPQN(uint32_t tick)
{
uint16_t step;
uint16_t length = NOTE_LENGTH;
// get actual step.
_step = *tick % _step_length;
_step = tick % _step_length;
// send note on only if this step are not in rest mode
if ( _sequencer[_step].rest == false ) {
@ -102,7 +102,7 @@ void ClockOut16PPQN(uint32_t * tick)
}
// The callback function wich will be called by uClock each Pulse of 96PPQN clock resolution.
void ClockOut96PPQN(uint32_t * tick)
void ClockOut96PPQN(uint32_t tick)
{
// Send MIDI_CLOCK to external hardware
Serial.write(MIDI_CLOCK);

@ -34,22 +34,22 @@ uint8_t bpm_blink_timer = 1;
uint8_t clock_state = 1;
uint8_t clock_mode = 0;
void handle_bpm_led(uint32_t * tick)
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 compass step will flash longer
bpm_blink_timer = 8;
TXLED1;
} else if ( !(*tick % (24)) ) { // each quarter led on
} else if ( !(tick % (24)) ) { // each quarter led on
TXLED1;
} else if ( !(*tick % bpm_blink_timer) ) { // get led off
} else if ( !(tick % bpm_blink_timer) ) { // get led off
TXLED0;
bpm_blink_timer = 1;
}
}
// Internal clock handlers
void ClockOut96PPQN(uint32_t * tick) {
void ClockOut96PPQN(uint32_t tick) {
// Send MIDI_CLOCK to external gears
MIDI.sendRealTime(MIDI_CLOCK);
handle_bpm_led(tick);

@ -7,7 +7,7 @@
#define MIDI_STOP 0xFC
// The callback function wich will be called by Clock each Pulse of 96PPQN clock resolution.
void ClockOut96PPQN(uint32_t * tick)
void ClockOut96PPQN(uint32_t tick)
{
// Send MIDI_CLOCK to external gears
Serial.write(MIDI_CLOCK);

@ -21,22 +21,22 @@
#include <uClock.h>
uint8_t bpm_blink_timer = 1;
void handle_bpm_led(uint32_t * tick)
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 compass step will flash longer
bpm_blink_timer = 8;
digitalWrite(LED_BUILTIN, HIGH);
} else if ( !(*tick % (24)) ) { // each quarter led on
} else if ( !(tick % (24)) ) { // each quarter led on
digitalWrite(LED_BUILTIN, HIGH);
} else if ( !(*tick % bpm_blink_timer) ) { // get led off
} else if ( !(tick % bpm_blink_timer) ) { // get led off
digitalWrite(LED_BUILTIN, LOW);
bpm_blink_timer = 1;
}
}
// Internal clock handlers
void ClockOut96PPQN(uint32_t * tick) {
void ClockOut96PPQN(uint32_t tick) {
// Send MIDI_CLOCK to external gears
usbMIDI.sendRealTime(usbMIDI.Clock);
handle_bpm_led(tick);

@ -24,19 +24,19 @@ 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 compass step will flash longer
bpm_blink_timer = 8;
digitalWrite(LED_BUILTIN, HIGH);
} else if ( !(*tick % (24)) ) { // each quarter led on
} else if ( !(tick % (24)) ) { // each quarter led on
digitalWrite(LED_BUILTIN, HIGH);
} else if ( !(*tick % bpm_blink_timer) ) { // get led off
} else if ( !(tick % bpm_blink_timer) ) { // get led off
digitalWrite(LED_BUILTIN, LOW);
bpm_blink_timer = 1;
}
}
// Internal clock handlers
void ClockOut96PPQN(uint32_t * tick) {
void ClockOut96PPQN(uint32_t tick) {
// Send MIDI_CLOCK to external gears on other port?
//usbMIDI.sendRealTime(usbMIDI.Clock);
handle_bpm_led(tick);

@ -37,19 +37,19 @@ uint8_t clock_state = 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 compass step will flash longer
bpm_blink_timer = 8;
digitalWrite(LED_BUILTIN, HIGH);
} else if ( !(*tick % (24)) ) { // each quarter led on
} else if ( !(tick % (24)) ) { // each quarter led on
digitalWrite(LED_BUILTIN, HIGH);
} else if ( !(*tick % bpm_blink_timer) ) { // get led off
} else if ( !(tick % bpm_blink_timer) ) { // get led off
digitalWrite(LED_BUILTIN, LOW);
bpm_blink_timer = 1;
}
}
// Internal clock handlers
void ClockOut96PPQN(uint32_t * tick) {
void ClockOut96PPQN(uint32_t tick) {
// Send MIDI_CLOCK to external gears
usbMIDI.sendRealTime(MIDI_CLOCK);
handle_bpm_led(tick);

@ -0,0 +1,81 @@
/* USB MIDI Sync Box
*
* This example demonstrates how to change the USB MIDI
* device name on Teensy LC and 3.x. When creating more
* that one MIDI device, custom names are much easier to
* use when selecting each device in MIDI software on
* your PC or Mac. The custom name is in the "name.c" tab.
*
* Windows and Macintosh systems often cache USB info.
* After changing the name, you may need to test on a
* different computer to observe the new name, or take
* steps to get your operating system to "forget" the
* cached info. (TODO: wanted... can anyone contribute
* instructions for these systems)
*
* This example code is in the public domain.
*/
#include <Adafruit_TinyUSB.h>
#include <MIDI.h>
Adafruit_USBD_MIDI usb_midi;
MIDI_CREATE_INSTANCE(Adafruit_USBD_MIDI, usb_midi, MIDI);
#include <uClock.h>
/*
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
digitalWrite(LED_BUILTIN, HIGH);
} else if ( !(tick % bpm_blink_timer) ) { // get led off
digitalWrite(LED_BUILTIN, LOW);
bpm_blink_timer = 1;
}
}
*/
// Internal clock handlers
void ClockOut96PPQN(uint32_t tick) {
// Send MIDI_CLOCK to external gears
//MIDI.sendRealTime(MIDI.Clock);
//handle_bpm_led(tick);
}
void onClockStart() {
//MIDI.sendRealTime(MIDI.Start);
}
void onClockStop() {
//MIDI.sendRealTime(MIDI.Stop);
}
void setup() {
MIDI.begin(MIDI_CHANNEL_OMNI);
// 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() {
}

@ -1,10 +1,10 @@
name=uClock
version=1.0.0
version=1.1.0
author=Romulo Silva <contact@midilab.co>
maintainer=Romulo Silva <contact@midilab.co>
sentence=BPM clock generator for Arduino and Teensy boards
paragraph=A Library to implement BPM clock tick calls using hardware interruption. Tested on ATmega168/328, ATmega16u4/32u4, ATmega2560 and Teensy ARM boards.
paragraph=A Library to implement BPM clock tick calls using hardware interruption. Tested on ATmega168/328, ATmega16u4/32u4, ATmega2560, Teensy ARM boards and Seedstudio XIAO M0
category=Timing
url=https://github.com/midilab/uClock
architectures=avr,arm
architectures=avr,arm,samd
includes=uClock.h

@ -1,10 +1,10 @@
/*!
* @file uClock.cpp
* Project BPM clock generator for Arduino
* @brief A Library to implement BPM clock tick calls using hardware timer interruption. Tested on ATmega168/328, ATmega16u4/32u4 and ATmega2560 and Teensy LC.
* @version 1.0.0
* @brief A Library to implement BPM clock tick calls using hardware timer interruption. Tested on ATmega168/328, ATmega16u4/32u4, ATmega2560, Teensy ARM boards and Seedstudio XIAO M0
* @version 1.1.0
* @author Romulo Silva
* @date 01/04/2022
* @date 04/03/2022
* @license MIT - (c) 2022 - Romulo Silva - contact@midilab.co
*
* Permission is hereby granted, free of charge, to any person obtaining a
@ -30,29 +30,26 @@
//
// Timer setup for work clock
//
#if defined(TEENSYDUINO) && !defined(__AVR_ATmega32U4__)
// all non-avr timmers setup
// Teensyduino port
#if defined(TEENSYDUINO)
IntervalTimer _uclockTimer;
void uclockISR();
void uclockInitTimer()
{
ATOMIC(
#endif
// Seedstudio XIAO M0 port
#if defined(SEEED_XIAO_M0)
// 24 bits timer
#include <TimerTCC0.h>
#define _uclockTimer TimerTcc0
// 16 bits timer
//#include <TimerTC3.h>
//#define _uclockTimer TimerTc3
#endif
// begin at 120bpm (20833us)
_uclockTimer.begin(uclockISR, 20833);
// 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);
)
}
#else
#if defined(ARDUINO_ARCH_AVR)
void uclockInitTimer()
{
ATOMIC(
// Timer1 init
// 16bits Timer1 init
// begin at 120bpm (48.0007680122882 Hz)
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
@ -67,6 +64,30 @@ void uclockInitTimer()
TIMSK1 |= (1 << OCIE1A);
)
}
#else
void uclockISR();
void uclockInitTimer()
{
// 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)
_uclockTimer.initialize(init_clock);
// attach to generic uclock ISR
_uclockTimer.attachInterrupt(uclockISR);
#endif
}
#endif
namespace umodular { namespace clock {
@ -150,14 +171,11 @@ void uClockClass::pause()
void uClockClass::setTimerTempo(float bpm)
{
// 96 ppqn resolution
tick_us_interval = (60000000 / 24 / bpm);
tick_hertz_interval = 1/((float)tick_us_interval/1000000);
uint32_t tick_us_interval = (60000000 / 24 / bpm);
#if defined(ARDUINO_ARCH_AVR)
float tick_hertz_interval = 1/((float)tick_us_interval/1000000);
#if defined(TEENSYDUINO) && !defined(__AVR_ATmega32U4__)
ATOMIC(
_uclockTimer.update(tick_us_interval);
)
#else
uint32_t ocr;
uint8_t tccr = 0;
@ -188,6 +206,14 @@ void uClockClass::setTimerTempo(float bpm)
TCCR1B |= (1 << WGM12);
TCCR1B |= tccr;
)
#else
#if defined(TEENSYDUINO)
_uclockTimer.update(tick_us_interval);
#endif
#if defined(SEEED_XIAO_M0)
_uclockTimer.setPeriod(tick_us_interval);
#endif
#endif
}
@ -201,9 +227,12 @@ void uClockClass::setTempo(float bpm)
return;
}
setTimerTempo(bpm);
ATOMIC(
tempo = bpm
)
tempo = bpm;
setTimerTempo(bpm);
}
float inline uClockClass::freqToBpm(uint32_t freq)
@ -349,21 +378,21 @@ void uClockClass::handleTimerInt()
if (bpm != tempo) {
if (bpm >= MIN_BPM && bpm <= MAX_BPM) {
tempo = bpm;
setTimerTempo(tempo);
setTimerTempo(bpm);
}
}
}
if (onClock96PPQNCallback) {
onClock96PPQNCallback(&internal_tick);
onClock96PPQNCallback(internal_tick);
}
if (mod6_counter == 0) {
if (onClock32PPQNCallback) {
onClock32PPQNCallback(&div32th_counter);
onClock32PPQNCallback(div32th_counter);
}
if (onClock16PPQNCallback) {
onClock16PPQNCallback(&div16th_counter);
onClock16PPQNCallback(div16th_counter);
}
div16th_counter++;
div32th_counter++;
@ -371,7 +400,7 @@ void uClockClass::handleTimerInt()
if (mod6_counter == 3) {
if (onClock32PPQNCallback) {
onClock32PPQNCallback(&div32th_counter);
onClock32PPQNCallback(div32th_counter);
}
div32th_counter++;
}
@ -439,10 +468,10 @@ volatile uint32_t _timer = 0;
// TIMER INTERRUPT HANDLER
//
//
#if defined(TEENSYDUINO) && !defined(__AVR_ATmega32U4__)
void uclockISR()
#if defined(ARDUINO_ARCH_AVR)
ISR(TIMER1_COMPA_vect)
#else
ISR(TIMER1_COMPA_vect)
void uclockISR()
#endif
{
// global timer counter

@ -1,10 +1,10 @@
/*!
* @file uClock.h
* Project BPM clock generator for Arduino
* @brief A Library to implement BPM clock tick calls using hardware timer interruption. Tested on ATmega168/328, ATmega16u4/32u4 and ATmega2560 and Teensy LC.
* @version 1.0.0
* @brief A Library to implement BPM clock tick calls using hardware timer interruption. Tested on ATmega168/328, ATmega16u4/32u4, ATmega2560, Teensy ARM boards and Seedstudio XIAO M0
* @version 1.1.0
* @author Romulo Silva
* @date 01/04/2022
* @date 04/03/2022
* @license MIT - (c) 2022 - Romulo Silva - contact@midilab.co
*
* Permission is hereby granted, free of charge, to any person obtaining a
@ -34,25 +34,25 @@
namespace umodular { namespace clock {
#define AVR_CLOCK_FREQ 16000000
#define PHASE_FACTOR 16
#define PLL_X 220
// for smooth slave tempo calculate display you should raise this value
// in between 64 to 128.
// note: this doesn't impact on sync time, only display time getTempo()
// if you dont want to use it, set it to 1 for memory save
#define EXT_INTERVAL_BUFFER_SIZE 24
#define MIN_BPM 1
#define MAX_BPM 300
// want a different avr clock support?
#define AVR_CLOCK_FREQ 16000000
#define PHASE_FACTOR 16
#define PLL_X 220
#define SECS_PER_MIN (60UL)
#define SECS_PER_HOUR (3600UL)
#define SECS_PER_DAY (SECS_PER_HOUR * 24L)
#define MIN_BPM 1
#define MAX_BPM 300
#define ATOMIC(X) noInterrupts(); X; interrupts();
class uClockClass {
@ -62,17 +62,17 @@ class uClockClass {
void setTimerTempo(float bpm);
float inline freqToBpm(uint32_t freq);
void (*onClock96PPQNCallback)(uint32_t * tick);
void (*onClock32PPQNCallback)(uint32_t * tick);
void (*onClock16PPQNCallback)(uint32_t * tick);
void (*onClock96PPQNCallback)(uint32_t tick);
void (*onClock32PPQNCallback)(uint32_t tick);
void (*onClock16PPQNCallback)(uint32_t tick);
void (*onClockStartCallback)();
void (*onClockStopCallback)();
// internal clock control
volatile uint32_t internal_tick;
volatile uint32_t div32th_counter;
volatile uint32_t div16th_counter;
volatile uint8_t mod6_counter;
uint32_t internal_tick;
uint32_t div32th_counter;
uint32_t div16th_counter;
uint8_t mod6_counter;
// external clock control
volatile uint32_t external_clock;
@ -81,12 +81,9 @@ class uClockClass {
volatile uint32_t indiv16th_counter;
volatile uint8_t inmod6_counter;
volatile uint32_t interval;
volatile uint32_t last_interval;
uint32_t last_interval;
uint32_t sync_interval;
uint32_t tick_us_interval;
float tick_hertz_interval;
float tempo;
uint32_t start_timer;
uint8_t mode;
@ -111,15 +108,15 @@ class uClockClass {
uClockClass();
void setClock96PPQNOutput(void (*callback)(uint32_t * tick)) {
void setClock96PPQNOutput(void (*callback)(uint32_t tick)) {
onClock96PPQNCallback = callback;
}
void setClock32PPQNOutput(void (*callback)(uint32_t * tick)) {
void setClock32PPQNOutput(void (*callback)(uint32_t tick)) {
onClock32PPQNCallback = callback;
}
void setClock16PPQNOutput(void (*callback)(uint32_t * tick)) {
void setClock16PPQNOutput(void (*callback)(uint32_t tick)) {
onClock16PPQNCallback = callback;
}

Loading…
Cancel
Save