Button (midi) tempo using interupts CODE AVAILABLE

So I was thinking about a way to use a button to enter a “tempo” of an led flashing - this could in future be used for midi tempo setting. There is some code in another thread that advises on a way to do this, however it seemed very confusing to me so I wrote my own if anyone is interested AND i am interested to hear of any improvements people may suggest. It uses a button interupt to update the time between button clicks, and it uses a CTC hardware timer to flash the led. I think its pretty simple, and it may help others learn about interupts. I am using a Arduino 2560.
I am worried about what happens if the gap is too big between button presses and had thought that the prescaler could be adjusted on the fly. Maybe someone could advise on a good way to do that.
Please copy into your IDE’s it should run fine on the UNO and tell me what you reckon.

const int buttonPin = 2;     // Interupt 2 (pin 21) 
const int ledPin =  48;      // the number of the LED pin 
volatile int state = 0; 
volatile int tempoCount = 100;//start at 1Hz 
volatile int tempoCheck = tempoCount;
volatile int flag = 0;
static unsigned long last_interrupt_time = 0; 
unsigned long currentTimer[2] = { 
  0, 0 };  /* array of most recent tap counts */ 
unsigned int ISR_TIMER1_COUNT = 16000000/1024/100 - 1; //10ms timer

void setup() { 
  // initialize the LED pin as an output: 
  pinMode(ledPin, OUTPUT);      
  // initialize the pushbutton pin as an input: 
  pinMode(buttonPin, INPUT); 
  attachInterrupt(buttonPin, tempo, FALLING); 
  setupTimer1();     //calls the timer setup fun  
} 

void loop(){ 
    
  if (state == 1){ 
  flashLed();
    state = 0; 
  } 
} 
void flashLed(){
      digitalWrite(ledPin, HIGH);    //Toggles the led 
    delay(5); 
    digitalWrite(ledPin, LOW);    //Toggles the led 
}
ISR(TIMER1_COMPA_vect) 
{ 
flag++;
if (flag > tempoCount){
//tempoCheck = tempoCount;
state = 1; 
flag = 0;
}
} 


void tempo(){ 

  unsigned long interrupt_time = millis(); 
  // If interrupts come faster than 100ms, assume it's a bounce and ignore 
  if (interrupt_time - last_interrupt_time > 100) 
  { 
    flashLed();
    tap();
    
  } 
  last_interrupt_time = interrupt_time; 
} 

void tap() 
{ 
  currentTimer[0] = currentTimer[1]; 
  currentTimer[1] = millis(); 
    tempoCount  = (currentTimer[1] - currentTimer[0])/10; 
  
} 
void setupTimer1() 
{ 
//check data sheet to see what bits do what 
  TCCR1A &= ~(1<<COM1A1) & 
            ~(1<<COM1A0) & 
            ~(1<<COM1B1) & 
            ~(1<<COM1B0) & 
            ~(1<<FOC1A) & 
            ~(1<<FOC1B) & 
            ~(1<<WGM11) & 
            ~(1<<WGM10); 

  TCCR1B &= ~(1<<ICNC1) &    // Clearing bits 
            ~(1<<ICES1) & 
            ~(1<<WGM13) & 
            ~(1<<WGM12) & 
            ~(1<<CS12) & 
            ~(1<<CS11) & 
            ~(1<<CS10); 

//we require CTC mode therefore WGM12 -> 1, prescaler of 1024, therefore CS11 and CS10 -> 1. 
cli(); 
  TCCR1B |= (1<<WGM12) |    // Setting bits 
            (1<<CS12)  | 
            (1<<CS10); 
  
  OCR1A = ISR_TIMER1_COUNT; 

  // OCIE1A interrupt flag set 
  TIMSK1 |= (1<<OCIE1A); 
    sei();    // allow interrupts globally 
}

You can't get longer than 4 seconds can you? Even with the maximum prescaler of 1024.

62.5 nS * 1024 * 65536 = 4.194304 seconds.

