Linux嵌入式驱动学习之路(十五)按键驱动-定时器防抖

  在之前的定时器驱动程序中,我们发现在连续按下按键的时候,正常情况下应该是一次按下对应一次松开。而程序有时候会显示是两次按下,一次松开。这个问题是因为在按下的时候,因为是机械按键,所以电压信号会产生一定的波动,会让程序进行两次中断,如何解决这个问题呢?

  我们可以在发生一次中断之后等待一段时间再去判断按键是否已经被按下,如果是已经被按下了 则本次有效,否则无效。这里用到了定时器。

定时器常用的操作函数有:

  init_timer(&timer);           //定时器初始化
  timer.data=10;                //设置超时处理函数参数
  timer.expires=jiffies+(10*HZ);     //设置超时jiffies值为10s  
  timer.function=timer_func;      //设置超时处理函数
  add_timer(&timer);             //添加定时器到内核
  del_timer(&timer);            //删除定时器  在每次调用之前应该把上次调用的删除

驱动代码:

#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/random.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/wait.h>
#include <linux/mutex.h>
#include <linux/io.h>
#include <asm/irq.h>
#include <linux/irq.h>
#include <linux/fs.h>
#include <asm/arch/regs-gpio.h>
#include <linux/interrupt.h>
#include <linux/poll.h>

static struct class *key_class;     //创建类
static struct class_device *key_class_devs;   //创建类对应的设备
static struct timer_list keys_timer;

struct pin_desc{
    unsigned int pin;
    unsigned int key_val;
};
struct pin_desc pins_desc[4] = {
    {S3C2410_GPF0,0X01},
    {S3C2410_GPF2,0X02},
    {S3C2410_GPG3,0X03},
    {S3C2410_GPG11,0X04},
};

struct pin_desc *pin_timer;    //在按键中断里保存按下按键的信息
unsigned char keyvals=0;

static volatile int ev_press = 0;
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

static struct fasync_struct *key_async_queue;

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

static irqreturn_t keys_irq(int irq, void *dev_id)
{
    pin_timer = (struct pin_desc *)dev_id;
    mod_timer(&keys_timer, jiffies+HZ/100 );  //jiffies是个全局变量,每10ms累加一次,这里是10ms产生一次中断
    return IRQ_HANDLED;
}

int key_open(struct inode *inode, struct file *fp)
{
    /*获取信号量*/
    if( fp->f_flags & O_NONBLOCK )
    {
        if(down_trylock(&canopen))
            return -EBUSY;
    }
    else
    {
        down(&canopen);
    }
    request_irq( IRQ_EINT0, keys_irq, IRQT_BOTHEDGE, "key2", &pins_desc[0]);
    request_irq( IRQ_EINT2, keys_irq, IRQT_BOTHEDGE, "key3", &pins_desc[1]);
    request_irq( IRQ_EINT11, keys_irq, IRQT_BOTHEDGE, "key4", &pins_desc[2]);
    request_irq( IRQ_EINT19, keys_irq, IRQT_BOTHEDGE, "key5", &pins_desc[3]);
    return 0;
}

ssize_t key_read(struct file *fp, char __user *buff, size_t count, loff_t *offp){

    if(fp->f_flags & O_NONBLOCK )
    {
        if(!ev_press)
            return -EAGAIN;
    }
    else
    {
        wait_event_interruptible(button_waitq,ev_press);
    }
    if(count != 1)
    {
        return -EINVAL;
    }

    copy_to_user(buff,&keyvals,1);
    ev_press = 0;
    return 0;
}

ssize_t key_write(struct file *fp, const char __user *buf, size_t count, loff_t *ppos){
}

int key_close(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(&canopen);
}

static unsigned int key_poll(struct file *file, struct poll_table_struct *wait)
{
    unsigned int mask = 0;
    poll_wait(file, &button_waitq,wait);
    if(ev_press)
        mask |= POLLIN|POLLRDNORM;
    return mask;
}

static int key_fsync (int fd, struct file *filp, int on)
{
    printk("ok\n");
    return fasync_helper (fd, filp, on, &key_async_queue);
}

struct file_operations led_fops={
    .owner = THIS_MODULE,
    .open = key_open,
    .write  = key_write,
    .read   = key_read,
    .release = key_close,
    .poll = key_poll,
    .fasync = key_fsync,
};

static void keys_timer_fun(unsigned long t)
{
    struct pin_desc *pindesc = (struct pin_desc *)pin_timer;
    unsigned int pinval;
    if( !pindesc )
        return ;
    pinval = s3c2410_gpio_getpin(pindesc->pin);
    if(pinval)
    {
        keyvals = pindesc->key_val|0x80;

    }
    else
    {
        keyvals = pindesc->key_val;
    }
    ev_press = 1;
    wake_up_interruptible(&button_waitq);

    kill_fasync (&key_async_queue, SIGIO, POLL_IN);
    return ;
}

int major;
static int key_init(void)
{
    major = register_chrdev( 0,"key_drv", &led_fops );
    key_class = class_create(THIS_MODULE,"key_class");
    key_class_devs = class_device_create(key_class,NULL,MKDEV(major,0),NULL,"my_keys");

    init_timer(&keys_timer);
    keys_timer.function = keys_timer_fun;    /*定时器中断函数*/
    keys_timer.expires = 0;
    add_timer(&keys_timer);
    printk("key install Module\n");
    return 0;
}

