首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

linux定时器消除按键抖动

linux定时器消除按键抖动

概要:上一篇中我们用到了中断来读取按键电平,似乎很成功,然而当我们快速按下松开时,会有数据异常,是什么导致呢?
按键抖动,在我们按下的时候按键可能会发生抖动,如图

在这里插入图片描述
我们的中断是双边沿触发的,假如我们按键发生了抖动,那么就触发了五次中断,这对我们来说实际应用上来看。是非常不稳定的,我们有两种解决方法,一种是硬件消抖,虽然我懂一些硬件,但我不想拿自己的弱处来改进它。那么另一种就是是软件消抖,这一听就在我的水平范畴内,我们可以使用定时器消抖
1.定时器

1.1 定义一个定时器结构体

static struct timer_list buttons_timer;

    1

timer_list结构体如下:

struct timer_list {
    struct list_head entry;
    unsigned long expires; //超时时间,当jiffies大于它的时候会执行一次超时函数

    void (*function)(unsigned long); //定时器超时函数
    unsigned long data;

    struct tvec_t_base_s *base;
#ifdef CONFIG_TIMER_STATS
    void *start_site;
    char start_comm[16];
    int start_pid;
#endif
};



1.2 初始化定时器

init_timer(&buttons_timer);

    1

->1.2.1) 定义超时函数

buttons_timer. function= buttons_timer_ function;

    1

    这个超时函数等一下我们需要编写

1.3 向内核加入一个定时器

add_timer(&buttons_timer);  

    1

到这一步我们定时器的初始化基本完成了,就差一个超时函数没写,写之前我们来看一下按键的中断处理函数:

static irqreturn_t  buttons_irq (int irq, void *dev_id)       //中断服务函数
{
     irq_dev_id =(struct pin_desc *)dev_id;

     mod_timer(&buttons_timer, jiffies+HZ/100);

     return IRQ_RETVAL(IRQ_HANDLED);               
}



    mod_timer(&buttons_timer, jiffies+HZ/100);
    当我们按键按下发生中断的时候,会进入这个中断函数,我们先不着急把电平值传出去,因为可能按键还有抖动。我们在按键中断服务函数里面启动定时器。怎么启用?
    答:使用 mod_timer(&buttons_timer, jiffies+HZ/100); 这个函数会更改我们定时器的超时时间。jiffies是啥?可以理解为电脑启动到目前为止的时间,比如说我开机到现在是10秒,那么这个jiffies就是10秒。HZ是啥?一个HZ就是电脑的一秒。那么这个函数就是把超时时间就是(当前时间 + 10ms)。

      假设我们按下按键,这时进入中断服务函数,把定时器超时时间变成(当前时间(假设是10s) + 10ms),
      过了5ms,然后按键发生抖动了,我们又进入中断处理函数,又把定时器超时时间变成(当前时间(10s+5ms) + 10ms)



实现超时函数

static void buttons_timer_function(unsigned long data)
{
    struct pin_desc * pindesc = irq_pd;
    unsigned int pinval;

    if (!pindesc)
        return;
   
    pinval = s3c2410_gpio_getpin(pindesc->pin);

    if (pinval)
    {
        /* 松开 */
        key_val = 0x80 | pindesc->key_val;
    }
    else
    {
        /* 按下 */
        key_val = pindesc->key_val;
    }

    ev_press = 1;                  /* 表示中断发生了 */
    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
}

    在超时函数里,很简单,把key值传递出来,并唤醒read()。
返回列表