//timer interrupts //by Amanda Ghassaei //June 2012 //http://www.instructables.com/id/Arduino-Timer-Interrupts/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * */ //timer setup for timer0, timer1, and timer2. //For arduino uno or any board with ATMEL 328/168.. diecimila, duemilanove, lilypad, nano, mini... //this code will enable all three arduino timer interrupts. //timer0 will interrupt at 2kHz //timer1 will interrupt at 1Hz //timer2 will interrupt at 8kHz //storage variables boolean toggle0 = 0; boolean toggle1 = 0; boolean toggle2 = 0; void setup(){ //set pins as outputs pinMode(8, OUTPUT); pinMode(9, OUTPUT); pinMode(13, OUTPUT); cli();//stop interrupts //set timer0 interrupt at 2kHz TCCR0A = 0;// set entire TCCR2A register to 0 TCCR0B = 0;// same for TCCR2B TCNT0 = 0;//initialize counter value to 0 // set compare match register for 2khz increments OCR0A = 124;// = (16*10^6) / (2000*64) - 1 (must be <256) // turn on CTC mode TCCR0A |= (1 << WGM01); // Set CS01 and CS00 bits for 64 prescaler TCCR0B |= (1 << CS01) | (1 << CS00); // enable timer compare interrupt TIMSK0 |= (1 << OCIE0A); //set timer1 interrupt at 1Hz TCCR1A = 0;// set entire TCCR1A register to 0 TCCR1B = 0;// same for TCCR1B TCNT1 = 0;//initialize counter value to 0 // set compare match register for 1hz increments OCR1A = 15624;// = (16*10^6) / (1*1024) - 1 (must be <65536) // turn on CTC mode TCCR1B |= (1 << WGM12); // Set CS12 and CS10 bits for 1024 prescaler TCCR1B |= (1 << CS12) | (1 << CS10); // enable timer compare interrupt TIMSK1 |= (1 << OCIE1A); //set timer2 interrupt at 8kHz TCCR2A = 0;// set entire TCCR2A register to 0 TCCR2B = 0;// same for TCCR2B TCNT2 = 0;//initialize counter value to 0 // set compare match register for 8khz increments OCR2A = 249;// = (16*10^6) / (8000*8) - 1 (must be <256) // turn on CTC mode TCCR2A |= (1 << WGM21); // Set CS21 bit for 8 prescaler TCCR2B |= (1 << CS21); // enable timer compare interrupt TIMSK2 |= (1 << OCIE2A); sei();//allow interrupts }//end setup ISR(TIMER0_COMPA_vect){//timer0 interrupt 2kHz toggles pin 8 //generates pulse wave of frequency 2kHz/2 = 1kHz (takes two cycles for full wave- toggle high then toggle low) if (toggle0){ digitalWrite(8,HIGH); toggle0 = 0; } else{ digitalWrite(8,LOW); toggle0 = 1; } } ISR(TIMER1_COMPA_vect){//timer1 interrupt 1Hz toggles pin 13 (LED) //generates pulse wave of frequency 1Hz/2 = 0.5kHz (takes two cycles for full wave- toggle high then toggle low) if (toggle1){ digitalWrite(13,HIGH); toggle1 = 0; } else{ digitalWrite(13,LOW); toggle1 = 1; } } ISR(TIMER2_COMPA_vect){//timer1 interrupt 8kHz toggles pin 9 //generates pulse wave of frequency 8kHz/2 = 4kHz (takes two cycles for full wave- toggle high then toggle low) if (toggle2){ digitalWrite(9,HIGH); toggle2 = 0; } else{ digitalWrite(9,LOW); toggle2 = 1; } } void loop(){ //do other things here }
The images above show the outputs from these timer interrupts. Fig 1 shows a square wave oscillating between 0 and 5V at 1kHz (timer0 interrupt), fig 2 shows the LED attached to pin 13 turning on for one second then turning off for one second (timer1 interrupt), fig 3 shows a pulse wave oscillating between 0 and 5V at a frequency of 4khz (timer2 interrupt).
Step 3: Example 1: Bike Speedometer
In this example I made an arduino powered bike speedometer. It works by attaching a magnet to the wheel and measuring the amount of time it takes to pass by a magnetic switch mounted on the frame- the time for one complete rotation of the wheel.
I set timer 1 to interrupt every ms (frequency of 1kHz) to measure the magnetic switch. If the magnet is passing by the switch, the signal from the switch is high and the variable "time" gets set to zero. If the magnet is not near the switch "time" gets incremented by 1. This way "time" is actually just a measurement of the amount of time in milliseconds that has passed since the magnet last passed by the magnetic switch. This info is used later in the code to calculate rpm and mph of the bike.
Here's the bit of code that sets up timer1 for 1kHz interrupts
cli();//stop interrupts
//set timer1 interrupt at 1kHz
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;//initialize counter value to 0
// set timer count for 1khz increments
OCR1A = 1999;// = (16*10^6) / (1000*8) - 1
//had to use 16 bit timer1 for this bc 1999>255, but could switch to timers 0 or 2 with larger prescaler
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS11 bit for 8 prescaler
TCCR1B |= (1 << CS11);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei();//allow interrupts
Here's the complete code if you want to take a look:
I set timer 1 to interrupt every ms (frequency of 1kHz) to measure the magnetic switch. If the magnet is passing by the switch, the signal from the switch is high and the variable "time" gets set to zero. If the magnet is not near the switch "time" gets incremented by 1. This way "time" is actually just a measurement of the amount of time in milliseconds that has passed since the magnet last passed by the magnetic switch. This info is used later in the code to calculate rpm and mph of the bike.
Here's the bit of code that sets up timer1 for 1kHz interrupts
cli();//stop interrupts
//set timer1 interrupt at 1kHz
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;//initialize counter value to 0
// set timer count for 1khz increments
OCR1A = 1999;// = (16*10^6) / (1000*8) - 1
//had to use 16 bit timer1 for this bc 1999>255, but could switch to timers 0 or 2 with larger prescaler
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS11 bit for 8 prescaler
TCCR1B |= (1 << CS11);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei();//allow interrupts
Here's the complete code if you want to take a look:
Step 4: Example 2: Serial Communication
This project is a 4x4 backlit button pad. The project connects to my computer via usb, it sends information about the buttons to the computer and receives information about how to light up the LEDs. Here is a video:
For this project, I used timer2 interrupts to periodically check if there was any incoming serial data, read it, and store it in the matrix "ledData[]". If you take a look at the code you will see that the main loop of the sketch is what is actually responsible for using the info in ledData to light up the correct LEDs and checking on the status of the buttons (a function called "shift()"). The interrupt routine is as short as possible- just checking for incoming bytes and storing them appropriately.
Here is the setup for timer2:
cli();//stop interrupts
//set timer2 interrupt every 128us
TCCR2A = 0;// set entire TCCR2A register to 0
TCCR2B = 0;// same for TCCR2B
TCNT2 = 0;//initialize counter value to 0
// set compare match register for 7.8khz increments
OCR2A = 255;// = (16*10^6) / (7812.5*8) - 1 (must be <256)
// turn on CTC mode
TCCR2A |= (1 << WGM21);
// Set CS21 bit for 8 prescaler
TCCR2B |= (1 << CS21);
// enable timer compare interrupt
TIMSK2 |= (1 << OCIE2A);
sei();//allow interrupts
Here's the complete Arduino sketch:
download the MaxMSP patch below (it will run in Max Runtime as well).
For this project, I used timer2 interrupts to periodically check if there was any incoming serial data, read it, and store it in the matrix "ledData[]". If you take a look at the code you will see that the main loop of the sketch is what is actually responsible for using the info in ledData to light up the correct LEDs and checking on the status of the buttons (a function called "shift()"). The interrupt routine is as short as possible- just checking for incoming bytes and storing them appropriately.
Here is the setup for timer2:
cli();//stop interrupts
//set timer2 interrupt every 128us
TCCR2A = 0;// set entire TCCR2A register to 0
TCCR2B = 0;// same for TCCR2B
TCNT2 = 0;//initialize counter value to 0
// set compare match register for 7.8khz increments
OCR2A = 255;// = (16*10^6) / (7812.5*8) - 1 (must be <256)
// turn on CTC mode
TCCR2A |= (1 << WGM21);
// Set CS21 bit for 8 prescaler
TCCR2B |= (1 << CS21);
// enable timer compare interrupt
TIMSK2 |= (1 << OCIE2A);
sei();//allow interrupts
Here's the complete Arduino sketch:
download the MaxMSP patch below (it will run in Max Runtime as well).
Step 5: Example 3: DAC
In this project I used a timer interrupt to output a sine wave of a specific frequency from the Arduino. I soldered a simple 8 bit R2R DAC to digital pins 0-7. This DAC was constructed from 10k and 20k resistors arranged in a multi-leveled voltage divider. I'll be posting more about the construction of the DAC in another instructable, for now I've included the photo's above.
I connected the output from the DAC up to an oscilloscope. If you need help understanding how to use/read the oscilloscope check out this tutorial. I loaded the following code onto the Arduino:
PORTD=byte(127+127*sin(t/100));
So as t increments from 0 to 627 the sine function moves through one complete cycle. The value sent to PORTD is a sine wave with frequency 63Hz and amplitude 127, oscillating around 127. When this is sent through the 8 bit resistor ladder DAC it outputs an oscillating signal around 2.5V with an amplitude of 2.5V and frequency of 63Hz.
The frequency of the sine wave can be doubled by multiplying the (t/100) term by 2, quadrupled by multiplying by 4, and so on...
Also notice that if you increase the frequency of the timer interrupt too much by decreasing the prescaler or OCR2A the sine wave will not output correctly. This is because the sin() function is computationally expensive, and at high interrupt frequencies it does not have enough time to execute. If you are using high frequency interrupts, instead of performing a computation during the interrupt routine, considering storing values in an array and simply calling these values using some kind of index. You can find an example of that in my arduino waveform generator- by storing 20,000 values of sin in an array, I was able to output sine waves with a sampling frequency of 100kHz.
I connected the output from the DAC up to an oscilloscope. If you need help understanding how to use/read the oscilloscope check out this tutorial. I loaded the following code onto the Arduino:
I set up a timer interrupt that increments the variable t at a frequency of 40kHz. Once t reaches 627 it resets back to zero (this happens with a frequency of 40,000/628 = 63Hz). Meanwhile, in the main loop the Arduino sends a value between 0 (00000000 in binary) and 255 (11111111 in binary) to digital pins 0 through 7 (PORTD). It calculates this value with the following equation:
PORTD=byte(127+127*sin(t/100));
So as t increments from 0 to 627 the sine function moves through one complete cycle. The value sent to PORTD is a sine wave with frequency 63Hz and amplitude 127, oscillating around 127. When this is sent through the 8 bit resistor ladder DAC it outputs an oscillating signal around 2.5V with an amplitude of 2.5V and frequency of 63Hz.
The frequency of the sine wave can be doubled by multiplying the (t/100) term by 2, quadrupled by multiplying by 4, and so on...
Also notice that if you increase the frequency of the timer interrupt too much by decreasing the prescaler or OCR2A the sine wave will not output correctly. This is because the sin() function is computationally expensive, and at high interrupt frequencies it does not have enough time to execute. If you are using high frequency interrupts, instead of performing a computation during the interrupt routine, considering storing values in an array and simply calling these values using some kind of index. You can find an example of that in my arduino waveform generator- by storing 20,000 values of sin in an array, I was able to output sine waves with a sampling frequency of 100kHz.
Step 6: Timer and Arduino Functions
One last thing to note- certain timer setups will actually disable some of the Arduino library functions. Timer0 is used by the functions millis() and delay(), if you manually set up timer0, these functions will not work correctly.
Additionally, all three timers underwrite the function analogWrite(). Manually setting up a timer will stop analogWrite() from working.
If there is some portion of your code that you don't want interrupted, considering using cli() and sei() to globally disable and enable interrupts.
You can read more about this on the Arduino website.
Additionally, all three timers underwrite the function analogWrite(). Manually setting up a timer will stop analogWrite() from working.
If there is some portion of your code that you don't want interrupted, considering using cli() and sei() to globally disable and enable interrupts.
You can read more about this on the Arduino website.
my code is here..
void setup()
{
TCCR0A |= (1 << WGM01);
// Set CS01 and CS00 bits for 64 prescaler
TCCR0B |= (1 << CS01) | (1 << CS00);
with a prescaler of 1024 you get:
Pin interrupt would be more efficient because you'll never miss a change, and above that, you'll not waste time with useless interruption =)
https://gist.github.com/cltnschlosser/e747efae07a76e117e02
What could you recommend to me? :D
greetings and congratulations for your excellent posts