m0_66267825 2023-11-21 10:55 采纳率: 50%
浏览 352
已结题

c51的外部中断的按键,怎么完美的消除抖动

我用的是stc12c5a60s2单片机,
现在我用了两个外部中断口,两个PCA外部中端口,
现在的情况是,四个中断都能触发,但是会存在多次触发的情况,
试过用延时函数,但是会卡在中断函数中
也试过用定时器定时,在触发外部中断时,先关闭外部中断,定时结束后,再从新打开外部中断,但是现象就是:(按键按下时的抖动应该是已经避免了,按键抬起时的抖动好像也能触发外部

下面附上自己写的代码,注释掉的是按键产生的功能,不用考虑

void Timer1Init(void)        //20毫秒@11.0592MHz
{
    TMOD &= 0x0F;        //设置定时器模式
    TMOD |= 0x10;        //设置定时器模式
    TL1 = 0x00;        //设置定时初值
    TH1 = 0xB8;        //设置定时初值
    TF1 = 0;        //清除TF1标志
    ET1 = 1; //使能 T1 中断
    TR1 = 0;        //定时器1关闭计时
}
/****************************************************/
void InterruptTimer1() interrupt 3
    {
        
                TL1 = 0x00;        //设置定时初值
                TH1 = 0xB8;        //设置定时初值
                TF1 = 0;        //清除TF1标志        
                TR1 = 0;        //定时器1关闭计时
                EX0=1;    //开外部中断0
                EX1=1;    //开外部中断1
                CCF0=0;    //开PCA外部中断P1.3
                CCF1=0;    //开PCA外部中断P1.4
                
            }
    }
/****************************************************/
void caidanjiquxiao() interrupt 0            //菜单及取消的按键
    {
        EX0=0;    //关外部中断0
//        switch (xianshijiemianxuanze)                              //用来判断箭头指示在哪一行?
//                    {            
//                        case 1000:
//                        xianshijiemianxuanze=1100;                     //主界面按下第一个键,显示菜单页
//                        shuaxinjiantouweizhi();                             //当进入新界面时,刷新箭头和星号位置
//                        break;
//                        
//                        case 1100:
//                        xianshijiemianxuanze=1000;                     //菜单页按下第一个键,显示主界面
//                        shuaxinjiantouweizhi();                             //当进入新界面时,刷新箭头和星号位置
//                        break;
//                        
//                        case 1110:
//                        xianshijiemianxuanze=1100;                     //标定页按下第一个键,显示菜单页
//                        shuaxinjiantouweizhi();                             //当进入新界面时,刷新箭头和星号位置
//                        break;
//                        
//                        case 1120:
//                        xianshijiemianxuanze=1100;                     //报警页按下第一个键,显示菜单页
//                        shuaxinjiantouweizhi();                             //当进入新界面时,刷新箭头和星号位置
//                        break;
//                        
//                        case 1130:
//                        xianshijiemianxuanze=1100;                     //通讯页按下第一个键,显示菜单页
//                        shuaxinjiantouweizhi();                             //当进入新界面时,刷新箭头和星号位置
//                        break;
//                        
//                        case 1111:
//                        xianshijiemianxuanze=1110;                     //零点页按下第一个键,显示标定页
//                        shuaxinjiantouweizhi();                             //当进入新界面时,刷新箭头和星号位置
//                        break;
//                        
//                        case 1112:
//                        xianshijiemianxuanze=1110;                     //标气页按下第一个键,显示标定页
//                        shuaxinjiantouweizhi();                             //当进入新界面时,刷新箭头和星号位置
//                        break;
//                        
//                        default:
//                            break;
//                    }
        TR1 = 1;    //开消抖计时
    }
/****************************************************/    
void querenanjian() interrupt 2            //确认按键
    {
        EX1=0;    //关外部中断1
//        switch (xianshijiemianxuanze)                              //用来判断箭头指示在哪一行?
//                    {            
//                        case 1000:
//                                                                                                    //主界面按下第四个键,不作反应
//                        break;
//                        
//                        case 1100:
//                        xianshijiemianxuanze=xianshijiemianxuanze+jiantouweizhi*10;    //菜单页按下第四个键,根据箭头位置打开下一页
//                        shuaxinjiantouweizhi();                             //当进入新界面时,刷新箭头和星号位置
//                        break;
//                        
//                        case 1110:
//                        xianshijiemianxuanze=xianshijiemianxuanze+jiantouweizhi*10;    //标定页按下第四个键,根据箭头位置打开下一页
//                        shuaxinjiantouweizhi();                             //当进入新界面时,刷新箭头和星号位置
//                        break;
//                        
//                        case 1120:
//                        xianshijiemianxuanze=1100;                     //报警页按下第四个键,显示菜单页,并且保存报警值
//                        shuaxinjiantouweizhi();                             //当进入新界面时,刷新箭头和星号位置
//                        break;
//                        
//                        case 1130:
//                        xianshijiemianxuanze=1100;                     //通讯页按下第四个键,显示菜单页,并且保存通讯地址和波特率
//                        shuaxinjiantouweizhi();                             //当进入新界面时,刷新箭头和星号位置
//                        ConfigUART(tongxunbotelv);                     //配置波特率为 9600
//                        break;
//                        
//                        case 1111:
//                        xianshijiemianxuanze=1110;                     //零点页按下第四个键,显示标定页,并且根据设置值进行标定折算
//                        shuaxinjiantouweizhi();                             //当进入新界面时,刷新箭头和星号位置
//                        break;
//                        
//                        case 1112:
//                        xianshijiemianxuanze=1110;                     //标气页按下第四个键,显示标定页,并且根据设置值进行标定折算,同时保存标定点
//                        shuaxinjiantouweizhi();                             //当进入新界面时,刷新箭头和星号位置
//                        break;

//                        default:
//                            break;
//                    }
        TR1 = 1;    //开消抖计时            
    }
