I use the InputCapture function without interrupts for high accuracy with an ultrasonic distance finder.
#include <util/delay.h>
const byte InputCapturePin = 8;
const byte TriggerPin = 9;
void setup()
{
Serial.begin(9600);
TCCR1A = 0; // Normal Mode
TCCR1B = 1; // Prescale to 1
pinMode(InputCapturePin, INPUT);
pinMode(TriggerPin, OUTPUT);
digitalWrite(TriggerPin, 0);
}
void loop()
{
unsigned int StartTime, EndTime;
unsigned long TriggerTime;
TCCR1B |= 0x40; // Capture on Rising Edge
TIFR1 |= 0x20; // Reset Interrupt Capture Flag
_delay_us(100);
if (TIFR1 & 0x20)
{
Serial.println("Echo pin already high");
return;
}
PORTB |= 2; // Trigger On
_delay_us(20);
PORTB &= 0xFD; // Trigger Off
for (TriggerTime = micros(); micros() - TriggerTime <= 500L;)
{
if (TIFR1 & 0x20)
{
StartTime = ICR1;
break;
}
}
if ((TIFR1 & 0x20) == 0)
{
Serial.println("Echo didn't reset");
return;
}
TCCR1B &= 0xBF; // Capture on Falling Edge
TIFR1 |= 0x20; // Reset Interrupt Capture Flag
for (TriggerTime = micros(); micros() - TriggerTime <= 16000L;)
{
if (TIFR1 & 0x20)
{
EndTime = ICR1;
break;
}
}
if ((TIFR1 & 0x20) == 0)
{
Serial.println("No Echo Received");
return;
}
Serial.print("Elapsed Time = ");
Serial.println(EndTime - StartTime);
}
You can configure the input capture hardware to fire on rising edge or falling edge. Wrapping it in an interrupt is doable. Or, if you can use the BlinkWithoutDelay state machine method, and if your loop routine is shorter than one half of the on/off cycle of the input pin, you don't need any interrupts at all. Just check the Interrupt Capture Flag each time through your loop routine, and if it gets set, the ICR1 register has a time stamp of the last time the pin fired. Your code doesn't need to be timing accurate if the loop executes quickly enough.
You do need to worry about wrap-around, since the time stamp is only 16 bits, and at full accuracy it's in 62.5 nanosecond units (clockspeed is 16 MHz) instead of microseconds. Also, the inputCapture pin is pin 8.