Debouncing

the button

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?

Links
http://www.rentron.com/Myke6.htm http://www.ganssle.com/debouncing.pdf
 
 
victor@avrtutor.com