static void key_exit(void)
{
    unregister_chrdev( major, "key_drv" );
    class_device_unregister(key_class_devs);
    class_destroy(key_class);
   del_timer(&keys_timer);    //删除定时器    printk("key Module exit\n");
}

module_init(key_init);
module_exit(key_exit);
MODULE_LICENSE("GPL");

测试应用程序:

#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
int fd;
static char key_val;
int main( int argc, char **argv )
{
    int oflags;
    fd = open("/dev/my_keys",O_RDWR);/* O_NONBLOCK为非阻塞*/
    if(fd<0)
    {
        printf("open failed\n");
        return 0;
    }
    while(1)
    {
        read(fd,&key_val,1);
        printf("key_val:%d\n",key_val);
    }
    return 0;
}

sd

时间: 2024-12-30 00:14:19

Linux嵌入式驱动学习之路(十五)按键驱动-定时器防抖的相关文章

Linux嵌入式驱动学习之路(十二)按键驱动-poll机制

实现的功能是在读取按键信息的时候,如果没有产生按键,则程序休眠在read函数中,利用poll机制,可以在没有退出的情况下让程序自动退出. 下面的程序就是在读取按键信息的时候,如果5000ms内没有按键信息,则自己退出. 首先应用程序执行poll函数 kernel中的sys_poll do_sys_poll init_poll_funcptr-->do_poll do_poll for(;;) { if(do_pollfd(pfd,pt)) { count++; //如果驱动的poll返回非0值,

Linux嵌入式驱动学习之路(十九)触摸屏驱动

触摸屏使用流程: 1. 按下产生中断. 2.在中断处理程序中启动AD转换XY坐标. 3.AD转换结束并产生AD中断. 4. 在AD的中断处理函数中上报信息,启动定时器. 5. 定时器时间到后进入中断,处理长按滑动.跳转到第二步 6. 松开. sd

Linux嵌入式驱动学习之路(十六)输入子系统

以前写的一些输入设备的驱动都是采用字符设备处理的.问题由此而来,Linux开源社区的大神们看到了这大量输入设备如此分散不堪,有木有可以实现一种机制,可以对分散的.不同类别的输入设备进行统一的驱动,所以才出现了输入子系统. 输入子系统引入的好处: (1)统一了物理形态各异的相似的输入设备的处理功能.例如,各种鼠标,不论PS/2.USB.还是蓝牙,都被同样处理. (2)提供了用于分发输入报告给用户应用程序的简单的事件(event)接口.你的驱动不必创建.管理/dev节点以及相关的访问方法.因此它能够

zigbee学习之路(十五):基于协议栈的按键实验

一.前言 经过上次的学习,相信大家已经初步学会使用zigbee协议进行发送和接受数据了.今天,我们要进行的实验是按键的实验,学会如何在协议栈里实现按键中断. 二.实验功能 在协议栈上实现按键中断,BUT1 按下,LED1 闪烁两次. 三.代码讲解 大家还记得,前面做过的按键实验是怎么配置的吗,其实基于zigbee的协议栈原理是差不多的,我们要对按键的接口和状态是差不多,TI已经为我们建立了专用的按键配置的代码文件,我们只要在此基础上修改就行了.先打开hel_key.c进行修改,下面是代码 #de

我的MYSQL学习心得(十五)

我的MYSQL学习心得(十五) 我的MYSQL学习心得(一) 我的MYSQL学习心得(二) 我的MYSQL学习心得(三) 我的MYSQL学习心得(四) 我的MYSQL学习心得(五) 我的MYSQL学习心得(六) 我的MYSQL学习心得(七) 我的MYSQL学习心得(八) 我的MYSQL学习心得(九) 我的MYSQL学习心得(十) 我的MYSQL学习心得(十一) 我的MYSQL学习心得(十二) 我的MYSQL学习心得(十三) 我的MYSQL学习心得(十四) 这一篇<我的MYSQL学习心得(十五)>

我的MYSQL学习心得(十五) 日志

原文:我的MYSQL学习心得(十五) 日志 我的MYSQL学习心得(十五) 日志 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(五) 运算符 我的MYSQL学习心得(六) 函数 我的MYSQL学习心得(七) 查询 我的MYSQL学习心得(八) 插入 更新 删除 我的MYSQL学习心得(九) 索引 我的MYSQL学习心得(十) 自定义存储过程和函数 我的

Android学习笔记(十五)——碎片的生命周期(附源码)

碎片的生命周期 点击下载源码 与活动类似,碎片具有自己的生命周期.理解了碎片的生命周期后,我们可以在碎片被销毁时正确地保存其实例,在碎片被重建时将其还原到前一个状态. 1.使用上一篇的项目Fragments,在Fragment1.java文件中添加如下代码: package net.zenail.Fragments; import android.app.Activity; import android.os.Bundle; import android.support.v4.app.Fragm

quick-cocos2d-x 学习系列之十五 状态机

quick-cocos2d-x 学习系列之十五 状态机 1.  代码 -- create Finite StateMachine self.fsm_ = {} cc.GameObject.extend(self.fsm_) :addComponent("components.behavior.StateMachine") :exportMethods() self.fsm_:setupState({ events = { {name = "start", from

Android学习路线(十五)Activity生命周期——重新创建(Recreating)一个Activity

先占个位置,下次翻译~ :p There are a few scenarios in which your activity is destroyed due to normal app behavior, such as when the user presses the Back button or your activity signals its own destruction by calling finish(). The system may also destroy your