Advice on button debounce interrupts

mckenney

Joined Nov 10, 2018
125
And this version still doesn't do any debouncing. You're kind of making it hard on yourself trying to do too many things at once -- debouncing two buttons, waiting for one to be pushed, and blinking an LED -- which is hard to get right. Once you get there it will probably look something like:
Code:
#pragma vector = PORT1_VECTOR
__interrupt void Port_1(void)   //Engine - for engine to gain control, bridge must accept!
{
    __delay_cycles(DEBOUNCE_PERIOD);    // Debounce P1:ECR_TAKE
    if ((P1IN & ECR_TAKE) == 0)         // Pushed, not released
    {
        P2OUT &= ~BRIDGE_CMD_LED;       // Commit
        while(1)    //Loop While NOT pressed is it supposed to be
        {
            if ((P2IN & BRIDGE_TAKE) == 0)   // Button pressed, see if it stays
            {
                __delay_cycles(DEBOUNCE_PERIOD); // Debounce P2:BRIDGE_TAKE
                if ((P2IN & BRIDGE_TAKE) == 0)
                    break;              // Pushed for real
            }
            P1OUT ^= ECR_CMD_LED;       //Toggle engine command while waiting for bridge to accept
            __delay_cycles(220000);     //Blink, blink
        }
        P1OUT |= ECR_CMD_LED;
    }
    P1IFG &= ~ECR_TAKE;             // We dealt with the ECR request
    P2IFG &= ~BRIDGE_TAKE;          // We dealt with the bridge acceptance
}
which is a bit of a mouthful.
 

Thread Starter

StrongPenguin

Joined Jun 9, 2018
307
The new plan was to debounce it in hardware, so I didn't have to worry about it in software. But this is not possible? Your solution was nothing I had imagined. Is working with buttons always this difficult?

Changing to PxOUT = 0x00 from PxOUT &= ~BRIDGE was just a desperate attempt at making things work, because it seemed like the line got skipped. Didn't think about the pull up resistor, thought.
 

danadak

Joined Mar 10, 2018
4,057
Ok, then I will hold out for a real one. I have enough scrappy tools as is.
What you want, at minimum, is a scope that has FFT, 50 Mhz or better, 200 MSPS or
greater sample rate, can trigger off runt pulses and pulses of specified width. People can
argue about memory depth, more is better. Search this forum, several threads discussion
about experiences and capabilities desired/needed.

One of the best scopes today is DS1054Z, ~ $350, 4 channel, tons of triggering capabilities,
FFT, PC interface, deep memory. Digital Filter. Can be hacked to 100 Mhz.

There are older scopes, 500 Mhz, 1 - 2 GSPS, 4 channel that sometimes can be found
for < $200. Memory and triggering basic/limited, check for FFT option installed. Tek TDS
400/500/600/700 series.

Regards, Dana.
 

mckenney

Joined Nov 10, 2018
125
Is working with buttons always this difficult?
They can be tamed, but they're still physical objects, with all the annoyance that goes with that.

I'm going to make one more pitch for a state machine, which I think simplifies things considerably. States aren't magic, they tell you not only where you are but (to some extent) how you got there.

The idea is that the ISR/debouncer does one thing -- detecting and registering a button push -- and leaves everything else to some other entity. Starting with:
Code:
#define DEBOUNCE_PERIOD     5000    // cycles
#define BLINK_PERIOD        220000

#define S_BRIDGE_OWNS       0
#define S_ECR_REQ           1
#define S_ECR_OWNS          2
#define S_BRIDGE_REQ        3
volatile unsigned gstate = S_ECR_OWNS;
The ISRs look like:
Code:
#pragma vector = PORT1_VECTOR
__interrupt void Port_1(void)   //Engine - for engine to gain control, bridge must accept!
{
    __delay_cycles(DEBOUNCE_PERIOD);
    P1IFG &= ~ECR_TAKE;
    if ((P1IN & ECR_TAKE) == 0)             // Pushed, not released
    {
        if (gstate == S_BRIDGE_OWNS)        // Request from ECR
            gstate = S_ECR_REQ;
        else if (gstate == S_BRIDGE_REQ)    // Acknowledgment to Bridge
            gstate = S_BRIDGE_OWNS;
    }
}

#pragma vector = PORT2_VECTOR
__interrupt void Port_2(void)   //Bridge - for bridge to gain control, engine must accept!
{
    __delay_cycles(DEBOUNCE_PERIOD);
    P2IFG &= ~BRIDGE_TAKE;
    if ((P2IN & BRIDGE_TAKE) == 0)             // Pushed, not released
    {
        if (gstate == S_ECR_OWNS)               // Request from Bridge
            gstate = S_BRIDGE_REQ;
        else if (gstate == S_ECR_REQ)           // Acknowledgment to ECR
            gstate = S_ECR_OWNS;
    }
}
The LEDs are handled in main (up to 220ms latency, but you already had that):
Code:
    while(1)
    {
        //  Display forever.
        //  Just setting the LEDs the way we want (level)
        //  is cheaper than figuring it out (edge).
        unsigned state;
        state = gstate;
        if (state == S_BRIDGE_OWNS)
        {
            P2OUT |= BRIDGE_CMD_LED;        // Show that the bridge owns it
            P1OUT &= ~ECR_CMD_LED;
        }
        else if (state == S_ECR_OWNS)
        {
            P2OUT &= ~BRIDGE_CMD_LED;
            P1OUT |= ECR_CMD_LED;           // Show that the ECR owns it
        }
        else if (state == S_ECR_REQ)
        {
            P2OUT &= ~BRIDGE_CMD_LED;
            P1OUT ^= ECR_CMD_LED;           // Blink the ECR LED
        }
        else if (state == S_BRIDGE_REQ)
        {
            P2OUT ^= BRIDGE_CMD_LED;        //
            P1OUT &= ~ECR_CMD_LED;
        }
        else                                // Goof
        {
            P2OUT ^= BRIDGE_CMD_LED;        // Blink 'em both
            P1OUT ^= ECR_CMD_LED;
        }
        __delay_cycles(BLINK_PERIOD);
    }