/****************************************************/    
void zuoyouanjian() interrupt 7            //左右方向按键
    {
        unsigned char anjian=0;
//        if(CCF0==1){anjian=2;}
//        if(CCF1==1){anjian=3;}
//        switch (xianshijiemianxuanze)                              //用来判断箭头指示在哪一行?
//                    {    
//                        case 1000:
//                                                                                                    //主界面按下箭头键,不作反应
//                        break;
//                        
//                        case 1100:                                                                         //菜单页的箭头变化
//                            switch (anjian)                              //用来判断箭头指示在哪一行?
//                                {            
//                                    case 2:
//                                        jiantouweizhi++;
//                                  if(jiantouweizhi>=4){jiantouweizhi=1;}                                                            
//                                    break;
//                                    
//                                    case 3:
//                                        jiantouweizhi--;
//                                  if(jiantouweizhi<=0){jiantouweizhi=3;}    
//                                    break;
//                            
//                                    default:
//                                    break;
//                                }
//                                
//                        case 1110:                                                                         //标定页的箭头和数值变化
//                            switch (anjian)                              //用来判断箭头指示在哪一行?
//                                {            
//                                    case 2:
//                                        jiantouweizhi++;
//                                        if(jiantouweizhi>=3){jiantouweizhi=1;}                                                            
//                                    break;
//                                    
//                                    case 3:
//                                        jiantouweizhi--;
//                                        if(jiantouweizhi<=0){jiantouweizhi=2;}    
//                                    break;
//                            
//                                    default:
//                                    break;
//                                }
//                                
//                        case 1120:                                                                         //报警页的箭头和数值变化
//                            switch (anjian)                              //用来判断箭头指示在哪一行?
//                                {            
//                                    case 2:
//                                        xinghaoweizhi++;
//                                        if(xinghaoweizhi>=9){xinghaoweizhi=1;}                                                    
//                                    break;
//                                    
//                                    case 3:
//                                        switch(xinghaoweizhi)
//                                    {
//                                        case 1:
//                                            dibao=dibao+1000;
//                                        break;
//                                        case 2:
//                                            dibao=dibao+100;
//                                        break;
//                                        case 3:
//                                            dibao=dibao+10;
//                                        break;
//                                        case 4:
//                                            dibao=dibao+1;
//                                        break;
//                                        case 5:
//                                            gaobao=gaobao+1000;
//                                        break;
//                                        case 6:
//                                            gaobao=gaobao+100;
//                                        break;
//                                        case 7:
//                                            gaobao=gaobao+10;
//                                        break;
//                                        case 8:
//                                            gaobao=gaobao+1;
//                                        break;
//                                    }        
//                                    break;
//                            
//                                    default:
//                                    break;
//                                }
//                                
//                        case 1130:                                                                         //通讯页的箭头和数值变化
//                            switch (anjian)                              //用来判断箭头指示在哪一行?
//                                {            
//                                    case 2:
//                                        jiantouweizhi++;
//                                        if(jiantouweizhi>=5){jiantouweizhi=1;}                                                            
//                                    break;
//                                    
//                                    case 3:
//                                        if(jiantouweizhi==2)
//                                        {
//                                            tongxundizhi++;
//                                          if(tongxundizhi>=40)
//                                            {
//                                                    tongxundizhi=0x01;
//                                                }
//                                        }                                    
//                                        if(jiantouweizhi==4)
//                                        {
//                                            botelvxuanze++;
//                                            if(botelvxuanze>=5)
//                                                {
//                                                    botelvxuanze=0;
//                                                }
//                                        }
//                                    tongxunbotelv=botelvshuzu[botelvxuanze];
//                                    break;
//                            
//                                    default:
//                                    break;
//                                }
//                                
//                        case 1111:                                                                         //零点页的箭头和数值变化
//                            break;                                                                             //零点页没有中间按键
//                            
//                        case 1112:                                                                         //标气页的箭头和数值变化
//                            switch (anjian)                              //用来判断箭头指示在哪一行?
//                                {            
//                                    case 2:
//                                        jiantouweizhi++;
//                                        if(jiantouweizhi>=5){jiantouweizhi=1;}                                                            
//                                    break;
//                                    
//                                    case 3:
//                                        switch(jiantouweizhi)
//                                        {
//                                            case 1:
//                                                biaodingdian=biaodingdian+1000;
//                                            break;
//                                            case 2:
//                                                biaodingdian=biaodingdian+100;
//                                            break;
//                                            case 3:
//                                                biaodingdian=biaodingdian+10;
//                                            break;
//                                            case 4:
//                                                biaodingdian=biaodingdian+1;
//                                            break;
//                                        }
//                                    break;
//                            
//                                    default:
//                                    break;
//                                }                                
//                        default:
//                        break;
//                    }
        TR1 = 1;    //开消抖计时        
    }
