×
嵌入式 > 技术百科 > 详情

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

发布时间:2020-10-23 发布时间:
|
按键去抖,一般采用普通延时,如

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 {
 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),如果为否,则说明按键确实按下,否则则为没有按下。有按键按下时,则会执行按键处理函数。
若没有按键按下,则清楚计数器。程序继续执行。
也就是说,不管是否在延时状态,程序,都会向下执行,而不会卡在某一个函数或循环内不动。这样的话,程序就会向下继续执行。
在程序中,若既有按键,又有一些对待实时性较高,但又不乐意放在中断里的程序。可以采用这种方法来实现按键延时,可以相对的提高程序的运行效率。目前这个程序,不支持长按,但可以实现简单的组合按键。 


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
单片机与PC机的通讯介绍02