Linux驱动之定时器在按键去抖中的应用

发布者:心灵舞动最新更新时间:2024-08-20 来源: elecfans关键字:Linux驱动  定时器  按键去抖 手机看文章 扫描二维码
随时随地手机看文章

机械按键在按下的过程中会出现抖动的情况,如下图,这样就会导致本来按下一次按键的过程会出现多次中断,导致判断出错。在按键驱动程序中我们可以这么做:

在按键驱动程序中我们可以这么做来取消按键抖动的影响:当出现一个按键中断后不会马上去处理它,而是延时一个抖动时间(一般10ms),如果在这个时间内再次出现中断那么再次延时10ms。这样循环,一直到在这个10ms内只有一个按键中断,那么就认为这次是真的按键值,然后在定时器处理函数里处理它。上述过程可以利用内核的定时器来实现。

定时器二要素:定时时间、定时时间到后做什么事情。根据这两个要素来编写程序,直接在sixth_drv.c的驱动程序上更改直接看到代码:

1、定时器的创建,先建立一个定时器结构

static struct timer_list buttons_timer;//定义一个定时器

2、在模块装载时初始化定时器


static int sixth_drv_init(void)

{

    /*增加一个定时器用于处理按键抖动*/

    init_timer(&buttons_timer);

    buttons_timer.expires = 0;//定时器的定时时间

//    buttons_timer->data = (unsigned long) cs;

    buttons_timer.function = buttons_timeout;//定时时间到后的处理函数

    add_timer(&buttons_timer);//将定义的定时器放入定时器链表

    

    sixthmajor = register_chrdev(0, 'buttons', &sixth_drv_ops);//注册驱动程序


    if(sixthmajor < 0)

        printk('failes 1 buttons_drv registern');

    

    sixth_drv_class = class_create(THIS_MODULE, 'buttons');//创建类

    if(sixth_drv_class < 0)

        printk('failes 2 buttons_drv registern');

    sixth_drv_class_dev = class_device_create(sixth_drv_class, NULL, MKDEV(sixthmajor,0), NULL,'buttons');//创建设备节点

    if(sixth_drv_class_dev < 0)

        printk('failes 3 buttons_drv registern');


    

    gpfcon = ioremap(0x56000050, 16);//重映射

    gpfdat = gpfcon + 1;

    gpgcon = ioremap(0x56000060, 16);//重映射

    gpgdat = gpgcon + 1;


    printk('register buttons_drvn');

    return 0;

}


3、编写定时器处理函数


static void buttons_timeout(unsigned long data)

{

    unsigned int pin_val;

    static long cnt=0;

    

    //printk('timeout cnt : %dn',++cnt);

    if(pin_des==NULL)

        return;

    else

    {

    //    printk('pin_des != NULLn');

        

        pin_val = s3c2410_gpio_getpin(pin_des->pin);

        

        if(pin_val) //按键松开

            key_val = 0x80 | pin_des->key_val;

        else

            key_val = pin_des->key_val;



        wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */

        ev_press = 1;    

        

        kill_fasync(&sixth_fasync, SIGIO, POLL_IN);//发生信号给进程

    }

}


4、当在卸载驱动时将定时器删除;在中断处理程序中直接改变定时器的超时时间,并记录下是哪个按键按下的即可,其他处理都在定时器超时函数中。直接看到完整代码:


#include

#include

#include

#include

#include         //含有iomap函数iounmap函数

#include //含有copy_from_user函数

#include //含有类相关的处理函数

#include //含有S3C2410_GPF0等相关的

#include     //含有IRQ_HANDLEDIRQ_TYPE_EDGE_RISING

#include    //含有IRQT_BOTHEDGE触发类型

#include //含有request_irq、free_irq函数

#include

#include   //含有各种错误返回值

//#include




static struct class *sixth_drv_class;//类

static struct class_device *sixth_drv_class_dev;//类下面的设备

static int sixthmajor;


static unsigned long *gpfcon = NULL;

