从零开始之驱动发开、linux驱动(二十一、内核定时器的使用)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_16777851/article/details/82872516

在异步通知章节中,使用驱动程序主动通用应用层调用绑定的信号函数。

有一个缺陷,那就是对按键程序来说,会出现抖动,会多次进入中断函数,这会影响应用层对按键事件的处理产生麻烦。

正常情况下都是按下和松开成对出现的,但上图因为有了抖动,则出现了问题。

一般我们消抖都是采用延时的方式处理的。

在操作系统中一般延时有两种选择

1.通过sleep、udelay等函数延时

2.通过定时延时

因为sleep的延时是秒级别的,udelay最大只能延时2000us,即2ms

通过内核定时器

关于内核定时器的使用可以查看我之前写的文章

https://blog.csdn.net/qq_16777851/article/details/81208671

我们CPU默认是200ZH的频率运行。

通常抖动的时间是在20ms以内,即按20ms的消抖,足以让电平稳定。

在异步通知软件的基础上修改。

#include <linux/fs.h>       /* 包含file_operation结构体 */
#include <linux/init.h>     /* 包含module_init module_exit */
#include <linux/module.h>   /* 包含LICENSE的宏 */
#include <linux/types.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/gfp.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/highmem.h> /* For wait_event_interruptible */
#include <linux/poll.h>
#include <asm/gpio.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <linux/sysctl.h>
#include <linux/timer.h>


static unsigned int major;
static struct class *button_class;
static struct device *button_dev;

static unsigned char key_val;


static atomic_t cnt = ATOMIC_INIT(1);
static struct timer_list button_timer;

static struct fasync_struct *button_fasync;



struct pin_desc {
    unsigned int    pin;
    unsigned int    key_val;
};


static struct pin_desc * pin_dev = NULL;

/* 按下时 值分别是  0x01 , 0x02 */
/* 松开时 值分别是  0x00 , 0x00 */
static struct pin_desc pins_desc[] = {
    {S5PV210_GPH0(2), 0x01},
    {S5PV210_GPH0(3), 0x02},
};


static irqreturn_t irq_handler(int irq, void *dev_id)
{
    pin_dev = dev_id;

    /* 中断函数中,修改定时器值为 4个jiffies后处理定时函数 */
    mod_timer(&button_timer, jiffies + 4);


    return IRQ_HANDLED;
}


void button_timer_func(unsigned long date)
{
    struct pin_desc *p = NULL;
    int pin_val;

    /* 判断当add_timer时jiffies为0时会不会进定时中断函数 */
    if(pin_dev == NULL) {
        printk(KERN_INFO"pin_dev is NULL\n");
        return;
    }

    p = pin_dev;

    pin_val =  gpio_get_value(p->pin);

 
    /* 得到键值,判断时按下还是松开 */
    if(pin_val)
    {
        /* 松开 */
        key_val &= ~p->key_val;
    }
    else
    {
        /* 按下 */
        key_val |= p->key_val;
    }

    /* 产生一个异步读信号 */
    kill_fasync(&button_fasync, SIGIO, POLL_IN);

}

/* open函数 */
static int button_drv_open(struct inode *inode, struct file *file)
{
    int ret = 0;

    /* 如果自减后为0,说明没人打开,否则已经被人打开 */
    if(!atomic_dec_and_test(&cnt)) {
        /* 已经被人打开,加回原来的操作,并返回忙 */
        atomic_inc(&cnt);
        return -EBUSY;
    }


    ret = request_irq(IRQ_EINT(2), irq_handler, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "irq-eint2",&pins_desc[0]);
    if(ret)
    {
        printk(KERN_ERR"request_irq IRQ_EINT(2) fail");
        return -EBUSY;
    }
    ret = request_irq(IRQ_EINT(3), irq_handler, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "irq-eint3",&pins_desc[1]);
    if(ret)
    {
        free_irq(IRQ_EINT(2), &pins_desc[0]);
        printk(KERN_ERR"request_irq IRQ_EINT(3) fail");
        return -EBUSY;
    }

    /* 定时器初始化 */
    init_timer(&button_timer);
    /* 定时到后执行函数 */
    button_timer.function = button_timer_func;

    /* 把该定时器,加入到定时器链表中 */
    add_timer(&button_timer);

    return 0;
}


static ssize_t button_drv_read(struct file *file, char __user *array, size_t size, loff_t *ppos)
{
    int len;

    if(size < 1)
    {
        return -EINVAL;
    }


    /* 赋值只是为了消除告警 */
    len = copy_to_user(array , &key_val, 1);


    return 1;
}




int button_drv_fasync (int fd, struct file *file, int on)
{
    printk(KERN_INFO"button_drv_fasync\n");

    /* 增加一个异步通知到到本设备的异步通知队列中 */
    return fasync_helper(fd , file, on, &button_fasync);

}


static int button_drv_close(struct inode *inode, struct file *file)
{
    free_irq(IRQ_EINT(2), &pins_desc[0]);
    free_irq(IRQ_EINT(3), &pins_desc[1]);

    /* 移除一个async */
    fasync_helper(-1, file, 0, &button_fasync);

    del_timer(&button_timer);

    atomic_inc(&cnt);  /* 释放设备 */

    return 0;
}

static const struct file_operations button_drv_file_operation = {
    .owner      = THIS_MODULE,
    .open       = button_drv_open,
    .read       = button_drv_read,
    .fasync     = button_drv_fasync,
    .release    = button_drv_close,
};

static int __init button_drv_init(void)
{
    /* 获取一个自动的主设备号 */
    major =  register_chrdev(0,"button_drv",&button_drv_file_operation);
    if(major < 0)
    {
        printk(KERN_ERR"register_chrdev button_drv fail \n");
        goto err_register_chrdev;
    }

    /* 创建一个类 */
    button_class = class_create(THIS_MODULE, "button_class");
    if(!button_class)
    {
        printk(KERN_ERR"class_create button_class fail\n");
        goto err_class_create;
    }

    /* 创建从属这个类的设备 */
    button_dev = device_create(button_class,NULL,MKDEV(major, 0), NULL, "button");
    if(!button_dev)
    {
        printk(KERN_ERR"device_create button_dev fail \n");
        goto err_device_create;
    }

    return 0;

/* 倒影式错误处理机制 */
err_device_create:
    class_destroy(button_class);
err_class_create:
    unregister_chrdev(major,"button_drv");
err_register_chrdev:

    return -EIO;
}


static void __exit button_drv_exit(void)
{
    /* 注销类里面的设备 */
    device_unregister(button_dev);
    /* 注销类 */
    class_destroy(button_class);
    /* 注销字符设备 */
    unregister_chrdev(major,"button_drv");
}

module_init(button_drv_init);
module_exit(button_drv_exit);   

修改两点:

1.使用原子操作,保证该设备只能被一个应用程序打开。

2.增加内核定时器,做消抖处理

应用程序和异步通知的一样。

经过多次测试,没有出现抖动现象。

细心的朋友注意到了,默认增加定时器到内核的定时器链表是没有初始值的,即为0.因为没有按键,所以pin_dev 的值是NULL,我在定时器处理函数中做了检查,判断定时器默认值为0时会不会立即进去

因为刚启动引用程序,没按键,即没外部中断时就已经进去了。

即证明为传入的0时,会立刻执行一次定时器处理函数。

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/82872516