One of the things the Arduino environment does very well is to provide a quick and easy path into the world of microcontrollers and embedded control. One way it achieves this is to try and hide away the complicated low-level parts of programming a microcontroller. When writing code for an Arduino you generally don’t need to worry about its inner workings, the bootloader configures your Arduino, and the built-in libraries and commands take care of the rest. But one down side to this is that there may be some useful parts of your Arduino hidden at the hardware level that you may not be aware of, or in some cases have direct access to. One of these is hardware interrupts.
What is an interrupt?
If you’re not sure what an interrupt is we can best explain it with an analogy. Say you wish to throw a dinner party and you are expecting your guest to arrive at any point. So, you keep checking the front door to see if any of your guests have arrived and between each check go back to preparing the food. Going back and forth from your main task and checking if something has happened isn’t a very efficient way to throw a party or indeed write a program. A hardware interrupt works like a door bell. You can simply go on with your main task, preparing the food for your guests, and not have to worry about checking the front door. As soon as a guest arrives they ring the doorbell. You can then stop what you are doing, let the guests in, then return back to exactly what you where doing before the doorbell rang. A hardware interrupt handles this for you automatically. You just tell the interrupt what piece of code needs to be run. When the interrupt is triggered it will jump to the code, run it, and then jump back to your main code without it ever knowing it was interrupted.
Microcontrollers can have various types of interrupts and the Arduino environment does give you some control. In particular it provides access to what is referred to as an external interrupt. These allow you to trigger an interrupt by changing the state of a digital pin. But there are also interrupts that are internal to your Arduino that it doesn’t give you direct access to. In this guide we are going to do a bit of hacking to gain access to one of these internal interrupts, called a timer interrupt, to do some clever multi-tasking tricks.
Most Arduino boards have three or more timer interrupts, but unfortunately they are all used by the Arduino environment for various functions:
Timer 0
This is used to do the actual timing for a lot of timing commands such as delay(), millis(), and micros() commands.
Timer 1
Most Arduinos use this for various servo functions.
Timer 2
Used for the tone() command to generate musical notes.
Some Arduino’s such as the Mega have more of these timer interrupts available and some of them are free to use, but if you have one of the more popular Arduinos such as an Uno or Nano, you’re stuck with just the above three. However, if we look at Timer number 2, it is only being used to generate musical notes. The majority of Arduino applications will never require this function, so this timer is a little wasted. If we can hack this timer and re-task it to our own needs we can make much better use of it.
Doing this requires the reconfiguration of some low level registers. This is much easier than it sounds, but it does require knowledge of what these registers do. Here is a brief explanation of the steps involved in reconfiguring these registers:
1) Turn off all interrupts as a precaution so we don’t accidentally trigger an interrupt ourselves or get interrupted by one.
cli();
2) Set the timer to automatically reset after it has been triggered.
TCCR2A = (1<<WGM21);
3) When enabled the timer will automatically count upwards. We can specify a value that it must reach before it triggers the interrupt. The timer will compare its counter against this value and when reached will trigger the interrupt. The higher the compare value the longer it takes the timer to count up to it and trigger the interrupt.
OCR2A = <A value between 0 and 255>;
4) To create a longer time delay before the interrupt is triggered we can slow down the speed at which the timer counts. The timer works directly off the main processor clock (usually 16MHz). For each tick of the main clock the timer counts up by one. But we can divide this main clock down or ‘prescale’ it. Valid amounts that we can divide the main clock by are 0, 8, 32, 64, 128, 256, 1024. So, for example, if we set the prescaler to 1024, the counter will only count up by one for every 1024 ticks of the main processor clock.
TCCR2B = <a valid value between 0 and 1024>;
5) We don’t know what state the timers counter register is in so lets reset it to zero:
TCNT2 = 0;
6) The timer is now set up so we can enable it. When we enable it we tell it that we want it to trigger every time its counter reaches the predetermined value we set in the OCR2A register.
7) Finally we re-enable all interrupts:
sei();
You may be able to see that by setting different values for the prescaler and compare values we change the amount of time it takes before the interrupt will trigger. Here is the equation for working this delay out:
[cpp]
((compare + 1) * prescaler) / 16MHz
[/cpp]
For example, if we set the compare register to 255 and the prescaler to 1024 we would have a time delay of:
((255 + 1) * 1024) / 16,000,000Hz = 0.016384 seconds or 16.384ms
So our interrupt code would get run once every 16ms or so. By changing these two values we can get the exact delay time we require.
Fortunately there’s no need to worry about setting all this up yourself as we have written a library which will do it all for you in the background.
You can download the library here and install it to your Arduino development environment as you would any other library.
To use this library you need to include it at the top of your sketch:
[cpp]
#include <HCTimer2.h>
[/cpp]
In the setup() section initialise it and give it a value for the prescaler and compare registers:
[cpp]
HCTimer2Init(PRESCALLER_VALUE, COMPARE_VALUE);
[/cpp]
For the PRESCALER_VALUE use one of these parameters:
T2_CLK_DIV_0
T2_CLK_DIV_8
T2_CLK_DIV_32
T2_CLK_DIV_64
T2_CLK_DIV_128
T2_CLK_DIV_256
T2_CLK_DIV_1024
For the compare value use a number between 0 and 255. But try to keep this number as high as possible.
Then just create a function called HCTimer2() and put any code you wish to run when the timer gets triggered in it:
[cpp]
void HCTimer2()
{
Your code here
}
[/cpp]
You can use your main loop() as normal. It will never know about your timer code running in the background. Here is an example sketch which demonstrates how to use the timer to create an alternative version of the blink sketch whilst leaving the main loop() function completely free for your own code:
[cpp]
/* FILE: HCTimer2_Example
DATE: 14/03/13
VERSION: 0.1
AUTHOR: Andrew Davies
You may copy, alter and reuse this code in any way you like, but please leave
reference to HobbyComponents.com in your comments if you redistribute this code.
This software may not be used directly for the purpose of selling products that
directly compete with Hobby Components Ltd’s own range of products.
THIS SOFTWARE IS PROVIDED "AS IS". HOBBY COMPONENTS MAKES NO WARRANTIES, WHETHER
EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ACCURACY OR LACK OF NEGLIGENCE.
HOBBY COMPONENTS SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR ANY DAMAGES,
INCLUDING, BUT NOT LIMITED TO, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY
REASON WHATSOEVER.
*/
This is an example of how to use the Hobby Components timer 2 interrupt library. This
library reprograms timer 2 for use as a timer interrupt for executing code at regular
intervals.
To initialise this library you will need call HCTimer2Init(prescaler, compare) once with
the following parameters:
The prescaler parameter sets the timer 2 clock prescaler. Valid values are
T2_CLK_DIV_0
T2_CLK_DIV_8
T2_CLK_DIV_32
T2_CLK_DIV_64
T2_CLK_DIV_128
T2_CLK_DIV_256
T2_CLK_DIV_1024
The compare parameter sets the value timer 2 compares its counter to before triggering
an interrupt. This is a 8 bit value from 0 to 255.
The two parameters can be used to alter the frequency of how often the timer interrupt
triggers and therefore runs your code. The formula to calculate the timer interrupt time
in seconds is: ((compare + 1) * prescaler) / 16000000MHz
To execute code each time the interrupt triggers, add the function HCTimer2() to your
program and place any code you wish to execute when the interrupt triggers within it.
Make sure that any code you place within this function is executed before the next
interrupt is due to be triggered. You must also not place any code inside this function
that requires the use of other interrupts such as the serial print functions.
You may copy, alter and reuse this code in any way you like but please leave
reference to hobbycomponents.com in your comments. This software may not be
used directly for the purpose of selling products that directly compete with
Hobby Components Ltd’s own range of products.
THIS SOFTWARE IS PROVIDED "AS IS". HOBBY COMPONENTS LTD MAKES NO WARRANTIES, WHETHER
EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ACCURACY OR LACK OF NEGLIGENCE.
HOBBY COMPONENTS LTD SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR ANY DAMAGES,
INCLUDING, BUT NOT LIMITED TO, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY
REASON WHATSOEVER.
*/
/* Include the HCTimer2 library */
#include <HCTimer2.h>
int Counter = 0;
void setup()
{
/* Set the DIO for pin 13 (LED L) to an output */
pinMode(13, OUTPUT);
/* Initialise the HCTimer2 library with a 15.616mS interval */
HCTimer2Init(T2_CLK_DIV_1024, 243);
}
void loop()
{
/* Place your normal code in here */
}
/* Place code inside this function that you would like to be executed
whenever the timer interrupt is triggered */
void HCTimer2()
{
Counter++;
/* Toggle DIO 13 (LED L) once every 64 * 15.616mS = approx 1 second */
if (Counter == 64)
{
digitalWrite(13,!digitalRead(13));
Counter = 0;
}
}
[/cpp]
Before we finish this tutorial there are a couple of things you need to keep in mind when using this timer function. Firstly, you should keep your code within the HCTimer2() function to a minimum. Whilst your Arduino is running code within the function it can’t do anything else. The longer it spends in this function the less time you have to run code in your main loop(). Secondly, whilst in this function you can’t use any commands that make use of other interrupts. The main one being the serial port functions – you can’t pass anything over the serial interface from within the HCTimer2() function.
To see how this library can be used to multi-task your sketches, look out for our follow-on guide:
Multi-tasking your Arduino (***Coming soon***)