OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 10882|回复: 20

按键外部中断的时候如何防抖?

[复制链接]

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
发表于 2018-3-1 17:24:42 | 显示全部楼层 |阅读模式
3金钱
现在写一个按一下灯亮,再按一下灯灭的程序。
效果不好,经常出现按一下,按的时候灭,松开时候还是亮的情况,推测是按键抖动的原因
在外部检测按键的时候,可以用延时来消除抖动,
但是在中断的时候,既然能进中断服务函数就证明已经检测到按下了,这时候应该如何消除抖动呢?
而且终端服务函数里一般不要延时,
所以,怎么写,才能让这个程序的效果好一些呢?比较准确的按一下亮,按一下灭。

最佳答案

查看完整内容[请看2#楼]

这个要参考原子哥的按键实验。先在main函数中设置一个全局变量。在中断服务函数所在的文件里声明该全局变量为extern。进入按键中断服务函数后,置该变量为1,再清除中断标志。然后在main函数中判断这个全局变量是否变为1,若是1,延时一小段时间,再判断按键是否还处于按下状态,若还处于按下状态后,就可以写自己想写的指令了。最后,设置这个全局变量为0。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

94

主题

369

帖子

0

精华

高级会员

Rank: 4

积分
865
金钱
865
注册时间
2016-8-25
在线时间
485 小时
发表于 2018-3-1 17:24:43 | 显示全部楼层
本帖最后由 学习stm32f4 于 2018-3-1 17:51 编辑

这个要参考原子哥的按键实验。先在main函数中设置一个全局变量。在中断服务函数所在的文件里声明该全局变量为extern。进入按键中断服务函数后,置该变量为1,再清除中断标志。然后在main函数中判断这个全局变量是否变为1,若是1,延时一小段时间,再判断按键是否还处于按下状态,若还处于按下状态后,就可以写自己想写的指令了。最后,设置这个全局变量为0。
2.jpg
1.jpg
回复

使用道具 举报

15

主题

184

帖子

0

精华

高级会员

Rank: 4

积分
647
金钱
647
注册时间
2014-4-29
在线时间
299 小时
发表于 2018-3-1 18:41:27 | 显示全部楼层
你在定时器中断里面连续2次(或2次以上)检测到按键的线路上出现相同的电平(都是有效电平或者都是无效电平),那就可以确认按键的电平状态了(按下或抬起)。不用中断内延时,也不用主循环延时,完全省下了cpu的时间。
回复

使用道具 举报

51

主题

2166

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
10653
金钱
10653
注册时间
2017-4-14
在线时间
2780 小时
发表于 2018-3-1 18:58:05 | 显示全部楼层
本帖最后由 275891381 于 2018-3-1 19:03 编辑

开启滴答定时器中断,中断内间隔20ms扫描以下按键最简单,不需要延时消抖
回复

使用道具 举报

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
 楼主| 发表于 2018-3-1 20:57:39 | 显示全部楼层
275891381 发表于 2018-3-1 18:58
开启滴答定时器中断,中断内间隔20ms扫描以下按键最简单,不需要延时消抖

滴答定时器我用来写延时函数了啊。我在网上看到类似的程序,有个变量表示重按键,没太看懂
回复

使用道具 举报

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
 楼主| 发表于 2018-3-1 21:05:24 | 显示全部楼层
xianshasaman 发表于 2018-3-1 18:41
你在定时器中断里面连续2次(或2次以上)检测到按键的线路上出现相同的电平(都是有效电平或者都是无效电平 ...

这样就占用一个定时器了啊
回复

使用道具 举报

51

主题

2166

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
10653
金钱
10653
注册时间
2017-4-14
在线时间
2780 小时
发表于 2018-3-1 21:14:19 | 显示全部楼层
本帖最后由 275891381 于 2018-3-1 21:36 编辑
伊森亨特 发表于 2018-3-1 20:57
滴答定时器我用来写延时函数了啊。我在网上看到类似的程序,有个变量表示重按键,没太看懂

volatile u64 xitong_haomiao;           //2^64/1000/3600/24/365=584942417年会复位
static   u8  fac_us=0;                                                             //us延时倍乘数        
//SYSTICK的时钟固定为HCLK时钟的1/8
//中断时间time  =  ( SysTick->LOAD + 1 ) / f                        f = AHB或AHB/8            (9000-1+1)/9M=1ms
void delay_init(void)
{
        SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);        //选择外部时钟  HCLK/8  9M 计数器减1为1/9000000秒
        fac_us=SystemCoreClock/8000000;                                      //9  
        SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;           //开启SYSTICK中断
        SysTick->LOAD=fac_us*1000-1;                                                     //每1/1000s中断一次        
        SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;           //开启SYSTICK   
  while(xitong_haomiao==0);               
}        

//nus:0~2^32=4294967296(最大值即2^32/fac_us@fac_us=1)                                                                             
void delay_us(u32 nus)
{               
        u32 ticks;
        u32 told,tnow,tcnt=0;
        u32 reload=SysTick->LOAD;                                        //LOAD的值                     
        ticks=nus*fac_us;                                                           //需要的节拍数                           
        tcnt=0;
        told=SysTick->VAL;                                                //刚进入时的计数器值
        while(1)
        {
                tnow=SysTick->VAL;        
                if(tnow!=told)
                {            
                        if(tnow<told)tcnt+=told-tnow;                //这里注意一下SYSTICK是一个递减的计数器就可以了.
                        else tcnt+=reload-tnow+told;            
                        told=tnow;
                        if(tcnt>=ticks)break;                                //时间超过/等于要延迟的时间,则退出.
                }  
        }        
}
//nms:0---2^32/1000=4294967.296
void delay_ms(u32 nms)
{                                    
         delay_us((u32)(nms*1000));                                        //普通方式延时  
}

void SysTick_Handler(void)
{        
     xitong_haomiao++;
    if(xitong_haomiao%20==0)//20ms扫描一下按键
         Key=KEY_Scan(0);
}




//注意此函数有响应优先级,WK_UP>KEY1>KEY2>KEY3
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//-1,没有任何按键按下
//0,WK_UP按下 WK_UP
//1,KEY1按下
//2,KEY2按下
//3,KEY3按下
u8 KEY_Scan(u8 mode)
{         
        static u8 flag=0;
        static s8 key_up=1;//按键按松开标志
        if(Key==KEY_NO_PRES)
        {
                  if(mode)key_up=1;  //支持连按
                        if(flag==0)
                        {
                                        if(key_up&&(KEY_UP==0||KEY_DOWN==0))
                                                        flag=1;
                                        else if(KEY_UP==1&&KEY_DOWN==1)
                                                 key_up=1;
                        }
                        else if(flag==1)
                        {
                                key_up=0;
                                flag=0;
                                if     (KEY_UP==0)    return KEY_UP_PRES;
                                else if(KEY_DOWN==0)  return KEY_DOWN_PRES;
                        }
      return KEY_NO_PRES;// 无按键按下                        
  }            
return Key;
}

这样用滴答定时器的好处是main里面可以实现简单调度,也减少delay的使用
if(xitong_haomiao-haomiao_old>=500)//delay_ms(500);
{
             haomiao_old=xitong_haomiao;
             LED13=~LED13;        
}

回复

使用道具 举报

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
 楼主| 发表于 2018-3-1 22:14:44 | 显示全部楼层
275891381 发表于 2018-3-1 21:14
volatile u64 xitong_haomiao;           //2^64/1000/3600/24/365=584942417年会复位
static   u8  fac ...

滴答定时器可以在做延时函数的同时,用来做中断实现按键扫描?
回复

使用道具 举报

51

主题

2166

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
10653
金钱
10653
注册时间
2017-4-14
在线时间
2780 小时
发表于 2018-3-1 22:36:52 | 显示全部楼层
本帖最后由 275891381 于 2018-3-1 22:41 编辑
伊森亨特 发表于 2018-3-1 22:14
滴答定时器可以在做延时函数的同时,用来做中断实现按键扫描?

就是用了滴答定时器的中断做了个毫秒节拍,顺带扫描下按键,没按键的程序,屏蔽扫描就可以了,这样主函数也可以不用延时了,改用简单的调度
回复

使用道具 举报

7

主题

199

帖子

0

精华

高级会员

Rank: 4

积分
711
金钱
711
注册时间
2017-5-20
在线时间
96 小时
发表于 2018-3-2 10:12:40 | 显示全部楼层
其实不应该用外部中断做按钮检测的,而是使用定时器。至于你说浪费定时器,不是这样的,一个定时器可以走很多任务,键盘扫描只是其中一个。
回复

使用道具 举报

56

主题

343

帖子

0

精华

高级会员

Rank: 4

积分
977
金钱
977
注册时间
2016-3-8
在线时间
267 小时
发表于 2018-3-3 17:43:11 | 显示全部楼层
用中断来做按键实在是自找苦吃。。。。
回复

使用道具 举报

9

主题

141

帖子

1

精华

高级会员

Rank: 4

积分
606
金钱
606
注册时间
2017-1-3
在线时间
65 小时
发表于 2018-3-3 20:39:02 | 显示全部楼层
定时器你搞个1ms,或者是1us,,,,然后再count,就可以用在很多任务里面了呀
回复

使用道具 举报

26

主题

261

帖子

1

精华

高级会员

Rank: 4

积分
734
金钱
734
注册时间
2013-11-1
在线时间
28 小时
发表于 2018-3-3 20:53:55 | 显示全部楼层
很简单,就是使用延时防抖,在中断处理函数里面加一个计数
    keyCountTime ++;
   
    if(keyCountTime >= 10)          //keyCountTime 1MS+1  按键消抖10MS
    {
        keyCountTime = 0;
        keyCheck = 1;
    }
这个是机智云的处理方式,可以借鉴,同时推荐看看机智云的代码,编程架构和思想都是不错的。
回复

使用道具 举报

57

主题

316

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1344
金钱
1344
注册时间
2018-1-11
在线时间
156 小时
发表于 2018-3-3 23:32:15 | 显示全部楼层
额,给你个答案吧,u8 Key_Scan(void)
        {
                if(KEY0==0||KEY1==0||KEY2==0)                                                       
                {
                        delay_ms(10);
                        if(KEY0==0){ while (!KEY0); return KEY0_Value;}
                }                       
                        return 0;
        }
你试试这个,肯定没问题
回复

使用道具 举报

57

主题

316

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1344
金钱
1344
注册时间
2018-1-11
在线时间
156 小时
发表于 2018-3-3 23:32:38 | 显示全部楼层
活到老,学到老 发表于 2018-3-3 23:32
额,给你个答案吧,u8 Key_Scan(void)
        {
                if(KEY0==0||KEY1==0||KEY2==0)                                                       

没有必要用什么定时器的
回复

使用道具 举报

57

主题

316

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1344
金钱
1344
注册时间
2018-1-11
在线时间
156 小时
发表于 2018-3-3 23:33:34 | 显示全部楼层
活到老,学到老 发表于 2018-3-3 23:32
额,给你个答案吧,u8 Key_Scan(void)
        {
                if(KEY0==0||KEY1==0||KEY2==0)                                                       

我这是部分程序哈,剩下的你自己加,每一个按键都记得加我加粗的那个语句。
回复

使用道具 举报

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
 楼主| 发表于 2018-3-7 17:17:25 | 显示全部楼层
BJTT 发表于 2018-3-3 20:39
定时器你搞个1ms,或者是1us,,,,然后再count,就可以用在很多任务里面了呀

对定时器的使用很初级,没有操作系统。
回复

使用道具 举报

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
 楼主| 发表于 2018-3-7 17:22:54 | 显示全部楼层
275891381 发表于 2018-3-1 22:36
就是用了滴答定时器的中断做了个毫秒节拍,顺带扫描下按键,没按键的程序,屏蔽扫描就可以了,这样主函数 ...

注意此函数有响应优先级,WK_UP>KEY1>KEY2>KEY3。意思是KEY3检测的优先级比KEY2低?
回复

使用道具 举报

9

主题

141

帖子

1

精华

高级会员

Rank: 4

积分
606
金钱
606
注册时间
2017-1-3
在线时间
65 小时
发表于 2018-3-9 10:13:01 | 显示全部楼层
伊森亨特 发表于 2018-3-7 17:17
对定时器的使用很初级,没有操作系统。

裸奔要搞个多任务处理,要么用systemtick 来count,或者timer来count吧

回复

使用道具 举报

4

主题

76

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
268
金钱
268
注册时间
2018-2-11
在线时间
53 小时
发表于 2018-3-9 10:51:42 | 显示全部楼层
原子哥的实验里有例程!仔细看看
回复

使用道具 举报

3

主题

35

帖子

0

精华

初级会员

Rank: 2

积分
99
金钱
99
注册时间
2017-8-17
在线时间
23 小时
发表于 2018-3-9 13:51:20 | 显示全部楼层
xianshasaman 发表于 2018-3-1 18:41
你在定时器中断里面连续2次(或2次以上)检测到按键的线路上出现相同的电平(都是有效电平或者都是无效电平 ...

这个就得实际操作才能知道到底变量累加到几才是正确。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-9 03:06

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表