Debouncing a momentary switch

Your source for all things Atmel.

Moderator: phalanx

Post Reply
space cake
Posts: 9
Joined: Tue Apr 28, 2009 3:59 pm

Debouncing a momentary switch

Post by space cake » Sat Sep 12, 2009 2:25 pm

I'm using a momentary push-button type switch hooked up to INT0 pin of an ATMEGA168. I want this switch to put the AVR into sleep mode and wake it up. This would be much easier using a latching type, however my application calls for a momentary switch for this task. The pins are held high until pulled low when the switch shorts.

The problem here is that I can't use the normal "polling" methods since I might miss some buttons being pushed.

I thought about disabling the interrupt when it is fired, do whatever it supposed to do and start a hardware timer (CTC or overflow) that will call an interrupt that will enable the interrupt after 250ms or something like that. This way the main code can continue to run and the counter won't be executed while in the ISR.

The problem is that even when I disable the interrupt, a single request will be put in the stack by the AVR (please correct me if this is more than one call, since this is the entire idea behind my code) when the interrupt will be enabled again, the interrupt will fire because 100% the switch will bounce at least once. So to solve this I can define a char variable that will act like a boolean variable. When the AVR starts up, this variable will be: enable=1. When the interrupt will fire it will check if enable=1, if so it will run the interrupt and will change enable=0. After the interrupt will be enabled again, after the counter counted to 250ms, it will fire 1 more time but enable=0 so it won't do anything other than setting enable=1 so the next time the interrupt will fire it will run the required code.

Any thoughts on this, suggestions?

krphop
Posts: 29
Joined: Sat Aug 29, 2009 6:31 am

Post by krphop » Sun Sep 13, 2009 9:47 am

You should be debouncing pretty much any switch that you use on a MCU, or at least I've had problems with most switches when not debouncing. I'm not sure if I understand exactly everything you want your program to do, but you mentioned you wanted a button go toggle the MCU between awake and asleep modes. I've used sleep modes in the past, and simply had timeouts, so you push buttons and do the required input, and after a period of time if no buttons are pushed, the MCU goes back to sleep. A specific button press then woke up the MCU until the timeout was exceeded again. Using a button to put the MCU to sleep could be done as well, and you could do debounce stuff inside your interrupt, but that just seems nasty even tho delays of <10mS are generally required for debounce.

Here's a few bits of code from a wireless battery powered remote I use to control a LED lamp. The remote itself has 3 buttons and a status LED.

This is the button debounce function, anytime input was required from a button this function is called to debounce the input before actually reading the input. The vars in here are global, yeah yeah, it could be modified to simply use returns, but I'm lazy:

Code: Select all

void buttonDebounce(byte buttonArray[3])
{
  byte buttonRead = digitalRead(buttonArray[buttonPin]);
  if (buttonRead != buttonArray[buttonPrevState])
  {
    lastDebounceTime = millis();
  }
  if (millis() - lastDebounceTime > debounceDelay)
    buttonArray[buttonCurrState] = buttonRead;
  buttonArray[buttonPrevState] = buttonRead;
}
Here's the code to put the MCU to sleep, as well as wake up on a button press:

Code: Select all

  // Uses last debounce time to determine when teh last time a button was pushed
  if (millis() - lastDebounceTime > buttonTimeout)
  {
    // Turn off all the LED's
    ledArray[ledRed][ledPrevColor] = 0;
    updateLed(ledArray[ledRed]);
    ledArray[ledGreen][ledPrevColor] = 0;
    updateLed(ledArray[ledGreen]);
    ledArray[ledBlue][ledPrevColor] = 0;
    updateLed(ledArray[ledBlue]);
    
    // Prep MCU to go into sleep mode
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    attachInterrupt(0, mcuWakeUp, CHANGE);
    digitalWrite(3, LOW); // For some reason, going to sleep turns on this pin...WTF?
    sleep_mode();  // Put MCU to sleep, code will pickup here after it comes back
    sleep_disable();
    detachInterrupt(0);
    
    // After wake up, reset counters
    buttonCounter = 0;
    lastDebounceTime = millis();
  }
If you want to see the whole example code, let me know and I can post the whole thing.

If all else fails, and you really want a button to toggle sleep/wake modes, do a hardware debounce with a RC time circuit and a Schmidt trigger

ptaa32
Posts: 6
Joined: Mon Jan 11, 2010 3:54 am
Location: Bergen, Norway

Post by ptaa32 » Mon Jan 11, 2010 4:14 am

Hi,

I would be very interested in getting your complete sample code.

Regards,

Per-Tore

mac
Posts: 44
Joined: Fri Dec 04, 2009 2:15 am

Post by mac » Thu Jan 21, 2010 4:12 am

You should use a mix of interrupt/polling.

For detecting switch when running, use polling and debounce it.
When the decision is taken to go into sleep/powersave mode, program external interupt on pin.

When the interrupt fire, this basically mean that the switch is closed, or beiing closed (or EMI). Disable the interrupt source, then process the debounce.
If debounce shows that switch is closed, continue your process. If not, reprogram interrupt when state is stable and goes to sleep again.

Your switch must be on one of the INTx pins for this to work.

This article is great on debouncing methods: http://www.ganssle.com/debouncing.pdf

Post Reply