This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP-EXP430F5529 Button Interrupt Debounce Example Code



I am trying to implement an enhanced version of the interrupt code based on the experimenter board's example button debounce code.  I notice in the example code the interrupt handler, Port1_ISR, uses this code for debouncing:

        // Vector  P1IV_P1IFG7:  P1IV P1IFG.7
        case  P1IV_P1IFG7:
            if (buttonDebounce == 1)
            {
                buttonDebounce = 2;
                Buttons_startWDT();
                __bic_SR_register_on_exit(LPM3_bits);
            }
            else if (buttonDebounce == 0)
            {
                __bic_SR_register_on_exit(LPM4_bits);
            }

            break;

This appears to only handle a single (simultaneous) interrupt on port 1, although this code is also repeated for the Port 2 ISR handler.  I'm wondering what needs to change in order to accommodate multiple interrupts on the same port on different port pins.  It seems using the same buttonDebounce variable in both the P1 and P2 (or even for multiple P1 interrupts) is problematic.

I'm not sure the WDT can be used for debouncing in a case where simultaneous P1 interrupts occur on separate pins.

Any help is greatly appreciated. 

Thanks.

  • As often with provided code, there are some smart programming tricks used without proper explanation about the why and how.

    What this code does:

    It reads the P1IV register in a switch statement. This register returns the number of the highest, priority interrupt on any of the associated port pins as an even 16bit value.
    To handle multiple port pins, you can add different case statements.

    However, per read of P1IV, only one 'event' is reported. Multiple reads (until it returns 0 for no more events in queue) are necessary to handle all - or if the ISR leaves with unhandled events, it is simple reentered immediately.

    If a button press is detected on the port pin, two things can happen. Either 'buttonDebounce' is 0, then the ISR exits LPM4. Or, if buttonDebounce is 1, it is incremented to 2 and the WDT is started and the ISR exits LPM3. Or if buttonDebounce is >1 (e.g.2), th einterrupt is ignored.

    First, I must say I don't see what the exit of LPM3 or 4 means. I don't know how the clock system is initialized and which LPM is initially entered.
    It looks like the code is attempting to either reactivate the system (exit LPM4) or just to activate ACLK (which is active in LPM3) and leave the CPU sleeping.
    Which won't work this way: the code should read
    __bic_SR_register_on_exit(LPM4_bits); __bis_SR_regsiter_on_exit(LPM3_bits);

    instead, to switch from LPM4 to LPM3 instead of 'leaving LPM3' (which would wake the CPU but leave ACLK disabled, quite the opposite than what seems to be intended)

    Now about debouncing multiple port pins. This is difficult.
    First, you need to signal the state of each individual port pin, so you would need multiple instances of buttonDebounce.
    And you would need multiple WDTs, because a button event on one pin may not retrigger the WDT for an already 'running' debounce, nor may an already runnign debounce shorten the debounce time for following events.

    How I would solve this:

    Don't use the WDT (hey, the WDT is there as a watchdog against an EMI/ESD caused CPU crash. Abusing it as a timer shouldn't be done without need). use a normal timer, that triggers an interrupt e.g. every millisecond.

    Set individual signed char counter variables for each port pin in use. If it is negative, set the counter to -1 and immediately exit LPM. If it is non-0, ignore it. if it is 0, set it to the desired debounce time (maximum 127) and enable ACLK as described above (which starts the timer if it is not already running).

    In the timer ISR, decrement all counters if they are >0. If one or more count to 0, check the current port pin state (to detect whether it was a button press or a glitch) and when it is indeed a button press , set the counter to-1 and exit LPM. If all counters are <1, disable the timer.

    Main can see, when LPM is exited, which one is -1, and can reset the ones that are handles, either to 0 if debounce is wanted, or any negative value <-1 if no debounce is wanted.

    However, there is a possible glitch: if a debounce is started while main is active, and main enters LPM4, this will stop the debounce delay.
    (I guess this will also happen with the demo code). So some additional handshaking might be needed, or put the code that checks for pressed buttons and enters LPM into an atomic section (clear GIE, check for ongoing debounces, and enter either LPM4 or LPM3 depending on the result of this check).

  • Thanks for the (very thorough) response.  Very helpful.

    I figured a buttonDebounce variable would be needed for each pin.  The existing code already has case statements to handle each potential pin interrupt. 

    Yeah, I'm not sure why they use the WDT in the example unless it's necessary for the LPM they are using.  I'm not sure if there are other issues using a non-WDT for this.

    I'm not sure if the system call to enter LPM is already an atomic operation and therefore, no glitch.

    Thanks.

  • Anthony Massa said:
    Yeah, I'm not sure why they use the WDT in the example unless it's necessary for the LPM they are using. 

    The WDT is relatively easy to setup. It is already running on power-on default, you only have to switch it to counter mode. It has a fallback to a clock source that is running (on newer MSPs) or you cannot disable the clock source of the WDT even if the LPM would normally do it (on older MSPs).

    So if one thinks he can live wihout the WDT workign as WDT, it is a cheap timer that doesn't consume othe rressources and ensures that the MSP will eventually wake up again, even if there are not many ays to influence this time (fixed timeout length 32768 clock ticks, clock may switch).

    Anthony Massa said:
    I'm not sure if the system call to enter LPM is already an atomic operation and therefore, no glitch.

    Yes and no. It's not a system call. It basically sets some bits in the processor status register. THis is a direct processor instruction like clearign the overflow flag or checking the zero flag.

    Howeve,r there are some side-effects. Due to the pipelinedstructure of the MSp core, teh instruction immediately after the LPM instruction is already fetched when the LPM is entered. THis means, a breakpoint set on the instruction behind the LPM instruction will eb triggered the moment the LPM is entered, not when it is exited.

    Also, this instruction will be executed before calling the ISR that temporarily ends the LPM (because it is already fetched andqueued). So it is a good advice to put a NOP right behind, and never put a breakpoint on it.

**Attention** This is a public forum