-2
\$\begingroup\$

I want to toggle a led with a push button and an ATMega16a but for some reason the toggle behavior seems a bit randomly.

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

/* if true we toggle PB0 */
volatile uint8_t toggle = 0;
/* contains last value of PD2 */
volatile uint8_t newval = 0;
volatile uint8_t oldval = 0;

int main(void) {
    /* set PB0 as output and PD2 as input */
    DDRB |=  (1 << PB0);
    DDRD &= ~(1 << PD2);

    /* enable timer such that we can pool every 16ms */
    TIMSK |= (1 << TOIE0);
    TCCR0 |= (1 << CS00) | (1 << CS01);

    sei();

    for (;;) {
        if (toggle) {
            PORTB ^= (1 << PB0);
        }
    }

    return 0;
}

ISR (TIMER0_OVF_vect) {
    /* get new input value */
    newval = PIND & (1 << PD2);

    /* only handles button presses */
    if (newval && !oldval) {
        if (toggle) {
            toggle = 0;
        } else {
            toggle = 1;
        }
    }

    oldval = newval;
}

Is there a problem with the code or is it a hardware problem?

\$\endgroup\$
2
\$\begingroup\$

A typical push-button switch will have significant bounce, which can last for many tens of milliseconds, depending on the particular switch.

It is perfectly possible that when you close the switch, at the time the ISR handler gets control, the switch contact is bouncing. You need to do multiple reads of the switch (or externally debounce it).

Statistically, there will be some occasions where your timer interrupt occurs at the same time as the switch is bouncing.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I think I got the theory but what about the code? Does it seem correct? I can't identify the error as I do multiple reads on the input pin. \$\endgroup\$ – bodokaiser Mar 18 '16 at 17:19
  • 1
    \$\begingroup\$ There is actually only one read in the test. If you had an array of newval[2], and on 3 successive timer interrupts made sure those were the same, you would properly debounce the switch. \$\endgroup\$ – Peter Smith Mar 18 '16 at 17:29
0
\$\begingroup\$

Depending on how your hardware is setup, you could always reduce the bounce with a capacitor and resistor circuit. Similar to the image below:

Switch De-bouncing

This will and a time constant, but you could adjust your R and C relationship to reduce the rise/fall time to fit your needs.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

This is how I do debouncing in timer interrupt. The example below also reads rotary encoder, and also returns 'pressed' and 'released' states of the button:


    //encoder on RA2-3, shaft button on RA1
uint8_t tmp;
static const int8_t const enc_states[] =         //encoder lookup table
    {0,ENCD_SIG,ENCI_SIG,0,ENCI_SIG,0,0,ENCD_SIG,ENCD_SIG,0,0,ENCI_SIG,0,ENCI_SIG,ENCD_SIG,0};

static uint8_t old_AB = 0;    //previous state of the encoder
static uint8_t debounce_state = 0U;
static uint8_t btn_debounced = 0U;

_T1IF = 0;                              /* clear interrupt */

tmp = PORTA;    //read encoder

//process encoder
old_AB >>= 2;                //remember previous state
old_AB |= ( tmp & 0x0c );  //add current state

if( enc_states[(old_AB & 0x0f)]) { //encoder changed state
    QACTIVE_POST_X_ISR((QActive *)&AO_Console,
        1, enc_states[(old_AB & 0x0f)], 0U);
}

/* read and debouce encoder shaft button */

tmp = EBUT_PIN;               /* read the shaft button */

switch (debounce_state) {
    case 0:
        if (tmp != btn_debounced) {
            debounce_state = 1U;        /* transition to the next state */
        }
        break;
    case 1:
        if (tmp != btn_debounced) {
            debounce_state = 2U;        /* transition to the next state */
        }
        else {
            debounce_state = 0U;          /* transition back to state 0 */
        }
        break;
    case 2:
        if (tmp != btn_debounced) {
            debounce_state = 3U;        /* transition to the next state */
        }
        else {
            debounce_state = 0U;        /* transition back to state 0 */
        }
        break;
    case 3:
        if (tmp != btn_debounced) {
            btn_debounced = tmp;        /* save the debounced button value */

            if (tmp == 0U) {            /* is the button depressed? */
                QACTIVE_POST_X_ISR((QActive*)&AO_Console, 1,
                    EBUT_PRESS_SIG, 0U);
            }
            else {
                QACTIVE_POST_X_ISR((QActive*)&AO_Console, 1,
                    EBUT_RELEASE_SIG, 0U);
            }
        }
        debounce_state = 0U;              /* transition back to state 0 */
        break;
}//switch(debounce_state...

QACTIVE_POST_X_ISR((QActive*)&AO_Console, 1,
    TICK_SIG, 0U);

QF_tickISR();                /* handle all armed time events in QF-nano */

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

I found the problem: It did not really had something to do with debouncing but with the state of the input pin. I needed to put the internal pull up resistor of the input pin up PORTD |= (1 << PD2);.

| improve this answer | |
\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.