Help needed with Input Capture interrupt handling

I am new to the Arduino and am impressed with how easy it is to get up and running productively. But, I am struggling with the Atmel timer documentation and would appreciate some help getting the Input Capture ISR to behave.

The Capture handler functions as expected, except the ICR1 count just overflows ( the ICR1 count is never reset).

Also, if I enable the overflow interrupt handler, program execution gets blocked.

Any help with the correct setup of the timer1 registers ( I assume that is my problem) would be much appreciated.

 [font=Courier New]
/******* Sketch to test Input Capture interrupt handling *********************************
   Intended functionality: measure length of pulses on the ICP pin with precision of 0.5 microseconds
 This version correctly mirrors incoming pulses on outPin 2 to verify that the ICP handler is working
 but there are two major problems, probably due to incorrect settings of the control register
 BUG: the ICR value is never reset, it incriments to overflow, not reseting on pulse edges
 BUG: if Timer1 overflow interrupt is enabled, execution is blocked in the interrupt handler!
**************************************************************************************************************/
#include <stdio.h>      // for verbose debugging using sprintf 

#define icpPin 8        // ICP input pin on arduino
#define outPin 2        // output pin that will shadow the input, can be monitored with a scope 

                    // some variable to help see that something is happening in the interrupt handlers
volatile int Value;     // this stores the current ICR1 value
volatile int MinValue;     
volatile int MaxValue;
volatile int Overflows;
volatile int PulseCount;


/* Overflow interrupt vector */
ISR(TIMER1_OVF_vect){           // here if no input pulse detected 
   Overflows++;                       // incriment overflow count  
}

/* ICR interrupt vector */
ISR(TIMER1_CAPT_vect){
   if( bit_is_set(TCCR1B ,ICES1)){       // was rising edge detected ?   
        digitalWrite(outPin,HIGH );      // yes, set our output pin high to mirror the input
   }
   else {                                // falling edge was detected 
        Value = ICR1;                    // save the input capture value 
                                         // BUG: ICR1 is not reset so just incriments until overflow 
        digitalWrite(outPin,LOW );       // set our output pin low to mirror the input  
        PulseCount++;
        if(Value < MinValue)             // update min or max values as appropriate   
            MinValue = Value;
        else if (Value > MaxValue)
            MaxValue = Value;    
   }     
   TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge    
}

void setup() {
  beginSerial (19200);
  pinMode(outPin, OUTPUT);               // declare the ledPin as an OUTPUT
  pinMode(icpPin, INPUT);                // ICP pin (digital pin 8 on arduino) as input
  MinValue =    30000;                   // Initialise with extreme values 
  MaxValue =    0;

  
  TCCR1A = _BV(WGM10) | _BV(WGM11);             // Timer 1 is Phase-correct 10-bit PWM.
  
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11); // div 8 clock prescaler to give .5us ticks on 16mhz clock
  TCCR1B |= _BV(ICES1);                         // enable input capture
  TIMSK1 =  _BV(ICIE1);                         // enable input capture interrupt for timer 1
//  TIMSK1 |= _BV(TOIE1);                         // BUG: enabling Overflow blocks in the interrupt handler   
  Serial.print("Finished setup\r\n");
}

// this loop prints the number of pulses in the last second, showing min and max pulse widths 
void loop() {
  char buffer[80];

  sprintf(buffer,"Got %d pulses: min=%d, max=%d (%d timer overflows)\r\n", PulseCount, MinValue, MaxValue, Overflows);         
  Serial.print(buffer);    // send the info to the serial port
  /* reset variables ready for the next reading */   
  PulseCount = Overflows = 0; 
  MinValue =    30000;     // reset to extreme values 
  MaxValue =    0;       
       
  delay(1000);             // wait 1 second for next update              
}[/font]

First post here, so please let me know if more information is needed.

No replies to the question above, perhaps this forum is not the place to ask.

I am new to Arduinos and AVRs so would appreciate pointers to sites where I am likely to find people that have experience with AVR Input Capture interrupt handling, ideally on the ATMega168 or similar in the Aduino environment