static unsigned long *gpfdat = NULL;

static unsigned long *gpgcon = NULL;

static unsigned long *gpgdat = NULL;


struct fasync_struct *sixth_fasync;

    

static unsigned int key_val;


struct pin_desc 

{

    unsigned int pin;

    unsigned int key_val;

};


static struct pin_desc  pins_desc[4] = 

{

    {S3C2410_GPF0,0x01},

    {S3C2410_GPF2,0x02},

    {S3C2410_GPG3,0x03},

    {S3C2410_GPG11,0x04}

};


static struct pin_desc *pin_des=NULL;


static unsigned int ev_press;

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);//注册一个等待队列button_waitq


 static atomic_t open_flag = ATOMIC_INIT(1);     //定义原子变量open_flag 并初始化为1


static DECLARE_MUTEX(button_lock);     //定义互斥锁


static struct timer_list buttons_timer;//定义一个定时器

/*

  *0x01、0x02、0x03、0x04表示按键被按下

  */

  

/*

  *0x81、0x82、0x83、0x84表示按键被松开

  */


/*

  *利用dev_id的值为pins_desc来判断是哪一个按键被按下或松开

  */

static irqreturn_t buttons_irq(int irq, void *dev_id)

{

    pin_des = (struct pin_desc *)dev_id;//取得哪个按键被按下的状态

    mod_timer(&buttons_timer, jiffies+HZ/100);//10ms之后调用定时器处理函数

    

    return IRQ_HANDLED;

}




static int sixth_drv_open (struct inode * inode, struct file * file)

{

    int ret;



//    if(atomic_dec_and_test(&open_flag)==0)//自检后是否为0,不为0说明已经被人调用

//    {

//        atomic_inc(&open_flag);//原子变量+1

//        return -EBUSY;

//    }

    if(file->f_flags & O_NONBLOCK)//非阻塞方式

    {

        if(down_trylock(&button_lock))//获取信号量失败则返回

            return -EBUSY;

    }

    else    

        down(&button_lock);//获得信号量

    

    ret = request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, 's1', (void * )&pins_desc[0]);

    if(ret)

    {

        printk('open failed 1n');

        return -1;

    }

    ret = request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, 's2', (void * )& pins_desc[1]);

    if(ret)

    {

        printk('open failed 2n');

        return -1;

    }

    ret = request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, 's3', (void * )&pins_desc[2]);

    if(ret)

    {

        printk('open failed 3n');

        return -1;

    }

    ret = request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, 's4', (void * )&pins_desc[3]);

    if(ret)

    {

        printk('open failed 4n');

        return -1;

    }

    

    return 0;

}



static int sixth_drv_close(struct inode * inode, struct file * file)

{

//    atomic_inc(&open_flag);//原子变量+1

    up(&button_lock);//释放信号量

    

    free_irq(IRQ_EINT0 ,(void * )&pins_desc[0]);


     free_irq(IRQ_EINT2 ,(void * )& pins_desc[1]);


    free_irq(IRQ_EINT11 ,(void * )&pins_desc[2]);


    free_irq(IRQ_EINT19 ,(void * )&pins_desc[3]);


    return 0;

}


static ssize_t sixth_drv_read(struct file * file, char __user * userbuf, size_t count, loff_t * off)

{

    int ret;


    if(count != 1)

    {

        printk('read errorn');

        return -1;

    }


    if(file->f_flags & O_NONBLOCK)//非阻塞方式

    {

        if(!ev_press)//判断是否有按键按下,如果没有直接返回

        {

                key_val = 0;

                ret = copy_to_user(userbuf, &key_val, 1);

                return -EBUSY;

        }

    }

    else//如果没有按键动作,直接进入休眠

        wait_event_interruptible(button_waitq, ev_press);//将当前进程放入等待队列button_waitq中

    

    ret = copy_to_user(userbuf, &key_val, 1);

    ev_press = 0;//按键已经处理可以继续睡眠

    

    if(ret)

    {

        printk('copy errorn');

        return -1;

    }

    

    return 1;

}


