|
If you played with the button, you have possibly
realized what the associated problems are. When a button is pressed
it does not produce a clean signal, it bounces up and down and the
settling time may reach 500ms. Our beloved microcontroller will read crap
if it polls the button during the settling period. So, when to poll it?
How frequently? And for how long? Well, the truth is, polling can work
but its a bit of a pain to get the timing right. And again, if you change
the button, settling time changes and the code is not usable.
So, think of a better solution, we could have the button
connected to pin 4 where the external interrupt INT0 is and deal with it
in an ISR. Hmm, the thing is, due to bouncing you would end up with multiple
interrupts. We could possibly count you think, delay a bit, let the button
settle down and then see what its state is. Count? For how long? A delay in
an ISR cannot be tolerated, stick to the fact that ISRs have to be quick.
Time is the key, we could poll the button at constant time
intervals for a period of time. This period of time should be something around
300-500ms. If less, bouncing may not be over, if more we may miss a click.
We are going to use a timer and its overflow interrupt. An 8-bit timer is
just a counter, counting from 0 to 255 at the micro's clock frequency.
One clock period for a single counter step. Thus for an 7.3728MHz clock,
the interrupt will hit every 256/7.3728MHz = 34.72us. Adding an 1/1024
timer prescaler will make this delay 34.72us*1024 = 35.55ms.
Hmm, looks like we have a descent delay that will not delay at all our
code. Now, since we have a delay of about 35ms we can poll the button
every 35ms and if for 10 polls its state is the same, we accept it as the
new valid button state.
The timer will keep running, the variable that holds
the button state will keep refreshing but, the important thing here is that
are code, in our superloop, will not experience any delay at all. You see,
in our example polling and interrupts work well together. We poll the button
but, we use interrupts to define the polling interval.
Try to do it. What you need is straightforward, initialize
Timer0 enabling overflow interrupt and write the ISR.
/// Includes //////////
/// include ...
/// Defines ///////////
/// define btn, ledon, ledoff
/// Routines //////////
/// InitUART, InitPorts, InitTimer
/// Globals ///////////
static volatile u8 btnState;
int main(void)
{
InitUART(23); InitPorts();
InitTimer();
sei ();
while (forever)
{
if (btnState) ledon;
else ledoff;
}
}
void InitTimer(void)
{
// Reset timer
TCNT0 = 0;
// Set prescaler 1/1024
TCCR0 |= 0x05;
// Enable overflow interrupt
TIMSK |= 1<<TOIE0;
}
// Timer Overflow ISR
SIGNAL(SIG_OVERFLOW0)
{
static volatile u8 oldState, newState, count;
// Read button's state
newState = btn;
// If remains the same, advance counter
if (newState==oldState) count++;
// If not, reset counter
else count=0;
// Roll, for the next time
oldState = newState;
// If it is stable, accept it and reset counter
if (count>10)
{
btnState = newState;
count=0;
}
}
Easy to follow I think, we are just switching on the LED
on button down. Note, that our variables should preserve their values
on successive ISR calls. Last thing, if you add a capacitor in parallel
with the button, the required stability period may decrease. With this setup,
I usually count up to 5. A final thought, when you have a task that can be
handled in software or in hardware, prefer software, it costs less. What would
you do if you had to debounce 12 buttons?
|