专注电子技术学习与研究
当前位置:单片机教程网 >> MCU设计实例 >> 浏览文章

一个简单的按键去抖延时程序

作者:huqin   来源:本站原创   点击数:  更新时间:2013年11月21日   【字体:

按键去抖,一般采用普通延时,如

if((GPIOC->IDR & 0x01)== 0)
{
    delay_ms(20);
    if(GPIOC->IDR & 0x01)== 0
    {
            //进行按键处理函数
    }
}

这个程序,需要有一个普通的延时程序,来检测去抖动,这个延时一般采用for循环和while循环。这样的话,就有一个问题,在延时的这20ms中,cpu一直在判断时间有没有到。如果不是中断,是不会打断cpu的程序的。这样的话,去抖延时,就会浪费cpu的效率。
假如,按键扫描的后面跟一个协议处理的函数。
即:

while(1)

        scan_key();    //按键扫描
        exe();              //协议解析

这个时候,若接收中断,在按键扫描时已经处理完成,正好按下按键,这个时候就必须要有20ms的间隔,在判断完按键后,才可以进入协议解析函数。也就是说,如果没有扫描函数,协议会立即执行解析并返回响应数据。而添加按键扫描后,协议有可能会在20ms后,进行解析并返回数据,这样的话,就会使产品的实时性无法保证。

所以我想了另一个方法,采用标致位,来实现延时,当然这个方法,肯定不是我第一个想出来的。如有雷同,可采用翻钢镚方法进行选择。
就是采用if语句来实现延时,只不过写程序时比较麻烦,但稳定性在stm8上测试了一下,感觉还可以。

代码如下
首先申请几个全局变量
unsigned int time_ms,time_us,time_ns,time_flag;
//以上这几个是定时标志和定时计数变量
unsigned key_old, key_new;
//这两个是按键键值

/*******************************************************************************
 函数名:delay_ms()
 函数功能:延时
 参数:ms 毫秒
 返回:无
 备注:此延时函数采用if实现,使用时,必须先申请flag变量然后调用延时函数,最后在
  执行中加入flag判断
例:u16 time_flag,time_ms,time_us,time_ns;
delay_ms(u16 ms);
if(time_flag>0){time_flag=0;......内容}
*******************************************************************************/
void key_delay(unsigned int ms)
{
if(time_ms<ms)
{
 if(time_us<10)                  //在应用时,不同的单片机,不同的频率,需要进行调整
{
if(time_ns<8)            //在应用时不同的单片机,不同的频率,需要进行调整
{
time_ns++;
}
else
{
 time_us++;
 time_ns=0;
}
}
else
{
 time_ms++;
 time_us=0;
}
}
else
{
 time_flag=1;
 time_ms=0;
}
}


以上代码,有一个time_flag,变量,这个变量就是定时标致变量。一旦这个标致置一,则说明定时器到时间
使用时可以

///////////////////////////////////////////////////////////
//函数名:scan()   
//功能:按键扫描    
//参数:无     
//返回值:无        
//备注:        
///////////////////////////////////////////////////////////
void scan()
{
    u8 key_new;
    key_new = GPIOC->IDR;
    if(key_old != key_new)
    {
        key_delay(150);
        if(time_flag == 1)
        {
            time_flag = 0;
            if(key_old != key_new)
            {
               switch()
               {
                   case 1:  k1_exe();  break;
                   case 2:  k2_exe();  break;
                   case 3:  k3_exe();  break;
                   case 4:  k4_exe();  break;
                   default:        break;
                }
                key_old = key_new;
            }
            else
            }
                time_ms=0;   
            }
        }
    }
}

以上就是代码
在大循环中,直接调用即可,和普通的按键函数一样,只不过,这个的实时性,应该相对较高一些。
while(1)

         scan_key();    //按键扫描
         exe();              //协议解析 

让我们来分析一下,为啥这个函数相对较好一些。
首先,我们来看
scan_key(); 
首先,扫描IO端口,存放如新按键变量
key_new = GPIOC->IDR;
然后将新按键与老按键号进行对比,如果新的按键号与老按键号不同,说明有按钮按下。
if(key_old != key_new)
当有按钮按下的时候进入,延时函数,
key_delay(150);
这时,进入多个if判断,进行time_ns++;这个函数,最主要的功能就是判断,当前的时间time_ms,与参数时间,是否一致,若不一致,则退出函数。这时time_flag不为1,当前的时间time_ms,与参数时间一致 ,这时time_flag为1。
if(time_flag == 1)
这个判断就是判断到时标致,如果到时,则说明去抖时间完成,则在判断一次if(key_old != key_new),如果为否,则说明按键确实按下,否则则为没有按下。有按键按下时,则会执行按键处理函数。
若没有按键按下,则清楚计数器。程序继续执行。
也就是说,不管是否在延时状态,程序,都会向下执行,而不会卡在某一个函数或循环内不动。这样的话,程序就会向下继续执行。
在程序中,若既有按键,又有一些对待实时性较高,但又不乐意放在中断里的程序。可以采用这种方法来实现按键延时,可以相对的提高程序的运行效率。目前这个程序,不支持长按,但可以实现简单的组合按键。
欢迎一切拍砖,转载以及抄袭。若感觉此文章对您有所帮助,请支持一下51hei单片机论坛
您的支持,是我原创的动力,谢谢。

关闭窗口

相关文章