static unsigned int sixth_drv_poll(struct file *file, poll_table *wait)

{

    unsigned int ret = 0;

    poll_wait(file, &button_waitq, wait);//将当前进程放到button_waitq列表


    if(ev_press)

        ret |=POLLIN;//说明有数据被取到了


    return ret;

}




static int sixth_drv_fasync(int fd, struct file * file, int on)

{

    int err;

    printk('fansync_helpern');

    err = fasync_helper(fd, file, on, &sixth_fasync);//初始化sixth_fasync

    if (err < 0)

        return err;

    return 0;

}



static struct file_operations sixth_drv_ops = 

{

    .owner   = THIS_MODULE,

    .open    =  sixth_drv_open,

    .read     = sixth_drv_read,

    .release = sixth_drv_close,

    .poll      =  sixth_drv_poll,

    .fasync   = sixth_drv_fasync,

    

};


static void buttons_timeout(unsigned long data)

{

    unsigned int pin_val;

    static long cnt=0;

    

    //printk('timeout cnt : %dn',++cnt);

    if(pin_des==NULL)

        return;

    else

    {

    //    printk('pin_des != NULLn');

        

        pin_val = s3c2410_gpio_getpin(pin_des->pin);

        

        if(pin_val) //按键松开

            key_val = 0x80 | pin_des->key_val;

        else

            key_val = pin_des->key_val;



        wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */

        ev_press = 1;    

        

        kill_fasync(&sixth_fasync, SIGIO, POLL_IN);//发生信号给进程

[1] [2]
关键字:Linux驱动  定时器  按键去抖 引用地址:Linux驱动之定时器在按键去抖中的应用

上一篇:Linux驱动之输入子系统简析
下一篇:Linux驱动之同步、互斥、阻塞的应用

推荐阅读最新更新时间:2024-11-07 12:09

msp432快速入门第十节之timer32定时器
(一)定时器介绍 指出定时器可以组合使用也可以单独使用,分频是1/16/256,可以产生中断,三种计数模式,时钟源是MCLK,也就是CPU时钟。 (二)定时器编程 (1)查看示例 这部分指明了现象; 然后纵观整个程序,主要是通过中断触发定时器来使LED亮1s (2)配置自己的函数 第一步 配置定时器 配置定时器Timer32: //配置timer32 Timer32_initModule(TIMER32_BASE, TIMER32_PRESCALER_1, TIMER32_32BIT, TIMER32_PERIODIC_MODE); //开启中断 Interrupt_enableI
[单片机]
msp432快速入门第十节之timer32<font color='red'>定时器</font>
单片机定时器基础知识
  时钟周期:时钟周期T是时序中最小的时间单位具体计算的方法就是1/时钟源,我们KST-51单片机开发板上用的晶振是11.0592M,那么对于我们这个单片机系统来说,时钟周期=1/11059200秒。   机器周期:我们的单片机完成一个操作的最短时间。机器周期主要针对汇编语言而言,在汇编语言下程序的每一条语句执行所使用的时间都是机器周期的整数倍,而且语句占用的时间是可以计算出来的,而C语言一条语句的时间是不可计算的。51单片机系列,在其标准架构下一个机器周期是12个时钟周期,也就是12/11059200秒。现在有不少增强型的51单片机,其速度都比较块,有的1个机器周期等于4个时钟周期,有的1个机器周期就等于1个时钟周期,也就是说大体
[单片机]
VxWorks共享看门狗定时器的设计与实现
VxWorks是目前应用最多的嵌入式实时操作系统之一,广泛应用于工业控制、医疗器械、通信、航空航天以及武器装备等领域。VxWorks是32位实时嵌入式操作系统,自20世纪80年代由风河公司推出以来,其良好的实时性、对多任务的支持、体积精简、可剪裁等优点得到众多公司、开发者及用户的喜爱。 在实时性要求高的应用系统中,定时器是经常被用到的重要器件。而对于VxWorks操作系统本身来说,并未提供一个通用、高效的定时器组件。文章所提出的共享看门狗定时机制就是针对这种情况实现的一种通用型定时器组件。 1 VxWorks定时的方法 1.1 使用taskDely函数 函数原型为:STATUS taskDelay(int
[单片机]
VxWorks共享看门狗<font color='red'>定时器</font>的设计与实现
C51-定时器/计数器
关于定时器和计数器本身用的是相同的物理器件,因其工作模式的不同而进行转化。
[单片机]
C51-<font color='red'>定时器</font>/计数器
MEGA16单片机定时器(16位)源代码
/* 程序名: mega16a 芯片16位定时计数器; 概括: 学会了控制8位定时器,16位定时器和它如出一辙,It's a piece of cake! 在芯片开发板上实现精确一秒闪烁... 心得 : 电子技术和音乐一样 ,都是一门艺术,令人回味... finish time: 2014年3月8日19:33:10; 作者 : 肖邦; */ #include avr/io.h //头文件; typedef unsigned char uint8 ; //在程序中用 uint8 相当于写 unsigned cha
[单片机]
STM32f103 定时器之编码器接口模式
背景 买了个Arduino的旋转编码器模块,配合STM32定时器的编码器模式实现了旋转角度以及圈数的计数。这种旋转编码器我能想到的实际应用场景暂时只有实体音量旋钮,鼠标的滚轮等,所以只实现了计数。阅读Arduino关于该编码器的介绍,该编码器还可以实现旋转的速度、加速度的计算。应该算是算法层级的吧,还没做到实际应用,暂时不深究,本篇仅仅对旋转编码器的原理以及STM32编码器接口模式的配置使用方法做个简介。 正文 编码器分类: 按工作原理:光电式、磁电式和触点电刷式; 按码盘的刻孔方式:增量式和绝对式两类; 这是从网上看到一个简介,只接触过Arduino的编码器,其他暂未使用过。 Arduino的编码器属于增量式。它一共有5根线。分
[单片机]
STM32f103 <font color='red'>定时器</font>之编码器接口模式
STM32F10x 学习笔记之SysTick 定时器
SysTick 定时器被集成在NVIC中。因此,只要是Cortex-M3 内核的单片机,就都有它。这个学习笔记就用SysTick 定时器来实现走马灯的功能。 SysTick 定时器非常简答,只有四个寄存器。这四个寄存器的含义在《Cortex-M3权威指南》那本书中讲的非常的清楚,这里不复述了,下面只讲讲在STM32上SysTick有什么特殊之处。按照CMSIS 标准,用C语言访问这四个寄存器时使用的寄存器名称分别如下: SysTick- CTRL SysTick- LOAD SysTick- VAL SysTick- CALIB SysTick- CALIB 的值固定为9000,因此,只有当系统嘀嗒时钟设定为9MHz(HC
[单片机]
SysTick定时器介绍,SysTick定时器寄存器
SysTick定时器介绍 SysTick定时器也叫SysTick滴答定时器, 它是Cortex-M3内核的一个外设,被嵌入在 NVIC 中。它是一个 24 位向下递减的定时器,每计数一次所需时间为1/SYSTICK,SYSTICK 是系统定时器时钟,它可以直接取自系统时钟,还可以通过系统时钟 8 分频后获取,本套程序中我们采用后者,即每计数一次所需时间为1/(72/8)us,换句话说在 1us 的时间内会计数 9 次。当定时器计数到 0 时,将从LOAD 寄存器中自动重装定时器初值,重新向下递减计数,如此循环往复。如果开启 SysTick 中断的话,当定时器计数到 0,将产生一个中断信号。因此只要知道计数的次数就可以准确得到它的延
[单片机]
SysTick<font color='red'>定时器</font>介绍,SysTick<font color='red'>定时器</font>寄存器
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved