Arduino Timer Interrupts by amandaghassaei
Featured
title.jpg
Timer interrupts allow you to perform a task at very specifically timed intervals regardless of what else is going on in your code.  In this instructable I'll explain how to setup and execute an interrupt in Clear Timer on Compare Match or CTC Mode.  Jump straight to step 2 if you are looking for sample code.

Normally when you write an Arduino sketch the Arduino performs all the commands encapsulated in the loop() {} function in the order that they are written, however, it's difficult to time events in the loop().  Some commands take longer than others to execute, some depend on conditional statements (if, while...) and some Arduino library functions (like digitalWrite or analogRead) are made up of many commands.  Arduino timer interrupts allow you to momentarily pause the normal sequence of events taking place in the loop() function at precisely timed intervals, while you execute a separate set of commands.  Once these commands are done the Arduino picks up again where it was in the loop().

Interrupts are useful for:

Measuring an incoming signal at equally spaced intervals (constant sampling frequency)
Calculating the time between two events
Sending out a signal of a specific frequency
Periodically checking for incoming serial data
much more...

There are a few ways to do interrupts, for now I'll focus on the type that I find the most useful/flexible, called Clear Timer on Compare Match or CTC Mode.  Additionally, in this instructable I'll be writing specifically about the timers to the Arduino Uno (and any other Arduino with ATMEL 328/168... Lilypad, Duemilanove, Diecimila, Nano...).  The main ideas presented here apply to the Mega and older boards as well, but the setup is a little different and the table below is specific to ATMEL 328/168.
 
Remove these adsRemove these ads by Signing Up

Step 1: Prescalers and the Compare Match Register

The Uno has three timers called timer0, timer1, and timer2.  Each of the timers has a counter that is incremented on each tick of the timer's clock.  CTC timer interrupts are triggered when the counter reaches a specified value, stored in the compare match register.  Once a timer counter reaches this value it will clear (reset to zero) on the next tick of the timer's clock, then it will continue to count up to the compare match value again.  By choosing the compare match value and setting the speed at which the timer increments the counter, you can control the frequency of timer interrupts.

The first parameter I'll discuss is the speed at which the timer increments the counter.  The Arduino clock runs at 16MHz, this is the fastest speed that the timers can increment their counters.  At 16MHz each tick of the counter represents 1/16,000,000 of a second (~63ns), so a counter will take 10/16,000,000 seconds to reach a value of 9 (counters are 0 indexed), and 100/16,000,000 seconds to reach a value of 99.

In many situations, you will find that setting the counter speed to 16MHz is too fast.  Timer0 and timer2 are 8 bit timers, meaning they can store a maximum counter value of 255.  Timer1 is a 16 bit timer, meaning it can store a maximum counter value of 65535.  Once a counter reaches its maximum, it will tick back to zero (this is called overflow).  This means at 16MHz, even if we set the compare match register to the max counter value, interrupts will occur every 256/16,000,000 seconds (~16us) for the 8 bit counters, and every 65,536/16,000,000 (~4 ms) seconds for the 16 bit counter.  Clearly, this is not very useful if you only want to interrupt once a second.

Instead you can control the speed of the timer counter incrementation by using something called a prescaler.  A prescaler dictates the speed of your timer according the the following equation:

(timer speed (Hz)) = (Arduino clock speed (16MHz)) / prescaler

