Interrupts
On a very basic level, an interrupt is an signal that interrupts the current processor activity. It may be triggered by an external event (change in pin state) or an internal event (a timer or a software signal). Once triggered, an interrupt pauses the current activity and causes the program to execute a different function. This function is called an interrupt handler or an interrupt service routine (ISR). Once the function is completed, the program returns to what it was doing before the interrupt was triggered.
Interrupts are asynchronous events, that is, something that occurs outside of the regular flow of your program – it can happen at any time, no matter what your code is crunching on at the moment. This means that rather than manually checking whether your desired event has happened, you can let your AVR do the checking for you.
Two of the pins on the Arduino D2 and D3 can have interrupts attached to them. If these pins receive a signal in a special way, the Arduino's processor will suspend what it was doing and run the function attached to the interrupt.
The following program will detect when a pushbutton has been pressed, and perform an action based on that press.
Here’s our example circuit:
#include <avr/interrupt.h>
#define led ___
#define interruptPin 2
volatile int period=500;
//
void setup(){
sei(); // Enable global interrupts
EIMSK |= (1 << INT0); // Enable external interrupt INT0
pinMode(led, OUTPUT);
pinMode(interruptPin, INPUT);
digitalWrite(interruptPin, HIGH); // Enable pullup resistor
//parameter one specifies which interrupt pin
//parameter two specifies which function to call
//parameter three is either LOW, CHANGE, FALLING or RISING
attachInterrupt(0, goFast, FALLING);
}
void loop(){
digitalWrite(led,HIGH);
delay(period);
digitalWrite(led,LOW);
delay(period);
}
void goFast(){
period=100;
}
Timers
A timer is used to measure a given time interval. You can set a timer to trigger an interrupt at a certain point in the future. When that point arrives, you can use the interrupt as an alert, run different code, or change a pin output. Think of it as an alarm clock for your processor.
The beauty of timers is that just like external interrupts, they run asynchronously, or independently from your main program. Rather than running a loop or repeatedly calling
millis(), you can let a timer do that work for you while your code does other things.
Let's say you want your program to blink an LED every two seconds. Using normal code techniques, you’d have to set a variable with the next time the LED should blink, then check constantly to see if that time had arrived. With a timer interrupt, you can set up the interrupt, then turn on the timer. Your LED will blink perfectly on cue, even while your main program executes its other routine.
How do timers work?
Timers work by incrementing a counter variable, also known as a counter register. The counter register can count to a certain value, depending on its size. The timer increments this counter one step at a time until it reaches its maximum value, at which point the counter overflows, and resets back to zero. The timer normally sets a flag bit to let you know an overflow has occurred. You can check this flag manually, or you can also have the timer trigger an interrupt as soon as the flag is set. Like any other interrupt, you can specify an
Interrupt Service Routine (ISR) to run code of your choice when the timer overflows. The ISR will reset the overflow flag behind the scenes, so using interrupts is usually your best option for simplicity and speed.
In order to increment the counter value at regular intervals, the timer must have access to a clock source. The clock source generates a consistent repeating signal. Every time the timer detects this signal, it increases its counter by one.
Because timers are dependent on the clock source, the smallest measurable unit of time will be the period of this clock. For example, if you provide a 1 MHz clock signal to a timer, you can calculate your timer resolution (or timer period) as follows:
T = timer period, f = clock frequency
T = 1 / f
T = 1 / 1 MHz = 1 / 10^6 Hz
T = (1 * 10^-6) s
The timer resolution is one millionth of a second.
You can also supply an external clock source for use with timers, but in most cases the chip’s internal clock is used as the clock source. This means that your minimum timer resolution will be based on your processor speed (either 8 or 16 MHz for most 8-bit AVRs).
Types of timers
If you’re using any of the standard Arduino variants or an 8-bit AVR chip, you have several timers at your disposal.
The ATmega328 has three timers: Timer0, Timer1, and Timer2. They also have a watchdog timer, which can be used as a safeguard or a software reset mechanism. Here are a few details about each timer:
TIMER0
Timer0 is an 8-bit timer, meaning its counter register can record a maximum value of 255 (the same as an unsigned 8-bit byte). Timer0 is used by native Arduino timing functions such as delay() and millis(), so don’t mess with it unless you’re comfortable with the consequences.
TIMER1
Timer1 is a 16-bit timer, with a maximum counter value of 65535 (an unsigned 16-bit integer). The Arduino Servo library uses this timer, so be aware if you use it in your projects.
TIMER2
Timer2 is an 8-bit timer that is very similar to Timer0. It is utilized by the Arduino tone() function.
TIMER3, TIMER4, TIMER5
The AVR ATmega1280 and ATmega2560 (found in the Arduino Mega variants) have an additional three timers. These are all 16-bit timers, and function similarly to Timer1.
In order to use these timers, you need to set them up, then make them start running. To do this, use built-in registers on the AVR chip that store timer settings. Each timer has a number of registers that do various things. Two of these registers hold setup values, and are called TCCRxA and TCCRxB, where x is the timer number (TCCR1A and TCCR1B, etc.). TCCR stands for Timer/Counter Control Register. Each register holds 8 bits, and each bit stores a configuration value.
To start using your timer, the most important settings are the last three bits in TCCR1B, CS12, CS11, and CS10. These dictate the timer clock setting. By setting these bits in various combinations, you can tell the timer to run at different speeds.
// Arduino timer CTC interrupt example
// www.engblaze.com
// avr-libc library includes
#include <avr/io.h>
#include <avr/interrupt.h>
#define led 2
void setup(){
pinMode(led, OUTPUT);
// initialize Timer1
cli(); // disable global interrupts
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
// set compare match register to desired timer count:
OCR1A = 15624;
// turn on CTC mode:
TCCR1B |= (1 << WGM12);
// Set CS10 and CS12 bits for 1024 prescaler:
TCCR1B |= (1 << CS10);
TCCR1B |= (1 << CS12);
// enable timer compare interrupt:
TIMSK1 |= (1 << OCIE1A);
// enable global interrupts:
sei();
}
void loop(){
// do some crazy stuff while my LED keeps blinking
}
ISR(TIMER1_COMPA_vect){
digitalWrite(led, !digitalRead(led));
}