• 11.按键驱动之定时器防抖(详解)


    本节目标:

      通过定时器来防止按键抖动,测试程序是使用上节的:阻塞操作的测试程序


    1.如下图所示,在没有定时器防抖情况下,按键没有稳定之前会多次进入中断,使得输出多个相同信息出来

    2.按键波形图,如下所示:

     

    3.如何消去按键抖动

    通过定时器延时10ms,然后每当按键进入中断时就更新定时器延时10ms,若延时10ms到了说明已经过了抖动范围,然后再打印按键电平信息

    4.定时器结构体和函数介绍


    我们先来看看两个全局变量:

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

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


    4.1定时器结构体timer_list

    timer_list常用结构体成员如下所示:

    1)data     //传递到*function超时处理函数的参数,可以通过参数来获取信息
    
    2)expires  //定时器到期的时间,当expires小于等于jiffies时,这个定时器便到期并调用定时器超时处理函数,然后就不会再调用了,
    比如要使用10ms后到期,赋值(jiffies+HZ/100)即可
    3)void (*function)(unsigned long) //定时器超时处理函数。

    4.2 定时器常用函数

    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()里添加

    5.修改驱动程序实现定时器消抖动

    5.1首先定义一个定时器结构体:

    static struct timer_list buttons_timer;   //定义定时器结构体

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

    init_timer(&buttons_timer);     //初始化结构体
    
    /*成员.data未使用
    不需要定时器到期时间,所以成员.expires无需初始化,默认为0,由于小于等于jiffies,会进入一次定时器超时函数*/
    buttons_timer. function= buttons_timer_ function;
    add_timer(&buttons_timer);       //告诉内核,有一个定时器

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

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

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

    5.4定义全局变量*irq_dev_id,然后在中断服务函数中获取dev_id

    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超时 */ mod_timer(&buttons_timer, jiffies+HZ/100); return IRQ_RETVAL(IRQ_HANDLED); }

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

    5.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 "); 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.测试效果

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

     

    7.本节测试程序代码使用的是上一节: 阻塞操作的测试程序

    8.本节驱动程序sixth.c代码:

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <linux/irq.h>  
    #include <asm/irq.h>
    #include <asm/arch/regs-gpio.h>
    #include <asm/hardware.h>
    #include <asm/uaccess.h>
    #include <asm/io.h>                      
    #include <linux/poll.h>                  
    
    static struct timer_list buttons_timer;   //定义定时器结构体
    struct pin_desc *irq_dev_id ;    //定义全局变量获取dev_id

    static struct class *sixthdrv_class; static struct class_device *sixthdrv_class_devs; /*定义互斥锁button_lock,被用来后面的down()和up()使用 */ static DECLARE_MUTEX(button_lock); /* 声明等待队列类型中断 button_wait */ static DECLARE_WAIT_QUEUE_HEAD(button_wait); /* 异步信号结构体变量 */ static struct fasync_struct * button_async; /* * 定义中断事件标志 * 0:进入等待队列 1:退出等待队列 */ static int even_press=0; /* * 定义全局变量key_val,保存key状态 */ static int key_val=0; /* *引脚描述结构体 */ struct pin_desc{ unsigned int pin; unsigned int pin_status; }; /* *key初始状态(没有按下): 0x81,0x82,0x83,0x84 *key状态(按下): 0x01,0x02,0x03,0x04 */ struct pin_desc pins_desc[4]={ {S3C2410_GPF0,0x01 }, {S3C2410_GPF2, 0x02 }, {S3C2410_GPG3, 0x03 }, {S3C2410_GPG11,0x04}, } ; int sixth_drv_class(struct inode *inode, struct file *file) //卸载中断 { free_irq(IRQ_EINT0,&pins_desc[0]); free_irq(IRQ_EINT2,&pins_desc[1]); free_irq(IRQ_EINT11,&pins_desc[2]); free_irq(IRQ_EINT19,&pins_desc[3]); /*释放信号量*/ up(&button_lock); return 0; } /* * 确定是上升沿还是下降沿 */ static irqreturn_t buttons_irq (int irq, void *dev_id) //中断服务函数 { irq_dev_id =(struct pin_desc *)dev_id; //获取引脚描述结构体 /*每产生一次中断,则更新定时器10ms超时 */ mod_timer(&buttons_timer, jiffies+HZ/100); return IRQ_RETVAL(IRQ_HANDLED); } static int sixth_drv_open(struct inode *inode, struct file *file) { if( file->f_flags & O_NONBLOCK ) //非阻塞操作,获取不到则退出 { if(down_trylock(&button_lock) ) return -1; } else //阻塞操作,获取不到则进入休眠 { down(&button_lock); } request_irq(IRQ_EINT0,buttons_irq,IRQT_BOTHEDGE,"S1",&pins_desc[0]); request_irq(IRQ_EINT2, buttons_irq,IRQT_BOTHEDGE, "S2", &pins_desc[1]); request_irq(IRQ_EINT11, buttons_irq,IRQT_BOTHEDGE, "S3", &pins_desc[2]); request_irq(IRQ_EINT19, buttons_irq,IRQT_BOTHEDGE, "S4", &pins_desc[3]); return 0; } static int sixth_drv_read(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { if( file->f_flags & O_NONBLOCK ) //非阻塞操作,获取不到则退出 { if(!even_press ) //没有按键按下 return -1; } /*阻塞操作,则直接进入休眠状态,直到有按键按下为止*/ /*进程 进入等待队列(休眠状态)*/ wait_event_interruptible(button_wait, even_press); /*有按键按下,退出等待队列,上传key_val 给用户层*/ if(copy_to_user(buf,&key_val,sizeof(key_val))) return EFAULT;
       even_press
    =0;
    return 0; } static unsigned sixth_poll(struct file *file, poll_table *wait) {    unsigned int mask = 0; poll_wait(file, &button_wait, wait); // 不会立即休眠 if (even_press) mask |= POLLIN | POLLRDNORM; return mask; } static int sixth_fasync (int fd, struct file *file, int on) { return fasync_helper(fd, file, on, & button_async); //初始化button_async结构体,就能使用kill_fasync()了 } static struct file_operations sixth_drv_fops={ .owner = THIS_MODULE, .open = sixth_drv_open, .read = sixth_drv_read, .release=sixth_drv_class, //里面添加free_irq函数,来释放中断服务函数 .poll = sixth_poll, .fasync= sixth_fasync, //初始化异步信号函数 }; 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 "); 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信号给应用层 } volatile int sixth_major; static int sixth_drv_init(void) { init_timer(&buttons_timer); //初始化定时器 buttons_timer. function= buttons_timer_function; //定时器超时函数 add_timer(&buttons_timer); //添加到内核中 sixth_major=register_chrdev(0,"sixth_drv",&sixth_drv_fops); //创建驱动 sixthdrv_class=class_create(THIS_MODULE,"sixth_dev"); //创建类名 sixthdrv_class_devs=class_device_create(sixthdrv_class, NULL, MKDEV(sixth_major,0), NULL,"buttons"); return 0; } static int sixth_drv_exit(void) { unregister_chrdev(sixth_major,"sixth_drv"); //卸载驱动 class_device_unregister(sixthdrv_class_devs); //卸载类设 class_destroy(sixthdrv_class); //卸载类

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

     return 0;
    }
    
    module_init(sixth_drv_init);
    module_exit(sixth_drv_exit); 
    MODULE_LICENSE("GPL v2");
    }

    下章学习:

    12.Linux之输入子系统分析(详解)

  • 相关阅读:
    Cocostudio学习笔记(2) Button + CheckBox
    Oracle会话及连接数优化
    linux zip压缩和解压的各种操控
    Linux select 机制深入分析
    算法的时间复杂度
    findmaven的英文版本号上线了
    XML高速入门
    spring xml properties split with comma for list
    There is an error in invoking javac. A full JDK (not just JRE) is required
    [Swift]LeetCode134. 加油站 | Gas Station
  • 原文地址:https://www.cnblogs.com/lifexy/p/7522122.html
Copyright © 2020-2023  润新知