So a 1 prescaler will increment the counter at 16MHz, an 8 prescaler will increment it at 2MHz, a 64 prescaler = 250kHz, and so on.  As indicated in the tables above, the prescaler can equal 1, 8, 64, 256, and 1024.  (I'll explain the meaning of CS12, CS11, and CS10 in the next step.) 

Now you can calculate the interrupt frequency with the following equation:

interrupt frequency (Hz) = (Arduino clock speed 16,000,000Hz) / (prescaler * (compare match register + 1))
the +1 is in there because the compare match register is zero indexed

rearranging the equation above, you can solve for the compare match register value that will give your desired interrupt frequency:

compare match register = [ 16,000,000Hz/ (prescaler * desired interrupt frequency) ] - 1
remember that when you use timers 0 and 2 this number must be less than 256, and less than 65536 for timer1

so if you wanted an interrupt every second (frequency of 1Hz):
compare match register = [16,000,000 / (prescaler * 1) ] -1
with a prescaler of 1024 you get:
compare match register = [16,000,000 / (1024 * 1) ] -1
= 15,624
since 256 < 15,624 < 65,536, you must use timer1 for this interrupt.
maxx-on says: Mar 29, 2013. 1:15 PM
Oh, I see. I can't reply to a message because there is no reCAPTCHA under the reply box, not because the reCAPTCHA isn't working. It works fine if I create a new post. Someone should fix this. I'm using Firefox 19.0.2 if that make a difference.
maxx-on says: Mar 29, 2013. 1:02 PM
Yeah, I know I could do that. I could also put in a function pointer into my interrupt handler, I was just wondering if it were possible to drop that overhead and go directly to the interrupt handler of my choice.

P.S. This reCAPTCHA stuff is garbage.:( I've put in several words that I'm absolutely sure are what they should be and it keeps saying "Please type the two words as seen on image"
maxx-on says: Mar 9, 2013. 2:59 PM
Can you only define one interrupt handler per interrupt? Can you not define multiple ones and switch it from one to another depending on circumstance?
amandaghassaei (author) says: Mar 28, 2013. 10:58 PM
put and if then statement inside the interrupt
mertg says: Mar 4, 2013. 1:14 AM
Awesome. Thanks. This helped me a lot. I was struggling to understand timers in pic. This instructable made my mind clear. Also with arduino.
amandaghassaei (author) says: Mar 28, 2013. 10:57 PM
thanks!
FieldingBlue says: Feb 9, 2013. 2:34 PM
A nice overview of timer interrupts, thank you for taking the time to write the article. And thank you to those who commented with helpful feedback.
rsellens says: Jan 6, 2013. 12:59 PM
Nice work! One detail:

timer2 uses a prescale that runs 1, 8, 32, 64, 128, 256, 1024 for the 001 through 111 bit settings. This is different from the 1, 8, 64, 256, 1024, Ext Falling, Ext Rising prescale values for timer0 and timer1 from the table you showed.

All three work the same for 010 giving a prescale of 8, as used in your examples, but slower timer2 interrupt frequencies will give different results.
amandaghassaei (author) says: Jan 7, 2013. 2:33 PM
that's interesting, I didn't know that. I'll update that step. thanks for the tip!
kbeharee says: Oct 19, 2012. 2:39 AM
Awesome instructable! I have one question though.. You use CS10 CS11 CS12 for timer 1 thats okay but for the other timers eg timer 2 should we use CS20 CS21 CS22 for prescalers rather than CS10 11 12 ?

Thanks.
amandaghassaei (author) says: Oct 19, 2012. 9:48 AM
good question! no it's always cs10 cs11 cs12. In the code in step 2 you can find an example of how to set up each timer interrupt, they all use cs10 cs11 and/or cs12. to decide which bits you need to set for a certain presclaer (all of them, one or two of them, etc) you use the table in step 2 (fig 4).
kbeharee says: Oct 19, 2012. 12:17 PM
Thanks alot for your prompt reply!
bilbolodz says: Sep 18, 2012. 2:07 PM
Hi,

My problem is:
External signal (via interrupt) after 6ms delay should trigger some actions (call procedure). Each external signal during "counting delay" should reset counting to start. I've already INT0 procedure but now I've program "delay counter". I thing that best idea is to use timers but how to do it? I've Arduino Mego so I've plenty of "free timers" ;-)

{optr
amandaghassaei (author) says: Sep 18, 2012. 2:29 PM
can you post your code? at least the interrupt routine and the timer setup. Are you using the delay() function? because it might act strangely depending on how you've set up your timers.
bilbolodz says: Sep 19, 2012. 12:17 AM
It's quite big program so it's hard to post all code but "main function" are like this:

#define IRQ_1 19
volatile boolean interrupt_1=false;

void irq1_proc(void)
{
interrupt_1=true;
//Serve time critical interrupt stuff
//reset timer (hot do to it?)
}

void timer_proc()
{
//delay passed do something because there wasn't interrupt longer then 6ms
}

void setup()
{
[..]
pinMode (IRQ_1,INPUT);
digitalWrite (IRQ_1,HIGH);
attachInterrupt(4,irq1_proc,FALLING) ;

//Timer setup (how to do it)
}

void loop(void)
{
//Do some usual stuff
if (interrupt_1 == true) {
interrupt_1 = false;
//do some staff when was interrupt but not time critical
}
}

I'm not using delay() because it's real time system it has external time critical devices.
bilbolodz says: Sep 20, 2012. 12:58 PM
OK, If someone interested I've found solution:
delays longer than 9ms between pulses on pin 19 release timer interrupt,
----
#define IRQ_1 19

volatile boolean interrupt_1=false;

void irq1_proc(void)
{
interrupt_1=true;
//Serve time critical interrupt stuff

//reset timer
reset_timer();
}

ISR(TIMER5_COMPA_vect)
{
//delay passed do something because there wasn't pulse longer then 9ms}
}

void init_timer()
{
  cli();          // disable global interrupts
  TCCR5A = 0;     // set entire TCCR1A register to 0
  TCCR5B = 0;     // same for TCCR1B

  // set compare match register to desired timer count:
  OCR5A = 624; //10 ms
  // turn on CTC mode:
  TCCR5B |= (1 << WGM12);
  // Set CS12 bits for 256 prescaler:
  TCCR5B |= (1 << CS12);
  // enable timer compare interrupt:
  TIMSK5 |= (1 << OCIE5A);
  // enable global interrupts:
  sei();
}
void reset_timer()
{
  cli();          // disable global interrupts - probably not needed because we are in IRQ1 procedure so interrupts are already disabled????
  TCNT5=0;
  sei();        // enable global interrupts:
}

void setup() {
[..] pinMode (IRQ_1,INPUT);
digitalWrite (IRQ_1,HIGH);
attachInterrupt(4,irq1_proc,FALLING) ;
//Initialize timer5
init_timer();
}
void loop(void)
{
//Do some usual stuff
if (interrupt_1 == true)
{
interrupt_1 = false;
//do some staff when was interrupt but not time critical
}
}
halamka says: Jul 17, 2012. 4:56 PM
sounds like another episode of my name is earl. can you help build a commodore computer. wait a second . well get someone who does not complicate things.
Build_it_Bob says: Jul 16, 2012. 4:09 PM
Very nicely done ! I may venture forth and attempt to use the elusive interrupt for the Arduino , thanks to your explanation and demonstration.
Greatly appreciate you work,
Build_it_Bob
amandaghassaei (author) says: Jul 16, 2012. 4:32 PM
thanks!
westfw says: Jul 11, 2012. 2:37 AM
You ought to clarify that the Arduino core normally uses timer0 to provide the millis() clock, and all three timers to provide the PWM "analogWrite()" function. Setting up timer interrupts as described here will break whatever functionality was already using those timers (note that this is frequently OK.)
amandaghassaei (author) says: Jul 11, 2012. 8:43 PM
good point. delay() would be affected as well. I'll add this in when I get the chance, for now I'll just post this.
thanks!
dougstrickland says: Jul 15, 2012. 11:08 AM
Would that interfere with millis() also?
amandaghassaei (author) says: Jul 15, 2012. 11:13 AM
yes, but if you really need something like millis(), you could set it up yourself using another of the timers. Check out the bike speedometer example to see how that might work.
Xellers says: Jul 15, 2012. 6:27 AM
Is there a way to branch to a new ISR during execution of a previously triggered ISR the moment the new interrupt source is triggered using ISR_NOBLOCK, but then not branch back to the original ISR?
amandaghassaei (author) says: Jul 15, 2012. 10:01 AM
not sure, tell me more about what you're trying to do.
cefn says: Jul 15, 2012. 7:46 AM
A great instructable. Really clear and relevant. Some comments on the interrupt code you've chosen, though.

The use of digitalWrite(...) certainly helps to make things comprehensible, but as described at http://billgrundmann.wordpress.com/2009/03/03/to-use-or-not-use-writedigital/ there's a big overhead from using it, which is worth noting.

There is a similar issue with analogRead(...) as described at http://arduino.cc/en/Reference/analogRead meaning it takes 1/10000 of a second to complete. Given that you are using a reed switch, I don't really know why digitalRead(...) is not used.

Thinking about the computational overhead and delays of code inside interrupts is particularly important when it comes to interrupt code because of the way interrupts just jump in and grab the CPU whilst other things are trying to do something else in the loop() function. By avoiding overheads, it's possible to minimise the side-effects on other processes which are expecting a certain amount of available CPU time or which make other assumptions about timing.

In interrupt code I'd be looking to avoid either of these function invocations where possible, and in the cases you have talked us through, I think they are indeed avoidable.
amandaghassaei (author) says: Jul 15, 2012. 10:01 AM
thanks for the comments.

yes, agreed about the digitalWrite stuff, I wrote it that way bc I wanted this code to be easily understood by people who are not familiar with all the port/pin stuff. But yes, when using interrupts (esp high frequency ones), you want to keep the interrupt routine as short as possible, and it may be worth addressing the ports/pins of the atmel chip directly.

You're right about my use of analogRead(), this was a remnant from when I was first prototyping the thing, but I've switched it over to digitalRead() now.
loneconspirator says: Jul 15, 2012. 9:59 AM
Great instructable, I've been meaning to try out interrupts ina project of mine and now I feel very equipped, thanks!
Jason Bedard says: Jul 9, 2012. 6:04 AM
Very well explained. Thank you.
arnefl says: Jul 9, 2012. 4:35 AM
Cool. Thank you!
Pro

Get More Out of Instructables

Already have an Account?

close

PDF Downloads
As a Pro member, you will gain access to download any Instructable in the PDF format. You also have the ability to customize your PDF download.

Upgrade to Pro today!