Tempo, BPM and timer interrupt

Hi all !

First post … 'cos I need help …
I’m trying to port some code from the oxobox project to the arduino in order to create a simple step sequencer.
I’m facing an issue regarding tempo management .
I have a timer overflow interrupt on TIMER1 that is in charge of managing the tempo for this project.
As arduino frequency CPU is 16 Mhz, maximum prescale is 1024 and TCNT1 is over 16 bits, it seems that the lower frequency I can get is 16 000 000 / 1024 / 65535, that is 0,238 Hz … (tell me if I’m wrong).

So that means I can have a timer overflow every 4 seconds ?

('cos it is not the case right now …).

Here is some code :

/* TIMER1 Overflow interrupt */

ISR(TIMER1_OVF_vect) {
TCNT1 = 0xFFFF;
do_tempo();
}

/* Tempo init */
void init_tempo(void) {

TCNT1 = 0xFFFF;
// Enable timer1 overflow interrupt
TIMSK1 = 1<<TOIE1;
}

/* do tempo */
void do_tempo(void) {
cli();
// do anything every 4ms
sei();
}

Why are you setting the timer1 counter in your ISR? I’m not sure if you realize, but TCNT1 counts up from 0 to 0xFFFF and then overflows back to 0 (it does not count down unless you put it in a special mode where it first counts up and then counts down) so by setting TCNT1 = 0xFFFF in your ISR, you’re putting the timer one tick away from overflowing again, which will jump you back to the ISR, which will put you one tick away from overflowing, etc. There is no need for you to touch TCNT1 in your ISR; the counter will just do its thing in hardware.

Also, you need to actually set the TCCR1B register for a prescaler of 1024 if you want to have an overflow every 4 seconds. If you want to process events less frequently than this, use a volatile global variable to count overflows and respond only when this variable passes a certain threshold.

Also, don’t call cli() and sei() from within your interrupt. Interrupts are disabled by default when you are in an ISR. Calling sei() to re-enable interrupts from within the ISR can have some pretty bad consequences if you don’t know what you’re doing.

  • Ben

Thanks ben.
So I would end up with this code :

/* TIMER1 Overflow interrupt */
ISR(TIMER1_OVF_vect) {
do_tempo();
}

/* Tempo init */
void init_tempo(void) {
TCCRB1 = 5; //1024
// Enable timer1 overflow interrupt
TIMSK1 = 1<<TOIE1;
}

/* do tempo */
void do_tempo(void) {

// do anything every 4s

}

One thing about cli() and sei() in the do_tempo function (not in the ISR) :
I wanted, the do_tempo not to be disturbed by any other interrupt that’s why I surronded code with cli() and sei().
Now, If I can do it, how could I be 100% sure that the do_tempo will never be disturb ?

PS : the above code do not make a call to do_tempo every 4 seconds … I’m lost !
PS2 : I tought that, for example, puting 100 in TCNT1 would force an overflow every 100 counts, is it not the case ?

TCCRB1 = 5; //1024

This should be TCCR1B, not TCCRB1. You probably discovered this already if you have tried to compile.

One thing about cli() and sei() in the do_tempo function (not in the ISR) :
I wanted, the do_tempo not to be disturbed by any other interrupt that’s why I surronded code with cli() and sei().
Now, If I can do it, how could I be 100% sure that the do_tempo will never be disturb ?

Do you ever plan on calling do_tempo() outside of the timer1 overflow ISR? If not, you can be sure it will never be disturbed as the timer1 overflow ISR is not interruptable (nor is anything that is called from that ISR because it is only returning from the ISR that re-enables interrupts). If you do call do_tempo() outside the ISR, just wrap the function call in cli() and sei():

void loop()
{

cli();
do_tempo();
sei();

}

PS : the above code do not make a call to do_tempo every 4 seconds … I’m lost !

The Arduino does some configuring of the hardware timers before your setup() routine is executed, so you might have to more explicitly undo these settings. Try adding:

TCCR1A = 0;

to the same place where you configure TCCR1B.

If this doesn’t work, please tell me how you are verifying functionality. Can you describe what behavior you’re seeing? Can you post your full sketch?

PS2 : I tought that, for example, puting 100 in TCNT1 would force an overflow every 100 counts, is it not the case ?

As I said, TCNT1 counts up, not down. Putting 100 in TCNT1 would force an overflow every 0x10000 - 100 counts (i.e. your counter would start with an initial value of 100 and would count up to 0xFFFF, at which point it would overflow to zero and generate an interrupt).

  • Ben

Thanks a lot ben !

Using TCCR1A = 0; solve the problem ! :wink:
And, thanks for this little course on TCNT* counter, 'cos now I get it ! and I’m ready to port the oxobox sequencer code :wink:
For the setup I have , for now, it is basically a serial-in parallel-out shift register that lights up 8 leds (8 steps for the sequencer). Also the shift register gets its output from the spi pin.
Tomorrow, I’ll make it 16 steps and will begin to use hardware interreput to modify the tempo from a rotary encoder.

Seriously, you save my day (my night in France actually :wink: )

PS : concerning the do_tempo function : yes it will be called from outside the ISR so I will follow your instruction and surround it with cli() and sei() when called (except in the ISR :wink: )

I’m glad to hear you have things working as expected now! Another option for you would be to have two versions of the do_tempo() function:

// call this version from inside any ISRs
void do_tempo_ISR()
{
// put your original do_tempo() code here and call this from inside the timer1 overflow ISR
}

// call this version from outside any ISRs
void do_tempo()
{
cli();
do_tempo_ISR();
sei();
}

This might keep the rest of your code simpler. Just a thought.

  • Ben