PIC 中的去抖动按钮

Debounce button in PIC

我是PIC mcu的新手。我使用 pic12f675 MPLAB 和 XC8 制作 LED 多次闪烁模式。 我有按钮的问题(在审查它调用弹跳和去抖动之后)。 有时当我按下按钮时,它会依次执行。 1->2->3->4->5 但有时它会跳转。 1->3->4->6 等

请教我如何在pic mcu中去抖或其他方法来解决我的问题。

谢谢。大家

(PS.I 将按钮与 10K 电阻连接)

我的代码在下面

    #include <xc.h>
    #pragma config FOSC=INTRCIO,WDTE=OFF,MCLRE=OFF,BOREN=OFF
    #define _XTAL_FREQ 4000000
    
    int cnt = 0;
    int k = 0;

void __interrupt() MyISR(void){
   
    if(INTCONbits.INTF)  //If External Edge INT Interrupt
    {
        
        cnt++;
        INTCONbits.GIE = 0;
            
        INTCONbits.INTF = 0;    // Clear the interrupt
        
        INTCONbits.GPIF = 0;
        
        
        if( cnt > 6 ){
            cnt = 1;
            
        }
        
    }
    
    INTCONbits.GIE = 1;
    
}

void main(void) {
    ANSEL = 0;
    CMCON = 0b00000111; //turns comparators off
    TRISIO = 0b00000100;
    GPIO   = 0;
    
    TRISIO2 = 1;                // Make GP2 pin as input
    
    INTCONbits.GIE = 1;
    INTCONbits.INTE = 1;
    INTCONbits.GPIF = 1;
    INTCONbits.INTF = 0;

    
    OPTION_REG = 0b01000000;   

    while(1){
     
        if( cnt == 1 ){
            GP0 = 1;
            GP5 = 1;
        }else if( cnt == 2 ){
            
            for(k=0;k<30;k++){
                GP5 = 1;
                GP0 = 1;
            }
            k=0;
            while(k<3){
                GP5 = ~GP5;
                __delay_ms(70);
                GP0 = ~GP0;
                __delay_ms(70);
                k++;
            }
        }else if( cnt == 3 ){
            for(k=0;k<5;k++){
                GP5 = 1;
                GP0 = 1;
                __delay_ms(70);
                GP5 = 0;
                GP0 = 0;
                __delay_ms(70);
            }
            GP5 = 0;
            GP0 = 0;
            __delay_ms(1200);
            
            
        }else if( cnt == 4 ){
            
            for(k=0;k<3;k++){
                GP0 = 1;
                __delay_ms(50);
                GP0 = 0;
                __delay_ms(50);
            }
            
            
            for(k=0;k<3;k++){
                GP5 = 1;
                __delay_ms(50);
                GP5 = 0; 
                __delay_ms(50);
            }
            
            
        }else if( cnt == 5 ){
            
            GP0 = 1;
            GP5 = 1;
            for(k=0;k<3;k++){
                GP5 = 1;
                __delay_ms(50);
                GP5 = 0; 
                __delay_ms(50);
            }
            GP0 = 1;
            GP5 = 1;
            for(k=0;k<3;k++){
                GP0 = 1;
                __delay_ms(50);
                GP0 = 0; 
                __delay_ms(50);
            }
            
        }else if( cnt == 6 ){
            
            GP0 = 1;
            GP5 = 1;
            __delay_ms(20);
            GP0 = 0;
            GP5 = 0;
            __delay_ms(3000);
            
        }
        
    }
    return;
}

我重写了您的代码并在 MPLAB 仿真中对其进行了测试。它按预期工作。它按升序更改模式,然后在所选模式中 运行s 直到再次按下更改按钮,然后它更改为下一个模式。如果需要,您可以添加更多工作模式,也可以修改 GPIO 闪烁的方式。没有 __delay_ms(),这就是延迟 运行 而没有消耗 CPU 的原因。请在实际电路中测试并反馈给我。

/*
 * File:   main.c
 * Author: kozmotronik
 *
 */

#define _XTAL_FREQ 4000000
#include <xc.h>
#include <stdint.h>
#include <stdbool.h>

#pragma config FOSC=INTRCIO,WDTE=OFF,MCLRE=OFF,BOREN=OFF

// Work mode definitions
#define MODE_IDLE   0
#define MODE_OFF    1
#define MODE_ON     2
#define MODE_SLOW   3
#define MODE_FAST   4
#define MODE_CANCEL 5
#define LAST_MODE   MODE_FAST

// Button states
#define BUTTON_IDLE             0
#define BUTTON_PRESS_DETECTED   1
#define BUTTON_DEBOUNCING       2
#define BUTTON_PRESS_CONFIRMED  3

#define SYSTEM_CLOCK_MS 1
#define SYSTEM_CLOCK_uS (SYSTEM_CLOCK_MS * 1000)
#define _XTAL_FREQ_MHZ (_XTAL_FREQ / 1000000) // Oscillator freq in MHz
#define TMR0_RELOAD_VALUE 256 - ( (SYSTEM_CLOCK_uS * _XTAL_FREQ_MHZ) / (8 * 4) ) // Result must be 131
#define MS_TO_TICKS(msTime) (msTime / SYSTEM_CLOCK_MS)

typedef struct{
    unsigned int start;
    unsigned int ticks;
} time_t;

char mode = MODE_IDLE;
char lastMode = MODE_OFF;
char buttonState = BUTTON_IDLE;
char k = 0;
unsigned int systemTick = 0; // Time value count by Timer0


