There is, indeed, a possible race condition. Consider this naive code,
which provides 32-bit timestamps:
/* Count overflows. */
volatile uint16_t timer1_overflow_count;
ISR(TIMER1_OVF_vect)
{
timer1_overflow_count++;
}
/* Timestamp input events. */
volatile uint32_t captured_timestamp;
volatile bool did_capture;
ISR(TIMER1_CAPT_vect)
{
captured_timestamp = timer1_overflow_count << 16 | ICR1;
did_capture = true;
}
Since TIMER1_CAPT has higher priority than TIMER1_OVF, then:
If ICF1
rises before TOV1
, TIMER1_CAPT_vect
runs first, which
is fine.
If ICF1
and TOV1
rise simultaneously, TIMER1_CAPT_vect
runs
first, which is still fine, as the captured value is 0xffff
.
If TOV1
rises before ICF1
, either ISR may run first:
if the CPU can process the TIMER1_OVF IRQ right away, then
TIMER1_OVF_vect
runs first and the result is correct
if the interrupt processing is delayed until after ICF1
has
risen, then TIMER1_CAPT_vect
runs first and grabs a wrong value
for timer1_overflow_count
.
Solution
There is an unhandled overflow when bit_is_set(TIFR1, TOV1)
. The
overflow may have happened at the same time or after the capture, in
which case it doesn't affect the timestamp. We know it happened before
the capture when the captured value is small, where “small” could be
defined as having its most significant bit clear. If this is the case,
we have to add 0x10000
to the timestamp we obtained.
A race-proof ISR can be written as follows:
ISR(TIMER1_CAPT_vect)
{
uint16_t captured_low = ICR1;
uint16_t captured_high = timer1_overflow_count;
if (bit_is_set(TIFR1, TOV1) && !(captured_low & 0x8000)) {
captured_high++;
}
captured_timestamp = (uint32_t) captured_high << 16 | captured_low;
did_capture = true;
}