Arduino Timer and Interrup
Arduino Timer and Interrup
This tutorial shows the use of timers and interrupts for Arduino boards. As Arduino
programmer you will have used timers and interrupts without knowledge, bcause all the
low level hardware stuff is hidden by the Arduino API. Many Arduino functions uses timers,
for example the time functions: delay(), millis() and micros() and delayMicroseconds().
The PWM functions analogWrite() uses timers, as the tone() and the noTone() function
does. Even the Servo library uses timers and interrupts.
What is a timer?
A timer or to be more precise a timer / counter is a piece of hardware builtin the Arduino
controller (other controllers have timer hardware, too). It is like a clock, and can be used
to measure time events.
The timer can be programmed by some special registers. You can configure the prescaler
for the timer, or the mode of operation and many other things.
The controller of the Arduino is the Atmel AVR ATmega168 or the ATmega328. These
chips are pin compatible and only differ in the size of internal memory. Both have 3 timers,
called timer0, timer1 and timer2. Timer0 and timer2 are 8bit timer, where timer1 is a 16bit
timer. The most important difference between 8bit and 16bit timer is the timer resolution.
8bits means 256 values where 16bit means 65536 values for higher resolution.
The controller for the Arduino Mega series is the Atmel AVR ATmega1280 or the
ATmega2560. Also identical only differs in memory size. These controllers have 6 timers.
Timer 0, timer1 and timer2 are identical to the ATmega168/328. The timer3, timer4 and
timer5 are all 16bit timers, similar to timer1.
All timers depends on the system clock of your Arduino system. Normally the system clock
is 16MHz, but for the Arduino Pro 3,3V it is 8Mhz. So be careful when writing your own
timer functions.
The timer hardware can be configured with some special timer registers. In the Arduino
firmware all timers were configured to a 1kHz frequency and interrupts are gerally enabled.
Timer0:
Timer0 is a 8bit timer.
In the Arduino world timer0 is been used for the timer functions, like delay(), millis() and
micros(). If you change timer0 registers, this may influence the Arduino timer function. So
you should know what you are doing.
Timer1:
Timer1 is a 16bit timer.
In the Arduino world the Servo library uses timer1 on Arduino Uno (timer5 on Arduino
Mega).
Timer2:
Timer2 is a 8bit timer like timer0.
In the Arduino work the tone() function uses timer2.
Timer Register
You can change the Timer behaviour through the timer register. The most important timer
registers are:
TCCRx - Timer/Counter Control Register. The prescaler can be configured here.
Different clock sources can be selected for each timer independently. To calculate the timer frequency (for example
2Hz using timer1) you will need:
1. CPU frequency 16Mhz for Arduino
2. maximum timer counter value (256 for 8bit, 65536 for 16bit timer)
3. Divide CPU frequency through the choosen prescaler (16000000 / 256 = 62500)
4. Divide result through the desired frequency (62500 / 2Hz = 31250)
5. Verify the result against the maximum timer counter value (31250 < 65536 success) if fail, choose bigger prescaler.
Timer modes
PWM mode. Pulth width modulation mode. the OCxy outputs are used to generate PWM signals
CTC mode. Clear timer on compare match. When the timer counter reaches the compare match register, the timer
will be cleared
What is an interrupt?
The program running on a controller is normally running sequentially instruction by
instruction. An interrupt is an external event that interrupts the running program and runs a
special interrupt service routine (ISR). After the ISR has been finished, the running
program is continued with the next instruction. Instruction means a single machine
instruction, not a line of C or C++ code.
Before an pending interrupt will be able to call a ISR the following conditions must be true:
Interrupts can generally enabled / disabled with the function interrupts() / noInterrupts(). By
default in the Arduino firmware interrupts are enabled. Interrupt masks are enabled /
disabled by setting / clearing bits in the Interrupt mask register (TIMSKx).
When an interrupt occurs, a flag in the interrupt flag register (TIFRx) is been set. This
interrupt will be automatically cleared when entering the ISR or by manually clearing the bit
in the interrupt flag register.
The Arduino functions attachInterrupt() and detachInterrupt() can only be used for external
interrupt pins. These are different interrupt sources, not discussed here.
Timer interrupts
A timer can generate different types of interrupts. The register and bit definitions can be
found in the processor data sheet (Atmega328 or Atmega2560) and in the I/O definition
header file (iomx8.h for Arduino, iomxx0_1.h for Arduino Mega in the
hardware/tools/avr/include/avr folder). The suffix x stands for the timer number (0..5), the
suffix y stands for the output number (A,B,C), for example TIMSK1 (timer1 interrupt mask
register) or OCR2A (timer2 output compare register A).
Timer Overflow:
Timer overflow means the timer has reached is limit value. When a timer overflow interrupt
occurs, the timer overflow bit TOVx will be set in the interrupt flag register TIFRx. When
the timer overflow interrupt enable bit TOIEx in the interrupt mask register TIMSKx is set,
the timer overflow interrupt service routine ISR(TIMERx_OVF_vect) will be called.
Ken Shirrifs IR library. Using timer2. Send and receive any kind of IR remote signals like RC5, RC6, Son
Timer1 and Timer3 library. Using timer1 or tiner3. The easy way to write your own timer interupt service
routines.
I have ported these libraries to different timers for Arduino Mega. All ported libraries can be
found in the attached file.
Pitfalls
There exists some pitfalls you may encounter, when programming your Arduino and make
use of functions or libraries that uses timers.
Servo Library uses Timer1. You can’t use PWM on Pin 9, 10 when you use the Servo Library on an Ardu
For Arduino Mega it is a bit more difficult. The timer needed depends on the number of servos. Each tim
can handle 12 servos. For the first 12 servos timer 5 will be used (loosing PWM on Pin 44,45,46). For 24
Servos timer 5 and 1 will be used (loosing PWM on Pin 11,12,44,45,46).. For 36 servos timer 5, 1 and 3
be used (loosing PWM on Pin 2,3,5,11,12,44,45,46).. For 48 servos all 16bit timers 5,1,3 and 4 will be u
(loosing all PWM pins).
Pin 11 has shared functionality PWM and MOSI. MOSI is needed for the SPI interface, You can’t use P
on Pin 11 and the SPI interface at the same time on Arduino. On the Arduino Mega the SPI pins are on
different pins.
tone() function uses at least timer2. You can’t use PWM on Pin 3,11 when you use the tone() function an
Arduino and Pin 9,10 on Arduino Mega.
Examples
The first example uses the timer1 in CTC mode and the compare match interrupt to toggle
a LED. The timer is configured for a frequency of 2Hz. The LED is toggled in the interrupt
service routine.
/* Arduino 101: timer and interrupts
1: Timer1 compare match interrupt example
more infos: http://www.letmakerobots.com/node/28278
created by RobotFreak
*/
#define ledPin 13
void setup()
{
pinMode(ledPin, OUTPUT);
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
void loop()
{
// your program here...
}
same example like before but now we use the timer overflow interrupt. In this case timer1
is running in normal mode.
The timer must be preloaded every time in the interrupt service routine.
/*
* Arduino 101: timer and interrupts
* 2: Timer1 overflow interrupt example
* more infos: http://www.letmakerobots.com/node/28278
* created by RobotFreak
*/
#define ledPin 13
void setup()
{
pinMode(ledPin, OUTPUT);
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
void loop()
{
// your program here...
}
The next example is part of my Ardubot project. It uses timer2 and the compare match
interrupt to read the encoder inputs. Timer2 is initialized by default to a frequency of 1kHz
(1ms period). In the interrupt service routine the state of all encoder pins is read and a
state machine is used to eliminate false readings. Using the timer interrupt is much easier
to handle than using 4 input change interrupts.
The signals of a quadrature encoder is a 2bit Gray code. Only 1 bit is changing from state to state. A state machine
is perfect to check the signal and count the encoder ticks. The timer must be fast enough, to recognize each state
change. For the Pololu wheel encoders used here, the 1ms timer is fast enough.
The following example has been modified to work with Arduino V1.x
/*
/*
* Arduino 101: timer and interrupts
* 3: Timer2 compare interrupt example. Quadrature Encoder
* more infos: http://www.letmakerobots.com/node/28278
* created by RobotFreak
*
* Credits:
* based on code from Peter Dannegger
* http://www.mikrocontroller.net/articles/Drehgeber
*/
// Encoder Pins
#define encLtA 2
#define encLtB 3
#define encRtA 11
#define encRtB 12
#define ledPin 13
val = 0;
if( RT_PHASE_A )
val = 3;
if( RT_PHASE_B )
val ^= 1; // convert gray to binary
diff = lastRt - val; // difference last - new
if( diff & 1 ){ // bit 0 = value (1)
lastRt = val; // store new as next last
encDeltaRt += (diff & 2) - 1; // bit 1 = direction (+/-)
}
digitalWrite(ledPin, LOW); // toggle LED pin
}
void QuadratureEncoderInit(void)
{
int8_t val;
cli();
TIMSK2 |= (1<<OCIE2A);
sei();
pinMode(encLtA, INPUT);
pinMode(encRtA, INPUT);
pinMode(encLtB, INPUT);
pinMode(encRtB, INPUT);
val=0;
if (LT_PHASE_A)
val = 3;
if (LT_PHASE_B)
val ^= 1;
lastLt = val;
encDeltaLt = 0;
val=0;
if (RT_PHASE_A)
val = 3;
if (RT_PHASE_B)
val ^= 1;
lastRt = val;
encDeltaRt = 0;
encLt = 0;
encRt = 0;
}
cli();
val = encDeltaLt;
encDeltaLt = 0;
sei();
return val; // counts since last call
}
cli();
val = encDeltaRt;
encDeltaRt = 0;
sei();
return val; // counts since last call
}
void setup()
{
Serial.begin(38400);
pinMode(ledPin, OUTPUT);
QuadratureEncoderInit();
}
void loop()
{
encLt += QuadratureEncoderReadLt();
encRt += QuadratureEncoderReadRt();
Serial.print("Lt: ");
Serial.print(encLt, DEC);
Serial.print(" Rt: ");
Serial.println(encRt, DEC);
delay(1000);
}