int0 and timer

Hi,

I need to record the exact time of when I receive a hardware interrupt on int0, is there a registry where this is alredy available or do i need to do it with old fashion like that:
currentTime = micros();…

this is on a Nano

thx

The old fashioned way is reliable and simple - features which tend to go together.

...R

You can also connect your input to the Input Capture Pin of one of the hardware timers. When that pin changes it can store the current timer count into the ICR (Input Capture Register). That will give you the most accurate timing. You can also enable the Input Capture Interrupt to cause an interrupt when the input capture occurs. That way you don't need the separate INT0 pin.

Thanks John,

I followed your advice and plugged a frequency generator on the D8 input pin of my Uno and wrote that minimalist code, but for some reason I have some very unstable result the period read is ±20% accuracy.

Anybody sees something wrong ?

volatile uint16_t T1Ovs2=0;
volatile uint32_t Capt1;

void setup() {

  //Set Initial Timer value
  TCNT1=0;
  TCCR1A=0;
  TCCR1B=0;
  // capture on rising edge
  TCCR1B|=(1<<ICES1);
  //Enable input capture and overflow interrupts
  TIMSK1|=(1<<ICIE1)|(1<<TOIE1);

  //Start timer without prescaller
  TCCR1B|=(1<<CS10);

  Serial.begin(115200);
}

//capture ISR
ISR(TIMER1_CAPT_vect)
{
  Capt1=ICR1+T1Ovs2*65535;
  //clear ovf
  T1Ovs2=0;
  //clear interrupt flags to avoid any pending interrupts
  TIFR1=(1<<ICF1)|(1<<TOV1);

}

//Overflow ISR
ISR(TIMER1_OVF_vect)
{
  //increment overflow counter
  T1Ovs2++;
}

void loop() {
  //display period in µs
  Serial.println(Capt1/16);
  delay(250);

}

Ok I’m replying to myself :slight_smile: just needed to reset the timer ( TCNT1 = 0; ).
For future generations here is the corrected code :

volatile uint16_t T1Ovs2=0;
volatile uint32_t period1;

void setup() {

  //Set Initial Timer value
  TCNT1=0;
  TCCR1A=0;
  TCCR1B=0;
  // capture on rising edge
  TCCR1B|=(1<<ICES1);

  //Enable input capture and overflow interrupts
  TIMSK1|=(1<<ICIE1)|(1<<TOIE1);

  //Start timer without prescaller
  TCCR1B|=(1<<CS10);

  Serial.begin(115200);
}

//capture ISR
ISR(TIMER1_CAPT_vect)
{
  period1=ICR1/16+T1Ovs2*4095;

  //clear ovf 
  T1Ovs2=0;

  //restart timer
  TCNT1 = 0;

}

//Overflow ISR
ISR(TIMER1_OVF_vect)
{
  //increment overflow counter
  T1Ovs2++;
}

void loop() {
  //display period in µs
  Serial.println(period1);
  delay(250);

}
  Capt1=ICR1+T1Ovs2*65535;

That’s probably wrong in two ways - first its 16 bit arithmetic and you then assign
to 32 bit quantity, so you’ll lose the top two bytes.

Secondly you need to multiply by 65536, since that’s the period of the counter,
not 65535.

This you do better with a shift:

 Capt1 = ICR1 + (((unsigned long)T1Ovs2) << 16) ;

The latter version has a * 4095 that should be << 12

Thanks Mark,
Indeed the 32 bit cast is helpfull, and multiplication by shift is way quicker, and it’s better to do the 16 division outside the interrupt in the main loop when time is not critical.

So here is the final version:

volatile uint16_t T1Ovs2=0;
volatile uint32_t Capt1;

void setup() {

  //Set Initial Timer value
  TCNT1=0;
  TCCR1A=0;
  TCCR1B=0;
  // capture on rising edge
  TCCR1B|=(1<<ICES1);

  //Enable input capture and overflow interrupts
  TIMSK1|=(1<<ICIE1)|(1<<TOIE1);

  //Start timer without prescaller
  TCCR1B|=(1<<CS10);

  Serial.begin(115200);
}

//capture ISR
ISR(TIMER1_CAPT_vect)
{
   Capt1 = ICR1 + (((unsigned long)T1Ovs2) << 16) ;

  //clear ovf 
  T1Ovs2=0;

  //restart timer
  TCNT1 = 0;

}

//Overflow ISR
ISR(TIMER1_OVF_vect)
{
  //increment overflow counter
  T1Ovs2++;
}

void loop() {
  //display period in µs, with no prescaler 16 ticks = 1µs
  Serial.println(Capt1/16);
  delay(250);

}