中断式按键驱动定时器防抖

为何要防抖:看如下按键波形

由于按键的机械振动,按键一次可能产生多次上升沿或下降沿,触发多次中断,而重复几次进入中断服务程序,上报重复键值,产生抖动。

如何通过软件消去按键抖动

修改中断服务程序,在中断服务程序中加入定时器,利用定时器产生软中断:在定时器timer_function中处理按键中断响 应。原理:抖动不重复进入中断服务处理函数,即不重复进入timer_function。

上升下降沿的 抖动时间间隔在10ms以内,在此时间内产生抖动,重新给定时器赋值,使其不触发timer_function, 这得益于函数int mod_timer(struct timer_list *timer, unsigned long expires),改变定时器的值,重新计时:触发中断后,调用 mod_timer给定时器赋值10ms,如果在第5ms产生抖动,又一次触发中断,进入中断函数,再次调用 mod_timer给定时器重新赋值。则15m后进入timer_function。以此类推。按键中断服务程序处理(timer_function)是后一次抖动进入中断服函数的10ms之后。防止抖动。

代码实现:

先讲解timer_list 的结构体:

struct timer_list {

struct list_head entry,

unsigned long expires,//定时器到期的时间,当expires小于等于jiffies时,这个定时器便到期并调用定时器超时处理函数,然后就不会                                            再调用了,比如要使用10ms后到期,赋值(jiffies+HZ/100)即可

void (*function)(unsigned long ),//定时器超时处理函数。

unsigned long data,//传递到*function超时处理函数的参数,可以通过参数来获取信息

struct timer_base_s *base,

}

function:定义了定时器超时处理函数,从使用的角度来将是最关键的。

 

expires:定义了相对目前的延时时间。当前时间用jiffiers来度量,延时一秒用expires=jiffiers+Hz,延时10秒就是expires=jiffiers+10*Hz,延时10ms就是expires=jiffiers+Hz/100。如果没有对其初始化,或者初始化为0,则激活定时器之后,内核会拿expires和jiffiers比较,当expires<jiffiers是,定时器就已经超时,则超时处理函数不会执行。

data:传递给超时处理函数function的参数。如果函数function不需要传递参数,则data可以不用配置。

另外两个参数不需要我们配置,由内核帮我们完成。

entry:添加到内核链表

base:指定处理器,由init_timer完成。

 

两个全局变量:

jiffies: 是系统时钟,全局变量,默认每隔10ms加1

HZ:是每S的频率,通过系统时钟换算出来,比如每隔10ms加1,那么HZ就等于100**

定时器常用函数

init_timer(struct timer_list*)    //定时器初始化结构体函数,

add_timer(struct timer_list*)     //往系统添加定时器,告诉内核有个定时器结构体

mod_timer(struct timer_list *, unsigned long jiffier_timerout) //修改定时器的超时时间为jiffies_timerout, 

                                                当expires小于等于jiffies时,便调用定时器超时处理函数。

timer_pending(struct timer_list *)    //定时器状态查询,如果在系统的定时器列表中则返回1,否则返回0;

del_timer(struct timer_list*)         //删除定时器,在本驱动程序出口函数sixth_drv_exit()里添加

按步写代码:

1首先声明一个定时器结构体:

static struct timer_list buttons_timer;   //声明一个定时器结构体

2在init入口函数中初始化定时器结构体:

init_timer(&buttons_timer);     //初始化:用init_timer对上一步声明的结构体初始化。主要初始化entry和base这两个参数,由内核完成,细节暂且不管。

/*成员.data未使用

不需要定时器到期时间,所以成员.expires无需初始化,默认为0,由于小于等于jiffies,会进入一次定时器超时函数*/

buttons_timer. function= buttons_timer_ function;

add_timer(&buttons_timer);       //激活定时器,从当前时刻开始计时。

注:以上3步可以用函数 setup_timer(time,func,data)代替,该函数实现赋值并初始化定时器,比手动设置更方便

3 在exit出口函数中删除定时器:


del_timer(&buttons_timer);        //删除定时器

不是必须的:

buttons_timer.data= n                                 //传递给buttons_timer_function的参数,可以不配置


buttons_timer.expires = jiffiers +10*Hz;              //延时10s,可以先不配置,通过mod_timer修改这个参数

4在中断函数中重置定时器的值

struct pin_desc *irq_dev_id ;    //定义全局变量获取dev_id

并修改中断服务函数:

static irqreturn_t  buttons_irq (int irq, void *dev_id)       //中断服务函数

{

     irq_dev_id =(struct pin_desc *)dev_id;     //获取引脚描述结构体

   

       /*每产生一次中断,则更新定时器10ms超时,改变定时器的值,重新计时:如果在第5ms产生抖动,又一次触发中断,进入中断函数,再次调用 mod_timer给定时器重新赋值。则15m后进入timer_function。以此类推。按键中断服务程序处理(timer_function)是后一次抖动进入中断服函数的10ms之后。防止抖动。 */   

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


     return IRQ_RETVAL(IRQ_HANDLED);                

}

注意: jiffies+HZ/100 也可以直接换成 jiffies + msecs_to_jiffies(10),更加方便

5当10ms超时到了,进入定时器超时函数,处理*irq_dev_id来判断是哪个按键按下的

static  void buttons_timer_function(unsigned long data)   //定时器超时处理函数:处理中断服务

{      

       unsigned int  pin_val=0;   


       if(!irq_dev_id)   //初始化时,由于定时器.expires成员=0,会进入一次,若irq_dev_id为0则退出 

       {printk("expires: timer out\n");

         return ; }                                              


       pin_val=s3c2410_gpio_getpin(irq_dev_id->pin);   //获取按键值


        if(pin_val)

        {

                  /*按下 (下降沿),清除0x80*/      

                   key_val=irq_dev_id->pin_status&0xef;

         }

         else

         {

                   /*没有按下(上升沿),加上0x80*/

                   key_val=irq_dev_id->pin_status|0x80;

         }

       

            even_press=1;                                        //退出等待队列

            wake_up_interruptible(&button_wait);                //唤醒 中断

            kill_fasync(&button_async, SIGIO, POLL_IN);        //发送SIGIO信号给应用层

}

6.测试效果

如下图所示,我们运行测试程序,来快速按下按键试试:

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值