如何在MPLAB X IDE中用C实现PIC16F1455按键可靠消抖?
PIC16F1455 按键消抖稳定解决方案
我在MPLAB X IDE中使用C语言为PIC16F1455单片机开发单开关控制的手电筒(按键切换开关状态)时,遇到按键消抖问题。尝试过队列方案,也编写了基于电平变化中断(IOC)的代码,但消抖效果不稳定,仅偶尔生效,希望获得能稳定实现按键消抖的解决方案。
当前代码如下:
#define _XTAL_FREQ 4000000 #include <xc.h> #include "pic16f1455.h" /************************************************************ * Interrups/debouncing *************************************************************/ /** Configuration ********************************************************/ // #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF. // CONFIG1 #pragma config FOSC = INTOSC // Oscillator Selection Bits (INTOSC oscillator: I/O function on CLKIN pin) #pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled) #pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled) #pragma config MCLRE = ON // MCLR Pin Function Select (MCLR/VPP pin function is MCLR) #pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled) #pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled) #pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin) #pragma config IESO = OFF // Internal/External Switchover Mode (Internal/External Switchover Mode is disabled) #pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled) // CONFIG2 #pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off) #pragma config CPUDIV = NOCLKDIV// CPU System Clock Selection Bit (NO CPU system divide) #pragma config USBLSCLK = 48MHz // USB Low SPeed Clock Selection bit (System clock expects 48 MHz, FS/LS USB CLKENs divide-by is set to 8.) #pragma config PLLMULT = 3x // PLL Multipler Selection Bit (3x Output Frequency Selected) #pragma config PLLEN = ENABLED // PLL Enable Bit (3x or 4x PLL Enabled) #pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset) #pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.) #pragma config LPBOR = OFF // Low-Power Brown Out Reset (Low-Power BOR is disabled) #pragma config LVP = OFF // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming) char statusFlag = 0; void main(void) { TRISA = 0b00110000; TRISC = 0b00000000; ANSELA = 0b00000000; ANSELC = 0b00000000; IOCAN = 0b00100000; INTCONbits.GIE = 1; INTCONbits.IOCIE = 1; PORTCbits.RC3 = 0; while(1) { if(statusFlag == 1) { if(PORTCbits.RC3 == 0) { PORTCbits.RC3 = 1; } else { PORTCbits.RC3 = 0; } statusFlag = 0; } } } void __interrupt() ISR(void) { if(INTCONbits.IOCIF == 1) { INTCONbits.IOCIF = 0; if(IOCAF5 == 1) { IOCAF5 = 0; statusFlag = 1; } } }
稳定消抖方案推荐
方案1:定时器+IOC中断组合消抖
该方案结合IOC的低功耗触发特性与定时器的精准延时,覆盖按键抖动周期(通常10-20ms),是稳定性最高的方案之一,适合低功耗、高实时性场景。
修改后的完整代码:
#define _XTAL_FREQ 4000000 #include <xc.h> #include "pic16f1455.h" // CONFIG1 #pragma config FOSC = INTOSC // Oscillator Selection Bits (INTOSC oscillator: I/O function on CLKIN pin) #pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled) #pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled) #pragma config MCLRE = ON // MCLR Pin Function Select (MCLR/VPP pin function is MCLR) #pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled) #pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled) #pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin) #pragma config IESO = OFF // Internal/External Switchover Mode (Internal/External Switchover Mode is disabled) #pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled) // CONFIG2 #pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off) #pragma config CPUDIV = NOCLKDIV// CPU System Clock Selection Bit (NO CPU system divide) #pragma config USBLSCLK = 48MHz // USB Low SPeed Clock Selection bit (System clock expects 48 MHz, FS/LS USB CLKENs divide-by is set to 8.) #pragma config PLLMULT = 3x // PLL Multipler Selection Bit (3x Output Frequency Selected) #pragma config PLLEN = ENABLED // PLL Enable Bit (3x or 4x PLL Enabled) #pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset) #pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.) #pragma config LPBOR = OFF // Low-Power Brown Out Reset (Low-Power BOR is disabled) #pragma config LVP = OFF // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming) char statusFlag = 0; char debounceFlag = 0; // 消抖流程标记 void main(void) { TRISA = 0b00110000; TRISC = 0b00000000; ANSELA = 0b00000000; ANSELC = 0b00000000; // 配置定时器0:16位模式,预分频1:256,溢出约18ms(4MHz时钟) OPTION_REGbits.T0CS = 0; OPTION_REGbits.PSA = 0; OPTION_REGbits.PS = 0b111; TMR0 = 0x00; INTCONbits.T0IE = 1; IOCAN = 0b00100000; // RA5下降沿触发IOC INTCONbits.GIE = 1; INTCONbits.IOCIE = 1; PORTCbits.RC3 = 0; while(1) { if(statusFlag == 1) { PORTCbits.RC3 ^= 1; // 切换LED状态 statusFlag = 0; INTCONbits.IOCIE = 1; // 恢复IOC中断 } } } void __interrupt() ISR(void) { if(INTCONbits.IOCIF == 1) { INTCONbits.IOCIF = 0; if(IOCAF5 == 1) { IOCAF5 = 0; INTCONbits.IOCIE = 0; // 关闭IOC中断,避免抖动重复触发 TMR0 = 0x00; debounceFlag = 1; } } else if(INTCONbits.T0IF == 1) { INTCONbits.T0IF = 0; if(debounceFlag == 1) { if(PORTAbits.RA5 == 0) // 再次检测,确认按键稳定按下 { statusFlag = 1; } debounceFlag = 0; INTCONbits.IOCIE = 1; // 恢复IOC中断 } } }
方案2:软件延时消抖(简化版)
适合简单项目,代码量小,但中断内的延时会短暂阻塞系统,仅推荐用于无其他实时任务的场景。
修改后的中断函数:
void __interrupt() ISR(void) { if(INTCONbits.IOCIF == 1) { INTCONbits.IOCIF = 0; if(IOCAF5 == 1) { IOCAF5 = 0; __delay_ms(20); // 延时覆盖抖动周期 if(PORTAbits.RA5 == 0) // 确认按键稳定 { statusFlag = 1; while(PORTAbits.RA5 == 0); // 等待按键释放,避免长按重复触发 __delay_ms(20); // 释放后二次消抖 } } } }
方案3:主循环状态机消抖
放弃IOC中断,在主循环中周期性扫描按键,通过状态机判断稳定的按下/释放状态,调试方便,适合对实时性要求不高的场景。
完整代码示例:
#define _XTAL_FREQ 4000000 #include <xc.h> #include "pic16f1455.h" // CONFIG1 #pragma config FOSC = INTOSC // Oscillator Selection Bits (INTOSC oscillator: I/O function on CLKIN pin) #pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled) #pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled) #pragma config MCLRE = ON // MCLR Pin Function Select (MCLR/VPP pin function is MCLR) #pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled) #pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled) #pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin) #pragma config IESO = OFF // Internal/External Switchover Mode (Internal/External Switchover Mode is disabled) #pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled) // CONFIG2 #pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off) #pragma config CPUDIV = NOCLKDIV// CPU System Clock Selection Bit (NO CPU system divide) #pragma config USBLSCLK = 48MHz // USB Low SPeed Clock Selection bit (System clock expects 48 MHz, FS/LS USB CLKENs divide-by is set to 8.) #pragma config PLLMULT = 3x // PLL Multipler Selection Bit (3x Output Frequency Selected) #pragma config PLLEN = ENABLED // PLL Enable Bit (3x or 4x PLL Enabled) #pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset) #pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.) #pragma config LPBOR = OFF // Low-Power Brown Out Reset (Low-Power BOR is disabled) #pragma config LVP = OFF // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming) typedef enum { KEY_IDLE, KEY_PRESSED_DEBOUNCE, KEY_RELEASED_DEBOUNCE } KeyState; KeyState keyState = KEY_IDLE; void main(void) { TRISA = 0b00110000; TRISC = 0b00000000; ANSELA = 0b00000000; ANSELC = 0b00000000; PORTCbits.RC3 = 0; while(1) { switch(keyState) { case KEY_IDLE: if(PORTAbits.RA5 == 0) { keyState = KEY_PRESSED_DEBOUNCE; __delay_ms(20); } break; case KEY_PRESSED_DEBOUNCE: if(PORTAbits.RA5 == 0) { PORTCbits.RC3 ^= 1; keyState = KEY_RELEASED_DEBOUNCE; } else { keyState = KEY_IDLE; } break; case KEY_RELEASED_DEBOUNCE: if(PORTAbits.RA5 == 1) { __delay_ms(20); keyState = KEY_IDLE; } break; } } }
内容的提问来源于stack exchange,提问作者Luregn Gianutt
相关产品推荐
相关产品推荐

