mirror of https://github.com/midilab/uClock
Merge pull request #24 from midilab/stm32xx-support
* STM32 generic support by using STM32Duino and Hal library. * Independent platform implementation support for easy arch ports, check platforms/ dir. * Fixed missing name.c file for usb-midi definitions on teensy arm platformspull/26/head v1.3.0
commit
ea8ef07e5b
@ -0,0 +1,83 @@ |
|||||||
|
/* 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 <uClock.h> |
||||||
|
|
||||||
|
// 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 |
||||||
|
|
||||||
|
HardwareSerial Serial1(PA10, PA9); |
||||||
|
|
||||||
|
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 gear
|
||||||
|
Serial1.write(MIDI_CLOCK); |
||||||
|
handle_bpm_led(tick); |
||||||
|
} |
||||||
|
|
||||||
|
void onClockStart() { |
||||||
|
// Send MIDI_START to external gear
|
||||||
|
Serial1.write(MIDI_START); |
||||||
|
} |
||||||
|
|
||||||
|
void onClockStop() { |
||||||
|
// Send MIDI_STOP to external gear
|
||||||
|
Serial1.write(MIDI_STOP); |
||||||
|
} |
||||||
|
|
||||||
|
void setup() { |
||||||
|
// Initialize Serial1 communication at 31250 bits per second, the default MIDI Serial1 speed communication:
|
||||||
|
Serial1.begin(31250); |
||||||
|
|
||||||
|
// An led to display BPM
|
||||||
|
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(120); |
||||||
|
// 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() { |
||||||
|
|
||||||
|
} |
@ -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 |
||||||
|
}; |
@ -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 |
||||||
|
}; |
@ -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 |
||||||
|
}; |
@ -1,10 +1,10 @@ |
|||||||
name=uClock |
name=uClock |
||||||
version=1.2.0 |
version=1.3.0 |
||||||
author=Romulo Silva <contact@midilab.co> |
author=Romulo Silva <contact@midilab.co> |
||||||
maintainer=Romulo Silva <contact@midilab.co> |
maintainer=Romulo Silva <contact@midilab.co> |
||||||
sentence=BPM clock generator for Arduino platform. |
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 |
category=Timing |
||||||
url=https://github.com/midilab/uClock |
url=https://github.com/midilab/uClock |
||||||
architectures=avr,arm,samd,esp32 |
architectures=avr,arm,samd,stm32,esp32 |
||||||
includes=uClock.h |
includes=uClock.h |
||||||
|
@ -0,0 +1,62 @@ |
|||||||
|
#include <Arduino.h> |
||||||
|
|
||||||
|
#define ATOMIC(X) noInterrupts(); X; interrupts(); |
||||||
|
|
||||||
|
// want a different avr clock support?
|
||||||
|
// 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) |
||||||
|
{ |
||||||
|
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; |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
#include <Arduino.h> |
||||||
|
|
||||||
|
#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);
|
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
#include <Arduino.h> |
||||||
|
|
||||||
|
// 24 bits timer
|
||||||
|
#include <TimerTCC0.h> |
||||||
|
// uses TimerTcc0
|
||||||
|
// 16 bits timer
|
||||||
|
//#include <TimerTC3.h>
|
||||||
|
// 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); |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
#include <Arduino.h> |
||||||
|
|
||||||
|
#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/install stm32duino core." |
||||||
|
#endif |
||||||
|
|
||||||
|
#if defined(TIM1) |
||||||
|
TIM_TypeDef * TimerInstance = TIM1; |
||||||
|
#else |
||||||
|
TIM_TypeDef * TimerInstance = TIM2; |
||||||
|
#endif |
||||||
|
|
||||||
|
// instantiate HardwareTimer object
|
||||||
|
HardwareTimer * _uclockTimer = new HardwareTimer(TimerInstance); |
||||||
|
|
||||||
|
#define ATOMIC(X) noInterrupts(); X; interrupts(); |
||||||
|
|
||||||
|
// forward declaration of ISR
|
||||||
|
void uclockISR(); |
||||||
|
|
||||||
|
void initTimer(uint32_t us_interval) |
||||||
|
{ |
||||||
|
_uclockTimer->setOverflow(us_interval, MICROSEC_FORMAT); |
||||||
|
_uclockTimer->attachInterrupt(uclockISR); |
||||||
|
_uclockTimer->resume(); |
||||||
|
} |
||||||
|
|
||||||
|
void setTimer(uint32_t us_interval) |
||||||
|
{ |
||||||
|
_uclockTimer->setOverflow(us_interval, MICROSEC_FORMAT); |
||||||
|
_uclockTimer->refresh(); |
||||||
|
} |
||||||
|
|
@ -0,0 +1,25 @@ |
|||||||
|
#include <Arduino.h> |
||||||
|
|
||||||
|
#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); |
||||||
|
} |
Loading…
Reference in new issue