I will think before I code… 24
Posted by md
on December 27, 2006
As CosineKitty pointed out: My Arduino timer interrupt code did not work because the Prescaler bits were not set correctly.
Applying basic boolean logic helps ;-) This is the working code:
#include < avr / interrupt.h >
#include < avr / io.h >
#define INIT_TIMER_COUNT 6
#define RESET_TIMER2 TCNT2 = INIT_TIMER_COUNT
int ledPin = 13;
int int_counter = 0;
volatile int second = 0;
int oldSecond = 0;
long starttime = 0;
// Aruino runs at 16 Mhz, so we have 1000 Overflows per second...
// 1/ ((16000000 / 64) / 256) = 1 / 1000
ISR(TIMER2_OVF_vect) {
RESET_TIMER2;
int_counter += 1;
if (int_counter == 1000) {
second+=1;
int_counter = 0;
}
};
void setup() {
Serial.begin(9600);
Serial.println("Initializing timerinterrupt");
//Timer2 Settings: Timer Prescaler /64,
TCCR2 |= (1< ");
Serial.print(millis() - starttime);
Serial.println(".");
digitalWrite(ledPin, HIGH);
delay(100);
digitalWrite(ledPin, LOW);
oldSecond = second;
}
}
I will think before I code, I will think before I code, I will think before I code, ...
The timer now works perfectly. I think the CTC mode will be more efficient, but my measurement shows that the clock has a slight skew (3 ms) when the Arduino starts up (lets say, during the first five minutes). Later, the skew is about zero. So the overflow mechanism is quite exact ;-)
Note that the TIMER2 is used for PWM in the Arduino libraries, so this sketch might interfere with PWM applications - I did not test this.
Trackbacks
Use this link to trackback from your own site.
Hey,
Nice work on the project. I’ve never had anything to do with interupts, so I wanted to start with you code to see how it all works. I can’t get it to compile however. Are you using the standard Arduino 007 environment? I couldn’t get it to link interrupt.h or io.h in their default locations, but after i moved them around I get errors in pretty much every line with
Alex,
I developed with Arduino0006, but since the code doesn’t use any external interrupt routines it should run without any changes in Arduino0007. Did you remove the blanks in the include lines? The line
#include < avr / interrupt.h >
should not have any blanks between the brackets. Sorry, I didn’t figure out how to tell WordPress not to insert any tags. Does this solve your issue? You can also find a working example in my Arduino DCF77 clock lib.
-Mathias
This is great thanks. I can finally set definate sampling rates. To get this to run on arduinos with atmega 168 note
//Timer2 Settings: Timer Prescaler /64,
TCCR2A |= (1
Again, nice one. Your code is the holy grail that I’m basing my project on (credited of course).
I’m not a programmer – I’ve only ever really used wrapper languages like the Arduino IDE, so I don’t fully understand every line yet, but having a working example is invaluable.
Next step is to get simmilar code working on an AtTint12/13/15 – I know they have a simmilar internal timer, but I’m not yet good enough to port over the required registers etc.
Hi Alex,
thanks. Porting the code should not be that difficult – AFAIK the structure of the Atmel AT* is pretty much the same for all models. You might want to check which bits are set in the control registers for the ATMega8 code above, and translate it to your chip. Use the datasheets from Atmel – have fun!
-Mathias
Mathias,
Thanks to your code (and your mistakes!) I think I understand the whole bitwise operations thing, and also the timer interrupts. I THINK i’ve translated it to the AtTiny15, but sorting a few other things out.
Next question – I’m using your code to update a variable infrequently – every 6 minutes. It seems wasteful to have the controller sitting around during this time. I’m looking at the arduino sleep code, but the wakeup function seems to only be looking at an external interrupt. Have you got any ideas, or seen others using sleep this way?
Essentially I want to start optimising this for lower power consumption to eventually have it running off battery power. Obviosuly the longer the battery life the better!
Hi trialex,
The AT-* chips support various sleepmodes, for an overview, consult e.g. the AVRlib sleep.h documentation. I assume you have to figure out which one of the sleepmodes is suitable for your application and use sleep.h directly (or set the bits as described in the datasheet).
On the ATMega8, you can use the “power-save” mode to safe power while waking on a timer comparison interrupt – this way, you could use the timer to wake the chip every six minutes.
I will need to use the power management features for my clock, so I am looking forward to see your code ;-)
-Mathias
Thanks a lot!
Huh! Looks like we are doing similar projects.
Looking at the data sheets for the Attiny15, Attiny2313 and Atmega8 shows you that by far the biggest saving is made by using a smaller device. Even in normal operation the tiny’s use much lower current than the mega’s.
The tiny’s have only “normal” and “idle” modes. The “idle” current for the 15 in like 10 times lower than the mega’s “power-save”
Damn the tiny15 only having 6 ports! I need 6 output, 1 input. I’m trying to do some tricking switching between modes, but not reliable yet. Need to work out how internal pull-ups work…
;-) Trialex, this is interesting, thanks for pointing out. Maybe it makes no sense to try to safe power on the ATMegas – battery lifetime will be short anyway. Since I think my way of displaying the time will not be very power-efficient, I will probably not investigate power saving any more…
As for the output port problem: I don’t know your design, but maybe a shift register as the 74HC595 chip can help. You need 3-5 pins on the microcontroller side to controll 8 output pins on the shift register – which might do in your case. And for additional outputs, you can simply chain the shift registers…
this is a great code to get me started on timers. my goal is more on the audio side. i’ll get to it at some point. in the meantime, any idea why output only works on pin13? i tried changing to 7, 8, 12, … and none work. if i plug my ext led to 13 and set ledpin to 13 (original code), no problem but if i change ledpin and put my led on corresponding pin >> nothing. does the interupt affect all pins (even the non-pwm ones)?
thanks.
Hi mat,
I see no reason why the LED output should not work on other pins. I can think of two problems: (1) Do you use a resistor (220 Ohms) when using another pin? Pin 13 has a build-in resistor, for other pins, you need to use an external one. (2) I don’t remember which PWM pin corresponds to the second timer interrupt, but it should be safe to try pin 4 or 5 for the LED.
Hope this helps,
-Mathias
I can’t get this code to work… I am using Arduino 0012.
The error:
In function ‘void __vector_9()’:
error: ‘TCNT’ was not declared in this scope In function ‘void setup()’:
In the code, the line RESET_TIMER2; is marked yellow.
Does anyone know how to fix this?
Thanks!
Hi Stijn,
I haven’t tested the code with recent Arduino versions – but your problem has something to do with the naming of control registers. Do you use an ATMega168-based Arduino? There are some differences in register naming.
For example, for my DCF77 library I had to define things differently, see
http://gonium.net/md/2007/04/18/tweaking-the-code/
Try to adjust the code accordingly.
HTH,
-Mathias
Hello,
when i run this code on my arduino the ms print out has a variation of about 2 – 3 ms, i.e. i see
1 – 1001
2 – 2004
3 – 3002
etc…
is this expected? I am looking for something that can guarantee a consistent 4 ms sampling rate. Is that possible do you think?
Hi Donal,
I don’t think this code is appropriate for measuring time in the millisecond range. Basically it uses busy waiting in order to detect the time interval between two DCF77 signals. If you want to measure time exactly, I suggest you take a look at interrupts. The ATMega168 chip provides three programmable timer interrupts which can be used to sample something with high accuracy.
HTH,
-Mathias
Hi again,
many thanks for your reply… I must apologise in advance as I’m a bit of a newbie here… but I was under the impression that this code does use ‘timer interrupts’. I thought it was working off an internal timer that overflows every 256 clock cycles and that when it does the ISR would be triggered. The code I’m using is actually adapted for the atmega168 chip and I have included it below.
My main question now is… is there a more appropriate ‘type’ of interrupt to be using here?
Many thanks,
Donal.
#include
#include
#define INIT_TIMER_COUNT 6
#define RESET_TIMER2 TCNT2 = INIT_TIMER_COUNT
int ledPin = 13;
int int_counter = 0;
volatile int second = 0;
int oldSecond = 0;
long starttime = 0;
//unsigned long m = millis();
// Aruino runs at 16 Mhz, so we have 1000 Overflows per second…
// 1/ ((16000000 / 64) / 256) = 1 / 1000
ISR(TIMER2_OVF_vect) {
RESET_TIMER2;
int_counter += 1;
if (int_counter == 4) {
second+=1;
int_counter = 0;
}
};
void setup() {
Serial.begin(9600);
Serial.println(“Initializing timerinterrupt”);
//so we just hard code this for now.
//Timer2 Settings: Timer Prescaler /64,
TCCR2A |= (1<<CS22);
TCCR2A &= ~((1<<CS21) | (1<<CS20));
// Use normal mode
TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
// Use internal clock – external clock not used in Arduino
ASSR |= (0<<AS2);
//Timer2 Overflow Interrupt Enable
TIMSK2 |= (1<<TOIE2) | (0<<OCIE2A);
sei();
//Timer2 Overflow Interrupt Enable
TIMSK2 = 1<”);
Serial.println(millis() – starttime, DEC);
//Serial.println(“.”);
//digitalWrite(ledPin, HIGH);
//delay(100);
//digitalWrite(ledPin, LOW);
oldSecond = second;
starttime = millis();
}
}
Hi Donal,
sorry, I was confused – of course you’re right, the code uses interrupts (I somehow thought of my first version of the sketch from which I remembered this behaviour, sorry). The interrupts itself are highly accurate. I would guess that the variations in the timing are a result of the calls to the Serial.println(…) method which is pretty heavyweight. This would explain the milliseconds variation. I don’t have an Arduino here, but you can test this simply by precomputing the values to be printed before the Serial.println calls. I’m thinking of something like
if (oldsecond != second) {
long difference=millis() – starttime;
Serial.print(second);
Serial.print(“. ->”);
Serial.print(difference);
Serial.println(“.”);
}
Please let me know whether this explains the behaviour. If not you can also adjust the timing settings of the interrupt. Please note that Timer2 is used here which has an 8-bit resolution. You could also try to use Timer1 instead which is a 16-bit timer. Depending on your problem you should also read the ATMega168 datasheet – for example, you can built a simple hardware frequency generator based on the timers. Atmel has also a nice introduction to the timer hardware, google for “AVR130: Setup and Use the AVR Timers”.
HTH
-Mathias
Hi Mathias,
We ended up just counting the number of reads in a specified time and found that they averaged out as they should.
Many thanks for your help,
Donal.
I borrowed your interrupt code, trying to do a serial read and write driven by the interrupt timings. Thank you.
[...] I found the following code and adapted it to the ATMEGA 328. http://gonium.net/md/2006/12/27/i-will-think-before-i-code/ [...]
emm. informative )
[...] http://gonium.net/md/2006/12/27/i-will-think-before-i-code/ Dejar un comentario Aún no hay comentarios por mucho Deja un comentario Canal RSS de los comentarios de la entrada. URI para TrackBack. Deja un comentario Haz clic para cancelar la respuesta. Línea y párrafo se rompe automáticamente, direcciones email nunca se muestran, [...]
I am also a newbie on Arduino. I need the Arduino (over a serial connection) 1. to receive a wake up time from a Telit GSM modem (an echoed AT command which needs to be parsed by Arduino), 2. to receive an echoed Telit AT#SHDN command, 3. shut down power to the Telit modem, 4. to fall asleep and 5. to wake up according to the parsed time given by the modem to the Arduino chip. In real life this would mean that the Arduino has to sleep sometimes for a couple of hours and then wake up. My question is if the Arduino can do such with the timer interrupts and/or time libraries. Thanks in advance for your answer.