void __interrupt() MyISR(void){
   
    if(INTCONbits.INTF)  //If External Edge INT Interrupt
    {
        INTCONbits.INTF = 0;    // Clear the interrupt
        buttonState = BUTTON_PRESS_DETECTED; // Signal the detected press
    }
    // Check for 1 ms periodic interrupt for system clock
    else if(INTCONbits.T0IF){
        INTCONbits.T0IF = 0; // clear flag
        TMR0 = TMR0_RELOAD_VALUE; // Reload the calculated value for 1 ms
        systemTick++;
    }
}

// Setup Timer0 for 1ms interrupt
void setupTimer0(){
#define PRESCALER_VALUE 2
#define PRESCALER_MASK ~7
    OPTION_REG &= PRESCALER_MASK; // Clear prescaler bits
    OPTION_REG |= PRESCALER_VALUE; // Set prescaler value for 1:8
    OPTION_REGbits.PSA = 0; // Assign prescaler to Tim0
    OPTION_REGbits.T0CS = 0; // Set internal oscillator as clock source
    TMR0 = TMR0_RELOAD_VALUE;
    INTCONbits.T0IF = 0;
    INTCONbits.T0IE = 1; // Enable Timer0 interrupt
}

// Get count atomically
unsigned int getTickCount(){
    unsigned int count;
    di(); // disable interrupts
    count = systemTick;
    ei(); // enable interrupts again
    return count;
}

void performMode(){
    static time_t modeDelay;
    static char slowModeState = 1;
    static char fastModeState = 1;
    
    switch(mode){
        case MODE_OFF:
            // Always must save the current mode before put it into the IDLE
            lastMode = mode; // We have to save the last mode first then put it into the IDLE state
            mode = MODE_IDLE; // The rollover bug caused by here since we haven't save the last mode before put it into the IDLE state
            GP0 = 0; GP5 = 0;
            break;
            
        case MODE_ON:
            GP0 = 1; GP5 = 1;
            break;
            
        case MODE_SLOW:
            if(slowModeState == 1){
                GP0 = 1; GP5 = 1;
                modeDelay.ticks = MS_TO_TICKS(100);
                modeDelay.start = getTickCount();
                slowModeState = 2; // Proceed the next step
            }
            else if(slowModeState == 2){
                if( !((getTickCount() - modeDelay.start) >= modeDelay.ticks) ){
                    // Delay not expired yet
                    return;
                }
                GP0 = ~GP0; GP5 = ~GP5; // Toggle
                // Reload the start time
                modeDelay.start = getTickCount();
            }
            break;
            
        case MODE_FAST:
            if(fastModeState == 1){
                GP0 = 1; GP5 = 1;
                modeDelay.ticks = MS_TO_TICKS(50);
                modeDelay.start = getTickCount();
                fastModeState = 2; // Proceed the next step
            }
            else if(fastModeState == 2){
                if( !((getTickCount() - modeDelay.start) >= modeDelay.ticks) ){
                    // Delay not expired yet
                    return;
                }
                // Delay time expired, proceed toggle
                GP0 = ~GP0; GP5 = ~GP5; // Toggle
                // Reload the start time
                modeDelay.start = getTickCount();
            }
            break;
            
        case MODE_CANCEL:
            // Cancel the current running mode, reset everything
            modeDelay.start = 0;
            modeDelay.ticks = 0;
            slowModeState = 1;
            fastModeState = 1;
            // Also reset the outputs
            GP0 = 0; GP5 = 0;
            break;
            
        default:
            mode = MODE_IDLE;
    }
}

void checkButton(){
#define DEBOUNCE_DELAY_MS 100u // Debounce delay is 100 ms
    static time_t debounceTimer;
    
    switch(buttonState){
        case BUTTON_IDLE:
            break;
            
        case BUTTON_PRESS_DETECTED:
            debounceTimer.ticks = MS_TO_TICKS(DEBOUNCE_DELAY_MS);
            debounceTimer.start = getTickCount();
            buttonState = BUTTON_DEBOUNCING;
            break;
            
        case BUTTON_DEBOUNCING:
            if( !((getTickCount() - debounceTimer.start) >= debounceTimer.ticks) ){
                // Debounce time has not expired yet
                return;
            }
            // Debounce time has expired so check the button last time to confirm if it is still pressed
            if(GPIObits.GP2 != 1){
                // Not stable yet, debounce again
                buttonState = BUTTON_PRESS_DETECTED;
            }
            // Button press is stable, confirm it
            buttonState = BUTTON_PRESS_CONFIRMED;
            break;
            
        case BUTTON_PRESS_CONFIRMED:
            buttonState = BUTTON_IDLE; // Change state so that it can process a new button press
            if(mode != MODE_IDLE && mode != MODE_OFF){
                // Cancel the running mode first
                lastMode = mode; // save the last mode
                mode = MODE_CANCEL; // purge the current one
                performMode();
            }
            mode = lastMode + 1; // Switch to next mode
            if(mode > LAST_MODE){
                // Rewind mode to the beginning which is MODE_OFF
                mode = MODE_OFF;
            }
            break;
            
        default:
            buttonState = BUTTON_IDLE;
    }
}


void main(void) {
    ANSEL = 0;
    CMCON = 0b00000111; //turns comparators off
    TRISIO = 0b00000100;
    GPIO   = 0;
    
    TRISIO2 = 1;                // Make GP2 pin as input
    
    INTCONbits.INTF = 0;
    INTCONbits.INTE = 1;
    INTCONbits.GIE = 1;

    OPTION_REG = 0b01000000; // Rising edge interrupt
    
    setupTimer0();

    // Super loop
    while(1){
        
        // Task 1: Button check
        if(buttonState != BUTTON_IDLE){
            checkButton();
        }
        
        // Task 2: Mode check
        else if(mode != MODE_IDLE){
            performMode();
        }
        
    }
    return;
}