Problem getting AVR interrupts to work as expected...

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; }

Again you can't use serial in an ISR.

Mark

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:

void loop ()
{
  while (millis () - previous < 1000)
  {}
  previous = millis () ;
  report () ;
}

(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 ().

Also this setup code:

   EIMSK |= (1<<INT0);      // Enable external trigger INT0 - connected to pin 2
   EICRA |= (1<<ISC01);     // Enable trigger on falling edge

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.

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

Rubbish. Attach is only called when you attach the interrupt. You should not use it over and over again.

   sei();                   // Enable global interrupts
   EIMSK |= (1<<INT0);      // Enable external trigger INT0 - connected to pin 2
   EICRA |= (1<<ISC01);     // Enable trigger on falling edge

The above is done in attach (or long before that)!

Mark

My frequency counter sketch on this page worked up to 5 MHz:

Don't do serial prints inside an ISR:

...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.

Look at my link above. The interrupt handler seems to be INT0_vect not EXT_INT0_vect.

Besides, why not do attachInterrupt, and not fiddle with registers?

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!

Anyway, thanks again.

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).

The hardware is designed to count, let it do it. :slight_smile:

Thanks Nick - your timer code is very nice. I think that's how'll I'll do it.

Thanks also to the other responders for your time and advice with this problem.