You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 22:00:58