/****************************************************/    


  • 写回答

20条回答 默认 最新

  • 专家-赤兔[在线] 优质创作者: 编程框架技术领域 2023-11-21 10:56
    关注
    获得3.00元问题酬金

    引自强大而免费的微信小程序:皆我百晓生

    消除按键抖动的方法

    按键抖动是指在按下或释放按键时,由于机械性质造成的按键信号不稳定的情况。为了消除按键抖动,可以采取以下方法:

    1. 硬件滤波:在按键输入引脚上添加一个电容或者一个RC滤波器来稳定信号,并消除抖动。这种方法简单有效,但需要对电路进行修改。

    2. 软件延时:通过在按键触发后加入软件延时,等待抖动结束再进行下一次判断。

    3. 定时器消抖:利用定时器来进行按键消抖,即在按键触发后关闭外部中断,启动定时器,等待一段时间(延时一段时间),定时器中断后再打开外部中断。这样可以在一定程度上消除按键抖动,但需要注意定时器时间需要适当选择。

    4. 状态机消抖:通过状态机的方式来判断按键的状态,只有在按键状态改变一定次数后才判定为有效按键,从而消除抖动。这种方法能够较好地应对按键抖动,但需要编写相应的状态机逻辑。

    建议综合使用软件延时和状态机消抖的方法,如下所示:

    1. 初始化定时器,设置适当的延时时间。

    2. 在外部中断中,触发按键后,关闭外部中断,启动定时器。

    3. 定时器中断响应函数中,关闭定时器,重新打开外部中断。

    4. 通过状态机判断按键状态的变化,只有在状态稳定后才判定为有效按键。

    下面是一种可能的代码实现(仅供参考):

    // 定义全局变量
    volatile unsigned char debounceCount = 0;  // 计数器,用于判断按键状态变化次数
    volatile unsigned char lastButtonState = 1;  // 上一个按键状态,默认为释放状态
    
    // 外部中断0中断服务函数
    void externalInterrupt0() interrupt 0
    {
        // 获取当前按键状态
        unsigned char buttonState = BUTTON_PIN;  // 假设BUTTON_PIN是获取按键状态的函数或宏
        
        // 判断按键状态是否发生变化
        if (buttonState != lastButtonState) {
            debounceCount++;  // 状态变化次数加1
        } else {
            debounceCount = 0;  // 状态未发生变化,计数器重置为0
        }
        
        // 更新上一个按键状态
        lastButtonState = buttonState;
        
        // 关闭外部中断0
        EX0 = 0;
        
        // 启动定时器
        TR1 = 1;
    }
    
    // 定时器1中断服务函数
    void timer1Interrupt() interrupt 3
    {
        // 关闭定时器
        TR1 = 0;
        
        // 重新打开外部中断0
        EX0 = 1;
        
        // 进行按键状态判断
        if (debounceCount >= 3) {
            // 执行相应的按键处理逻辑
            // ...
        }
        
        debounceCount = 0;  // 重置计数器
        lastButtonState = 1;  // 重置上一个按键状态为释放状态
    }
    

    以上代码是使用状态机消抖的方法来处理按键抖动。在外部中断0服务函数中,通过比较当前按键状态和上一个按键状态来判断是否发生按键状态变化,并更新计数器和上一个按键状态。在定时器中断服务函数中,判断计数器的值是否达到阈值,如果达到则执行相应的按键处理逻辑。最后,重置计数器和上一个按键状态为初始值。

    需要注意的是,具体的延时时间和状态变化次数阈值需要根据实际情况进行调整,以满足抖动消除的要求。

    评论 编辑记录

报告相同问题?

问题事件

  • 系统已结题 11月29日
  • 创建了问题 11月21日