This is intended to be simple and transparent. There are many variations.
 

mckenney

Joined Nov 10, 2018
125
The new plan was to debounce it in hardware, so I didn't have to worry about it in software.
Sorry I guess I lost track. I'm mostly a software guy, so I can't help you much with hardware debouncing.

I will point out that the time constant can vary quite a bit from one button (part#) to another. In hardware, that may mean re-soldering caps, but in software it's just a change of DEBOUNCE_PERIOD (above).
 

danadak

Joined Mar 10, 2018
4,057
I have seen buttons exceed 100 mS in their bounce, but so far never hitting 200 mS.

FYI.

I also like that some processors have a HW bounce facility in them, just instantiate
it and hook it up to a pin. Can bounce either or both edges.

upload_2018-12-15_11-0-29.png

3'rd column over, 2'ond down. Above just some of stuff on part. Another page
for its analog stuff.


Regards, Dana.
 

Thread Starter

StrongPenguin

Joined Jun 9, 2018
307
Ok, now I'm just being dumb and stubborn. I got this logic to work with a ridiculous debounce time. I started out at 1000000 (1M), and at 500000 it started to break down, being unstable. At 600000 delay cycles, it works just fine. I read in the datasheet that the SMCLK is at 1 MHz, which results in a 600 ms delay. Something is off, that's for sure. But never mind, I'm finally at peace.

I know the code is useless, but I (again) learned a few things.

Code:
    while(1)
    {
        __delay_cycles(DEB);               //Debounce, damn you!
        if((P2IN & BRIDGE_TAKE) == 1)      //Bridge function
        {
            while((P1IN & ECR_TAKE) == 0)
            {
                __delay_cycles(220000);
                P2OUT ^= BRIDGE_CMD_LED;
            }
            __delay_cycles(DEB);
            P1OUT &= ~ECR_CMD_LED;
            P2OUT |= BRIDGE_CMD_LED;
        }
        __delay_cycles(DEB);
        if((P1IN & ECR_TAKE) == 1)      //Engine department
        {
            while((P2IN & BRIDGE_TAKE) == 0)
            {
                __delay_cycles(220000);
                P1OUT ^= ECR_CMD_LED;
            }
            __delay_cycles(DEB);
            P2OUT &= ~BRIDGE_CMD_LED ;
            P1OUT |= ECR_CMD_LED;
        }
    }

    return 0;
}
 

ebeowulf17

Joined Aug 12, 2014
3,307
@danadak That's pretty sweet! I feel however I need to get better at the basics, before I go to that level.

@mckenney I am trying to implement your code as we speak. Well return with some feedback later.
I'm no expert at most of this, but I have done a lot of projects with debounced switches. I generally prefer software solutions because of the flexibility and the simpler physical build.

On the other hand, if software resources are limited, or if you're just having trouble getting it to cooperate with other code, the hardware solution should be pretty much bulletproof, it's much simpler to wrap your head around, and it doesn't take any system resources.

Here's the critical hardware debounce question - what R and C values did you use?
 

Thread Starter

StrongPenguin

Joined Jun 9, 2018
307
@ebeowulf17 0.1 uF cap and a 10M resistor. Don't have any 1M. That is on the 555 circuit.

Used 10k and 0.1 uF on the Hex Schmitt setup.

The switches are extremely poor, you can hear the spring going bonkers after the release.
 

ebeowulf17

Joined Aug 12, 2014
3,307
0.1 uF cap and a 10M resistor. Don't have any 1M. That is on the 555 circuit.

Used 10k and 0.1 uF on the Hex Schmitt setup.
Unless I've slipped a digit somewhere, your 10k and 0.1uF yields a 1ms time constant, which is way, way too low. I don't remember the exact relationship between the RC time constant and the debounce time with a Schmitt trigger, but I think they're sort of in the same ballpark.

Switch bounce can easily be 20-50ms, and apparently up to 100-200ms in some cases, so a 1ms time constant is effectively doing nothing. I bet if you run the debounce with a 100ms time constant you get better results.

*** EDIT:
I overlooked the part where you said you have exceptionally slow, boingy switches. In that case you really might end up needing something more extreme, like the 500ms implied by your recent software debounce success. In any case, the 1ms time constant never had a chance. The Schmitt trigger setup should definitely be viable if you're still interested in it, but you'll need a much longer time constant than you were trying the first time. Cheers!
 
Last edited:

Thread Starter

StrongPenguin

Joined Jun 9, 2018
307
I think that's the case here, the switch being an extreme example. The only reason I haven't thrown it overboard yet, is that I want to analyze it when I finally get my scope.
 

mckenney

Joined Nov 10, 2018
125
At 600000 delay cycles, it works just fine.
600ms of bounce is pretty impressive.I think the most Jack Ganssle saw was about 150ms (release only) but most were <10ms.

My EE once designed-in a button for me which didn't bounce at all. He said he just picked it because it was small and cheap. When I went to test my debouncer, I couldn't see anything. I took the scope down to 10s of nsec, and the most I could pick out was a little bit of ring. Ya never know.
 
Top