embeddedavrblinkdebouncingatmega32

AVR interrupt debouncing issues


I don't how to eliminate the effects of small oscillations when pressing a switch. I have a primitive switch and a led and I tried to create an interrupt that makes the LED blink when the switch is pressed. I did it in such a way that the interrupt fires on the falling edge of the input signal.


#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

ISR(INT0_vect){
    PORTD ^= 1;
}

int main(void)
{
    DDRD = 1;//we set for input D port
    PORTD |= 1<<2;//pull-up for interrupt pin
    EICRA &= ~3;//clear EICRA and activate INT0 on LOW
    EICRA |= 2;//activate INT0 on falling edge
    SREG |= 1<<7;//I bit from SREG has to be enabled
    EIMSK |= 1;//enable INT0
    while (1) 
    {
        EIFR |= 1;//I've tried to clear the corresponding flag, but in vain
    }
}


Solution

  • To debounce correctly, you must measure time in some way and you can do two things:

    1. Ensure that a certain thing is stable (this is not actually debouncing). For example, if your button had long wires, you could be worried of noises which could pollute the signal in. So you read the signal one time, and then a second time a little later, to check that the signal is still there and it was not noise. Perfect would be to monitor the signal for a period of time and, only if the signal didn't change in that period of time, take it as valid.

    2. Proper debouncing. When a signal from inactive state goes to active, you must ignore shorts falls (active-inactive-active) of the signal.

    Your button gives you a low level when pressed, and you can ignore the noise. So, as soon the signal goes low, you know the button has been pressed. This is the perfect scenario for an interrupt. But, if the button bounces, it will lower and rise the signal very quickly - too much quickly for a human being, but not too much for an MCU. What you have to do is to ignore further "pressures" for a while, for example 1 millisecond, or 5, or 1/100 of a second, or so. Then, you have to count time (another interrupt, presumably).

    Suppose you have an interrupt routine that fires every millisecond. In that routine, decrement a counter (if it is > 0). When the button is pressed, and the counter is zero, accept the button press and set the counter to a value. Further pressures of the button will be ignored, until the counter will "slowly" decay to zero thanks to the other routine (in interrupt) which measures time.

    This is just an idea, there are many many ways to do just the same or even better (noise canceling).

    In your case, you could also just sleep() (or do a delay loop) inside the interrupt routine you already have (not beautiful), or disable that interrupt for a given time (so you must measure time).