For longer periods you could just set it to interrupt (say) every 100th of a second, and then count up until the required interval.

Yeah, but I dont know what the user will do when I compile the code, so assuming they wont press the button with gaps greater than 4 seconds, should I have conditions to adjust the prescaler, or just set it to 1024, and cast the value to an integer when I write to OCR1A?

OCR1A is 2 bytes anyway. Should make it unsigned int. It can't go negative.

I think I would leave it at 1024. After all that gives you a resolution of 64 uS.

nice call. made those adjustments to the above code. i doubt someone can press faster than 64uS and I will build in a condition to stop the value going over 4 seconds (tomorrow), so the range is 64us - 4 seconds. I think thats quite nice way of doing it?

Sounds good. Should debounce anyway, typically you debounce a switch for 10 mS, otherwise it might think they pressed it twice in 100 uS when it was just the switch bouncing.

I think I am debouncing:

void tempo(){
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  // If interrupts come faster than 50ms, assume it's a bounce and ignore
  if (interrupt_time - last_interrupt_time > 50)
  {
    tap();
  }
  last_interrupt_time = interrupt_time;
}

But is 50ms overkill? Do you think I can lower that to 10ms? then my range would be 10ms to 4 seconds?

P.S
I made some more changes to the above code - like building in a condition to stop the time going over 4 seconds - Im not sure its the neatest way of doing it (comments welcome), and I haven’t tested it but I think it should work,
Also changed the value that OCR1A gets set to at setup to be 1 Hz with a 1024 prescaler (15624).

I want to add in the MIDI beat clock to this program, so that it outputs the midi tempo signal. Im not sure if the MIDI.h has a function for this, however its just a case of sending the hex cod F8 (I think). I need to send it at 24*BPM which is going to make things a little tricky for setting up my timer. I was wondering if anyone has any ideas:

At 120BPM thats 2880 times per minute.
120BPM = 2 Hz, so 48 messages every second, i.e 48Hz.
with a prescaler of 1024, 16000000/1024/48 - 1 = 324.52083
with a prescaler of 256, 16000000/256/48 - 1 = 1301.083333
with a prescaler of 64, 16000000/64/48 - 1 = 5207.3333333
with a prescaler of 8, 16000000/8/48 - 1 = 41665.66666666

I.e no nice numbers. cast to an integer???

Ahh pause halt stop!!!
Been playing with the code, and everything seems good until I time two seconds between button presses - then the led seems to flash every 4 seconds!
Can someone please put a button on an interupt, and led on another pin and run the following code, leaving about 2 seconds between button presses and tell me if they experience the same thing. I am sure my registers are correct. The only thing I worry about is how I am setting the value of OCR1A (in tap()).

const int buttonPin = 2;     // the number of the pushbutton pin interupt
const int ledPin =  48;      // the number of the LED pin
volatile int state = 0;
volatile int tempo_change = 0;
static unsigned long last_interrupt_time = 0;
unsigned long currentTimer[2] = { 
  0, 0 };  /* array of most recent tap counts */
unsigned int ISR_TIMER1_COUNT = 15624; //1Hz

void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);      
  // initialize the pushbutton pin as an input:
  //pinMode(buttonPin, INPUT); 
  attachInterrupt(buttonPin, tempo, LOW);
  setupTimer1();     //calls the timer setup fun  
}

void loop(){

  if (tempo_change == 1){
    tap();
    tempo_change = 0;  
  }
  if (state == 1){
    digitalWrite(48, digitalRead(48)^1);    //Toggles the led
    delay(20);
    digitalWrite(48, digitalRead(48)^1);    //Toggles the led
    state = 0;
  }
}

void tempo(){

  unsigned long interrupt_time = millis();
  // If interrupts come faster than 100ms, assume it's a bounce and ignore
  if (interrupt_time - last_interrupt_time > 25)
  {
    tempo_change = 1;
  }
  last_interrupt_time = interrupt_time;
}

