I am trying to do quadrature decoding using atmel xmega avr microcontroller. Xmega has only 16-bit
counters. And in addition I hav
One solution is:
uint16_t a, b, c;
do {
a = timer_over_flow;
b = timer_16bit_count;
c = timer_over_flow;
} while (a != c);
uint32_t counter = (uint32_t) a << 16 | b;
Per comment from user5329483, this must not be used with interrupts disabled, since the hardware counter fetched into b
may be changing while the interrupt service routine (ISR) that modifies timer_over_flow
would not run if interrupts are disabled. It is necessary that the ISR interrupt this code if a wrap occurs during it.
This gets the counters and checks whether the high word changed. If it did, this code tries again. When the loop exits, we know the low word did not wrap during the reads. (Unless there is a possibility we read the high word, then the low word wrapped, then we read the low word, then it wrapped the other way, then we read the high word. If that can happen in your system, an alternative is to add a flag that the ISR sets when the high word changes. The reader would clear the flag, read the timer words, and read the flag. If the flag is set, it tries again.)
Note that timer_over_flow
, timer_16bit_count
, and the flag, if used, must be volatile
.
If the wrap-two-times scenario cannot happen, then you can eliminate the loop:
a
, b
, and c
as above.b
to 0x8000
.b
has a high value, either there was no wrap, it was read before a wrap upward (0xffff to 0), or it was read after a wrap downward. Use the lower of a
or c
.b
was read after a wrap upward, or it was read before a wrap downward. Use the larger of a
or c
.