midilab / uClock

A tight BPM clock generator for Arduino and PlatformIO using hardware timer interruption. AVR, Teensy, STM32xx, ESP32 and RP2040 support
https://midilab.co/umodular
MIT License
153 stars 19 forks source link

external incoming clock ticks supported by uClock? #1

Closed m-r-m-s closed 3 years ago

m-r-m-s commented 3 years ago

@midilab can uClock be implemented to take incoming clock ticks from an external MIDI device and display the incoming BPM while passing on the clock ticks to another MIDI connected sound source? If so would you be able to share an example? Thanks!

midilab commented 3 years ago

Yes uClock is capable of receive sync, the whole hard work on that library is actually focus on external tigth sync.

Arduino uses UART interrupt on his core code, what makes it imposible to get this interrupt again from library or user code without change the arduino core code.

What i have made to overcome that problem was to get a 8bit timmer running at 1khz(1ms) and read data from uart RX(the place where you connect the external MIDI input). If there is a clock byte over ther you call uClock.clockMe();

Here it is a code i use to setup the timmer for 1ms, works for arduino leonardo too. its only meta code idea of what you need, depending on what library are you using for midi or pure UART arduino call, there will be different ways of check sync byte over there.


void setup() {

  // sets uClock for external clock
  uClock.setMode(1);
  setTimmer();
}

void setTimmer()
{
    uint8_t tmpSREG;

    // init action timmer
    tmpSREG = SREG;
    cli();
#if defined(__AVR_ATmega32U4__) 
    TCCR3A = 0;// set entire TCCR2A register to 0
    TCCR3B = 0;// same for TCCR2B
    TCNT3  = 0;//initialize counter value to 0
    // set compare match register for 1khz increments
    OCR3A = 1999;// = (16*10^6) / (1000*8) - 1
    // Set CS31 bits for 8 prescaler
    TCCR3B |= (1 << CS31);
    // turn on CTC mode
    TCCR3A |= (1 << WGM31);  
    // enable timer compare interrupt
    TIMSK3 |= (1 << OCIE3A);
#else
    TCCR2A = 0;// set entire TCCR2A register to 0
    TCCR2B = 0;// same for TCCR2B
    TCNT2  = 0;//initialize counter value to 0
    // set compare match register for 1khz increments
    OCR2A = 249;// = (16*10^6) / (1000*64) - 1 (must be <256)     
    // Set CS01 and CS00 bits for 64 prescaler
    TCCR2B |= (1 << CS21) | (1 << CS20);    
    // turn on CTC mode
    TCCR2A |= (1 << WGM21);  
    // enable timer compare interrupt
    TIMSK2 |= (1 << OCIE2A);
#endif  
    SREG = tmpSREG; 
}

#if defined(__AVR_ATmega32U4__) 
ISR(TIMER3_COMPA_vect, ISR_NOBLOCK) 
#else
ISR(TIMER2_COMPA_vect, ISR_NOBLOCK) 
#endif
{
   // read uart rx and check for MIDI sync byte.
   // if it is sync byte call uClock.clockMe();
}

I hope it helps, im about to release a new library called uCtrl, with all those things needed for common MIDI including out-of-the-box sync byte handle with no more than 2 lines of code. Those parts of code above are from that project.

m-r-m-s commented 3 years ago

Yes - thank you so much for explaining and providing this example. It is very helpful! I will look out for the upcoming uCtrl library. Is there any chance there will be Teensy support for your libraries, too?

midilab commented 3 years ago

It supports Teensy 2.0 that uses the same micro controller as arudino leonardo.

But its on my TODO list to order a teensy 3.6 to start playing with and code uClock support for it.

m-r-m-s commented 3 years ago

Ok great - I would like to support your efforts in development with the Teensy. Would it also then be possible to support the Teensy LC as well? I will send an email and thanks for all of your efforts so far!