If you read the previous blog post you might remember the problem I mentioned when using the delay function. Using the function locks up the processor and prevents any other code from running until it is done waiting. One simple way to get around using the delay function is to use timers instead.
There are a six available timers in the Arduino Mega. The tricky part is that they are already tied to specific peripherals and functions. Using a timer for an interrupt will interfere with using the pins it's tied to for PWM. The Arduino website says the Mega2560 has 14 PWM pins available. But, if I read the schematic correctly, it appears there are possibly three more pins PWM ready: Pins 44, 45, and 46. I'll have to test this when I start messing with PWM.
Looking at the Arduino schematic, the Timers are tied to the following Pins:
Timer0: Pins 4 and 13
Timer1: Pins 11 and 12
Timer2: Pins 9 and 10
Timer3: Pins 2, 3, and 5
Timer4: Pins 6, 7, and 8
Timer5: Pins 44, 45, and 46
When using the timers you need to check which pins they are connected to so that you know what functionality you are losing in regards to PWM.
The following program can be used to replace the delay(1000) from the earlier project. By configuring the Timer2 Overflow Interrupt to fire every 1ms, we can increment a counter in the Interrupt Vector and wait until 1000 interrupts have occurred to toggle the LED.
/*=======================================================================================
Noah Stahl
5/7/2011
Arduino Mega 2560
This program will blink an LED on Port 53 once a second using a Timer2 Overflow Interrupt. The timer overflow interrupt fires every 1ms by setting the prescaler to 128 and preloading Timer2's counter with 130. Timer2 is 8-bits so the max number it can count to is 255, this leaves 125 cycles left to count. The Arduino has a clock of 16MHz so:
(1/16E6) * 125 * 128 = 1ms
The interrupt flag will fire every 1ms. Once the interupt fires 1000 times (i.e. one second) the LED on pin 53 will toggle.
=======================================================================================*/
#include <avr/interrupt.h>
#include <avr/io.h>
unsigned int toggle = 0; //used to keep the state of the LED
unsigned int count = 0; //used to keep count of how many interrupts were fired
//Timer2 Overflow Interrupt Vector, called every 1ms
ISR(TIMER2_OVF_vect) {
count++; //Increments the interrupt counter
if(count > 999){
toggle = !toggle; //toggles the LED state
count = 0; //Resets the interrupt counter
}
digitalWrite(53,toggle);
TCNT2 = 130; //Reset Timer to 130 out of 255
TIFR2 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag
};
void setup() {
pinMode(53,OUTPUT);
//Setup Timer2 to fire every 1ms
TCCR2B = 0x00; //Disbale Timer2 while we set it up
TCNT2 = 130; //Reset Timer Count to 130 out of 255
TIFR2 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag
TIMSK2 = 0x01; //Timer2 INT Reg: Timer2 Overflow Interrupt Enable
TCCR2A = 0x00; //Timer2 Control Reg A: Wave Gen Mode normal
TCCR2B = 0x05; //Timer2 Control Reg B: Timer Prescaler set to 128
}
void loop() {
}
A few important things to note about the code:
- Disable the Timer while you configure your interrupt and timer by setting TCNT2 = 0x00.
- Timer2 is 8-bits wide, so it can count up to 255. By preloading the counter with 130, TCNT2 = 130, this leaves 125 cycles left to count.
- (125 cycles) * (128 prescaler) / (16MHz clock speed) = 1ms
- Clear the Timer2 INT Flag by setting TIFR2 = 0x00.
- Enable the Timer2 Overflow Interrupt by setting TIMSK2 = 0x01.
- When your timer and interrupt are configured, you can then set the prescaler which will restart the timer. In this example the prescaler is set to 128 by setting TCCR2B = 0x05. The timer will count 128 clock cycles before it increments its counter.
- Once inside the Interrupt vector, be sure to reset your timer count (TCNT2 = 130) and clear the interrupt flag (TIFR2 = 0x00) so that you can reuse the timer and interrupt function.
Hi Noah,
ReplyDeleteit seems you truly master the management of timers. I realized two projects that work great independently but I can not run simultaneously and I guess it is due to a timer conflict. Unfortunately I'm really lost with it. If you have time maybe you can have a look ? The libraries that seem to be in conflicts are VirtualWire.h and DigitShield.h. The code here is compiling without error but nothing works. Hope you can help...
thank's!! ^^
ReplyDeleteGood explaination, this is a modest example using pin 13:
ReplyDelete< BEGINCODE >
unsigned int toggle = 0; //used to keep the state of the LED
unsigned int count = 0; //used to keep count of how many interrupts were fired
byte ledpin = 13; //for testing - onboard pin
unsigned int blinkms = 0; //duration of blink
ISR(TIMER2_OVF_vect) //Timer2 Overflow Interrupt Vector, called every blinkms
{
count++; //Increments the interrupt counter
if(count > (blinkms - 1))
{
toggle = !toggle; //toggles the LED state
count = 0; //Resets the interrupt counter
}
digitalWrite(ledpin,toggle);
TCNT2 = 130; //Reset Timer to 130 out of 255 - 130 1 sec - 192.5 0.5 sec
TIFR2 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag
}
void setup()
{
Serial.begin(57600);
TIMSK2 = 0x00; //Timer2 INT Reg: Timer2 Overflow Interrupt Enable
TCCR2A = 0x00; //Timer2 Control Reg A: Normal port operation, Wave Gen Mode normal
}
void loop()
{
if(Serial.available())
{
byte c = Serial.read();
if (c==65) //A
{
Serial.println("Blink 500 ms");
blinkled(13,500);
}
if (c==66) //B
{
Serial.println("Blink 1 sec.");
blinkled(13,1000);
}
if (c==67) //C
{
Serial.println("Disabled");
TIMSK2 = 0x00; //Timer2 INT Reg: Timer2 Overflow Interrupt Disable
digitalWrite(ledpin,LOW);
}
if (c==68) //D
{
Serial.println("Enabled");
TIMSK2 = 0x00;
digitalWrite(ledpin,HIGH);
}
if (c==69) //E
{
for (unsigned int i = 0; i<1000; i++) Serial.println("TESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTEST");
}
}
}
void blinkled(byte setpin,unsigned int microseconds)
{
pinMode(ledpin,OUTPUT);
ledpin = setpin;
blinkms = microseconds;
TIMSK2 = 0x01; //Timer2 INT Reg: Timer2 Overflow Interrupt Enable
}
To blink use the command: blinkled(pin,duration);
I would like to test 16bit timers...
p.s.: the captcha doesn't works in google chrome, works in IE
Hi, I have some questions can you help me please?
ReplyDeleteGreat post and well explained.
ReplyDeleteTIFR2 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag
ReplyDeleteI don't think you can clear the flag by writing 0 to it. You need 1s:)