I'm having a problem with getting AVR interrupts to work as I expect. I haven't used them before, so please forgive me if I'm making a really obvious mistake.
I have a sensor, generating square-wave pulses, wired to pin2 and the intention of my code is to count the number of these pulses that arrive each second. This sensor can generate pulses up to 500kHz, so using Arduino 'attachInterrupt()' isn't quite fast enough, hence my need to use lower level code. I'm running this on an Arduino pro-mini ATmega328 5V, with Arduino 1.0.1 IDE.
What I want to happen is that, once per second, the report() routine prints how many pulses it counted during the previous second. What I'm actually finding is that the report either states zero, or the code doesn't even execute the report() routine.
I know the hardware is working properly since, if I replace the AVR code and call an ISR using an 'attachInterrupt()' instead, then everything works as expected - but then I'm limited to only working at lower frequencies (I've tried and can only reach ~ 140kHz).
Thank you - any help would be greatly appreciated!
Here is my code:
#include "TimerOne.h"
#include <avr/io.h>
#include <avr/interrupt.h>
volatile long int pulseCount=0;
void setup() {
Timer1.initialize(); // initialize timer1
Timer1.attachInterrupt(report, 1000000); // call report() when timer interrupts after 1 sec.
Serial.begin(9600);
pinMode(2, INPUT); // pin2 is wired to my sensor output
sei(); // Enable global interrupts
EIMSK |= (1<<INT0); // Enable external trigger INT0 - connected to pin 2
EICRA |= (1<<ISC01); // Enable trigger on falling edge
}
void loop() { } // the main loop does nothing - everything is interrupt driven
ISR(EXT_INT0_vect) { pulseCount++ ; } // simply counts the pulses from the sensor.
// Once per second, report the number of pulses in the previous second, then reset counter
void report() { Serial.println(pulseCount); pulseCount=0; }
You are calling Serial.print in an interrupt routine - that's going to block the other interrupts for ages
(especially at 9600baud). Much better to busy-wait for the counter in loop() using:
(This also saves a timer). If you want more accurate timing then just record the value of pulseCount
in the interrupt routine and call Serial.print() in loop ().
is the wrong order - setup the trigger mode before enabling interrupts - otherwise you'll get false or
unexpected triggering - here it may not matter, but in general setup up, then enable.
...Aah, of course. How stupid of me! I was focussing on the 'pulseCount' ISR and forgot about not putting Serial in the timer ISR. Newbie error! So sorry.
But I'm still not quite there. Even incorporating your corrections it's still not running properly. I'm getting no output at all. Here's the revised code:
Thanks again.
#include <avr/io.h>
#include <avr/interrupt.h>
volatile long int pulseCount=0;
long int previous = millis();
void setup() {
Serial.begin(19200);
pinMode(2, INPUT); // this is wired to my sensor output
sei(); // Enable global interrupts
EICRA |= (1<<ISC01); // Enable trigger on falling edge
EIMSK |= (1<<INT0); // Enable external trigger INT0 - connected to pin 2
}
void loop ()
{
while ( (millis() - previous) < 1000)
{}
previous = millis() ;
Serial.println(pulseCount);
}
ISR(EXT_INT0_vect) { pulseCount++ ; } // simply counts the pulses from the sensor.
Thanks Nick! Yes, 'INT0_vect' not 'EXT_INT0_vect' was the answer. It works now.
With regard to your question "why not do attachInterrupt, and not fiddle with registers?", I initially tried using 'attachInterrupt()' but the code only ran for frequencies up to ~ 140kHz before behaving erratically. It seemed as if I had ~ 5-6us of overhead in my ISR, and if I interrupted faster than this, things didn't work properly. Reading around I found several examples from other people doing similar things and finding similar problems (e.g. 'The overhead of Arduino Interrupts | Bill Grundmann's Blog '). He looked closely and found that a large number of processor cycles were used during the interrupt handler for INT0. He was able to eliminate this overhead by changing from 'attachInterrupt()' to the lower level AVR code.
(This is what I meant in my original comment: 'using Arduino 'attachInterrupt()' isn't quite fast enough', although I probably wasn't clear enough in what I meant.)
I was following Grundmann's example to solve my problem, but if you think that attachInterrupt() is fast enough to reach 500kHz, and that I am misunderstanding the situation, I'd be delighted to be corrected. I'm new to this and still learning!
The attachInterrupt approach does have a bit of an overhead. Without it, you will be taking around 2.625 uS to execute the interrupt, and with it you will take around 5.125 uS. So, yes there's an overhead. But you are getting close to the limit. Figures from here.
However as I mentioned you can get much faster counting by letting the hardware do it (see my timers page). That lets the hardware timer count events, which it can do up to the clock speed of the processor, and then have an interrupt on an overflow (every 65535 counts).