This sort of timer usage is more of a "hidden feature" of Arduino - not really part of the language that we document or that most people use. That said, you can sometimes find answers to these sorts of questions here.

A couple of suggestions. You might need to read the high and low bytes of ICR1 separately, the low one first. Also, it appears that the value you read comes from the value of TCNT1 at the time, so you might want to reset TCNT1 inside your capture interrupt handler.

Finally, you might want to try the AVR Freaks forum.

mellis, thanks for the suggestions, unfortunately, neither solved the problem. I will try the AVR Freaks forum.

BTW, you guys are doing a great job. I have been using the Arduino platform for a couple of weeks now and amazed with how easy the environment is to get up to speed.

For anyone else that wants to do something with the Input Capture interrupt, I have attached code below that fixes both problems mentioned in my first post.

The fix was to use the timer in normal mode by setting the appropriate bits in TCCR1A and TCCR1B (see the code sample). That enabled TCNT1 to be reset in the handler and allowed the overflow interrupt to work.

The ATmega168 data sheet says that ‘Input Capture is easy to use in normal mode’, I didn’t realise that meant that its damn near impossible to get it to work in any other mode !!

/******* Sketch to test Input Capture interrupt handling *********************************
 functionality: measure length of pulses on the ICP pin with precision of 0.5 microseconds
 Show the min and max pulse widths in microseconds on the serial port

**************************************************************************************************************/
#include <stdio.h>      // for verbose debugging using sprintf 

#define icpPin 8        // ICP input pin on arduino
#define outPin 2        // output pin that will shadow the input, can be monitored with a scope 

                        // some variables to help see that something is happening in the interrupt handlers
volatile unsigned int Value;     // this stores the current ICR1 value
volatile unsigned int MinValue;     
volatile unsigned int MaxValue;
volatile unsigned int Overflows;
volatile unsigned int PulseCount;


/* Overflow interrupt vector */
ISR(TIMER1_OVF_vect){                 // here if no input pulse detected 
   Overflows++;                       // incriment overflow count  
}

/* ICR interrupt vector */
ISR(TIMER1_CAPT_vect){
   TCNT1 = 0;                            // reset the counter  
   if( bit_is_set(TCCR1B ,ICES1)){       // was rising edge detected ?   
        digitalWrite(outPin,HIGH );      // yes, set our output pin high to mirror the input
   }
   else {                                // falling edge was detected 
        Value = ICR1;                    // save the input capture value 
        digitalWrite(outPin,LOW );       // set our output pin low to mirror the input  
        PulseCount++;
        if(Value < MinValue)             // update min or max values as appropriate   
            MinValue = Value;
        if (Value > MaxValue)
            MaxValue = Value;    
   }     
   TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge    
}

void setup() {
  beginSerial (19200);
  pinMode(outPin, OUTPUT);               // declare the ledPin as an OUTPUT
  pinMode(icpPin, INPUT);                // ICP pin (digital pin 8 on arduino) as input
  MinValue =    30000;                   // Initialise with extreme values 
  MaxValue =    0;


  TCCR1A = 0 ;                    // this register set to 0!
  TCCR1B =_BV(CS11);              // NORMAL MODE!!, prescaller 8, rising edge ICP1 - this works
  TCCR1B |= _BV(ICES1);           // enable input capture     

  TIMSK1 =  _BV(ICIE1);                         // enable input capture interrupt for timer 1
  TIMSK1 |= _BV(TOIE1);                         // enable overflow interrupt to detect missing input pulses 
  Serial.print("Finished setup\r\n");
}

// this loop prints the number of pulses in the last second, showing min and max pulse widths 
void loop() {
  char buffer[80];
  // show the captured data, divide pulse widths by 2 to display time in microseconds
  sprintf(buffer,"Got %d pulses: min=%d, max=%d (%d timer overflows)\r\n", PulseCount, MinValue / 2, MaxValue / 2, Overflows);         
  Serial.print(buffer);    // send the info to the serial port
  /* reset variables ready for the next reading */   
  PulseCount = Overflows = 0; // reset counts
  MinValue =    30000;        // set values to extremes
  MaxValue =    0;       
   
  delay(1000);             // wait 1 second for next update  [/font]            
}