As part of a larger project, I developed a set of classes to capture and decode a digital signal (DCC for model trains, described here, for those interested). One of those classes handles getting the raw timing of the pulses using an interrupt. To keep the ISR code as simple and quick as possible, I implemented a simple queue, whereby the ISR just puts a timestamp in the queue, and then non-ISR code can deal with decoding it. This makes it easy to change how I obtain the timestamp, as well as allowing considerably more flexibility in the code that interprets the timestamps.
I got curious the other day and examined a variety of methods for obtaining the timestamp. I captured 250 samples for each method and plotted the calculated pulse width as a function of time (all times in microseconds). The tests were done using a Sparkfun Redboard with the ATmega328 running at 16MHz. The red and green lines in each plot indicate the limits for 1 and 0 bits, respectively.
Pic 1 shows the pulse widths calculated using a hardware interrupt and the micros() call to get the timestamp. The 4us resolution of the micros() call is evident.
Pic 2 shows the pulse widths using a hardware interrupt with the timestamp taken from the timer 1 counter TCNT1. For this case timer 1 was configured with a prescaler of 8, which gives 0.5us resolution, with an overflow every 32ms.
Pic 3 shows the effect on the timestamps of a 10us noInterrupts block in main(), to simulate the servicing of other interrupts. Since the hardware interrupt only gets serviced outside that block, the recorded timestamps lose a similar degree of accuracy. This effect is evident in a couple of spots in Pics 1 and 2 as well.
Pics 4 and 5 show the pulse widths based on a timestamp from the input capture register, which is then added to the queue by the ISR. As above, timer 1 was configured with a prescaler of 8, which gives 0.5us resolution. Because the timestamp is captured in the register independent of when the ISR gets called, a delay in calling the ISR does not affect the accuracy of the timestamp. This is shown in Pic 5, with the same 10us noInterrupts block as in Pic 3.
And finally, Pic 6 shows the pulse widths based on a timestamp from the input capture register, with timer 1 configured with no prescaler. This gives 0.0625us resolution, with an overflow every 4ms. Although the 0.5us resolution in pics 4 and 5 stays reliably within the 1 and 0 limits, the curve really takes shape here.