void tap()
{
  int tempo;
  /* we keep two of these around to average together later */
  currentTimer[0] = currentTimer[1];
  currentTimer[1] = millis();
    tempo  = currentTimer[1] - currentTimer[0];
  cli();
  ISR_TIMER1_COUNT = 16000000/1024/(1000/tempo) - 1;
  OCR1A = (int) ISR_TIMER1_COUNT;
  sei();
}

/* This interrupt service routine gets called once per second
REMEMBER NEVER DO ANYTHING COMPICATED IN AN INTERRUPT ROUTINE*/
ISR(TIMER1_COMPA_vect)
{
state = 1;
}

/* A function to set up the timers*/
void setupTimer1()
{
//check data sheet to see what bits do what
  TCCR1A &= ~(1<<COM1A1) & 
            ~(1<<COM1A0) &
            ~(1<<COM1B1) &
            ~(1<<COM1B0) &
            ~(1<<FOC1A) &
            ~(1<<FOC1B) &
            ~(1<<WGM11) &
            ~(1<<WGM10);

  TCCR1B &= ~(1<<ICNC1) &    // Clearing bits
            ~(1<<ICES1) &
            ~(1<<WGM13) &
            ~(1<<WGM12) &
            ~(1<<CS12) &
            ~(1<<CS11) & 
            ~(1<<CS10); 

//we require CTC mode therefore WGM12 -> 1, prescaler of 1024, therefore CS11 and CS10 -> 1.
  TCCR1B |= (1<<WGM12) |    // Setting bits
            (1<<CS12)  |
            (1<<CS10);
 
  OCR1A = ISR_TIMER1_COUNT;

  // OCIE1A interrupt flag set
  TIMSK1 |= (1<<OCIE1A);
    sei();    // allow interrupts globally
}

Thank you

I tried it, but it is not working at all reliably. How do you have the switch wired up? Any pull-up resistors?


const int buttonPin = 2;     // the number of the pushbutton pin interupt

That is confusing. It is not pin 2, it is interrupt 2.


digitalWrite(48, digitalRead(48)^1);    //Toggles the led

Why not:

digitalWrite(ledPin, digitalRead(ledPin)^1);    //Toggles the led

attachInterrupt(buttonPin, tempo, LOW);

You don't want a LOW interrupt, believe me. You want a FALLING one. The LOW interrupt will interrupt continuously while the switch is down.


I tap the button and get random results, basically. The LED does not flash at all at the rate I tap the button.

Thanks Nick for trying that. - I forgot about a pull-up resistor - I can use an internal one though can't I. Did you try it with a pull up resistor? - yeah sorry, interrupt 2, not pin 2... :roll_eyes: - I dunno why 48 and not ledPin - should be ledPin.. - OK I'll change to FALLING. - Well I dont get completely RANDOM results, but anything over about a second seems to always be ~4 seconds. - I'll make your changes this evening, and get back to you. Thanks for checking that for me.

a.mlw.walker: - I forgot about a pull-up resistor - I can use an internal one though can't I. Did you try it with a pull up resistor?

I just set the internal pull-up. Otherwise the results would be unpredictable.

OK I think I fixed it. I have updated the original code in my Very first post. Basically instead of changing the value of OCR1A I set this to a constant 10ms. Then I use a flag that increases each time 10ms passes, and compare that to an integer, when it is greater than it, the led flashes. It is that integer that changes now based upon the button press. I think it is much more reliable now... If you have a chance, could you try it again nick, and make any further comments. Thanks Alex

I have updated the original code in my Very first post.

In the future, please do not do that. If someone comes along later looking at this thread, the comments in the replies don't match the code in the first post, making all the people that tried to help look like idiots.

Post the amended code in a new reply, rather than overwriting the original code.

Sorry, I can probably revert it if you would like me to - I have the old code. Give me the go ahead if so.

Sorry, I can probably revert it if you would like me to - I have the old code. Give me the go ahead if so.

I think it would be better if you did. That way, the progression from non-working code to working code can be seen, and the context of the replies is clear.