SIGNAL(SIG_OVERFLOW1)
{
}
A very common thing to do is to keep track of a tick counter. This way, the rest of the program can get a sense of time. The first cut is simply to use a global variable as follows:
unsigned long tick_counter = 0;
SIGNAL(SIG_OVERFLOW1)
{
++tick_counter;
}
Then the rest of the program can track time as follows:
{
unsigned long first_tick;
while (PINA & (1 << PB1)); // wait for PB1 be pressed
first_tick = tick_counter; // mark the time
while (!(PINA & (1 << PB1)); // wait for PB1 be released
duration = tick_counter - first_tick;
}
This code attempts to time how long a push button has been kept
pushed. Even though tick_counter can overflow, the
unsigned subtraction actually is insensitive to overflows. As long
as the duration to measure does not exceed
,
duration still reflects the number of ticks that has passed.
This code does have a serious problem. As we read tick_counter
(which is a 32-bit integer), the timer interrupt can occur. This is
very bad. We can be halfway loading the 32-bit integer into
registers when this happens. The ISR will complete without any
problems and update tick_counter. However, as it returns,
we resume to load the other part of the now-updated
tick_counter into registers. This means half of the bits of
the registers comes from the ``original'' tick_counter,
while the other portion comes from the ``updated''
tick_counter. What if tick_counter wraps around to
0 in the ISR?
Besides, gcc is a smart compiler. It is likely to optimize
the program and just assume that ``nothing is going to happen to
tick_counter, so it may just decide that duration
always get 0. In other words, by default, gcc
assumes a global variable is not modified by ISRs.
To fix this code, we need to use the following definition for
tick_counter:
volatile unsigned long tick_counter;
The reserved word volatile tells the compiler that
``anything can happen to this variable at any time''. However,
we still have not fixed the first problem (getting interrupted
when a multi-byte variable is getting loaded or stored).
The solution to the first problem is a little longer. We'll define a subroutine first:
volatile unsigned long get_tick_counter(void)
{
unsigned char oldSREG = SREG;
unsigned long tmp;
_CLI(); // disable interrupt
tmp = tick_counter;
if (SREG & (1 << SREG_I)) _SEI();
return tmp;
}
Again, volatile tells the compiler that this subroutine has
the potential to return something different everytime it is called.
This subroutine has the wrapper to disable interrupt first, then
restore the interrupt flag at the end. The key of this subroutine
is that we capture the tick_counter value after interrupt
is disabled. This guarantees the operation is ``atomic''. The
captured value in local variable tmp is then returned at the
end.
Having defined get_tick_counter, we just need to change all
references to tick_counter to get_tick_counter() in the
program.