5.按键定时器去抖动

按键定时器去抖动

按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,开关不会马上稳定地接通或断开。因而在闭合及断开的瞬间总是伴有一连串的抖动的。按键去抖动的方法主要有两种,一种是硬件电路去抖动;另一种就是软件延时去抖动。而延时又一般分为了两种,一种是for循环等待,另一种是定时器延时。在操作系统中,由于效率方面的原因,一般不允许使用for循环来等待,只能使用定时器。

上面是内核定时器的结构。其中expires是定时器延时的时间,function函数是定时器超时后要做的事情。

由上面可以知道,定时器的使用分为了四个步骤:1.定义定时器的变量,就是timer_list结构。2.要对结构进行初始化。Init_timer是系统自动运行的初始化函数,能初始化很大部分timer_list里面的成员。但是,超时函数是需要我们自己设置,就是function。3.使用add_timer函数注册定时器。4.mod_timer重启定时器。注意,定时器不是循环的,需要重复调用mod_timer函数。

接下来是例子:

Nkey.c:

#include <linux/module.h>        /* For module specific items */

#include <linux/fs.h>            /* For file operations */

#include <linux/ioport.h>        /* For io-port access */

#include <linux/io.h>            /* For inb/outb/... */

#include <linux/init.h>

#include <linux/miscdevice.h>

#include <linux/interrupt.h>

#include <linux/slab.h>

#define GPNCON 0x7f008830

#define GPNDAT 0x7f008834

struct work_struct *work1;

struct timer_list key_timer;//定义定时器

unsigned int *gpio_data;

void work1_func(struct work_struct *work)

{

    //启动定时器 100毫秒超时=HZ/10,HZ=1秒。jiffies是系统当前时间

    mod_timer(&key_timer,jiffies+HZ/10);

}

void key_timer_func(unsigned long data)

{

    unsigned int key_val;

    //超时的时候,就要读取data

    key_val=readw(gpio_data)&0x01;//读出一个按键EINT0的值。

    //当他被按下,就是低电平的时候,就是被按下了。才是有效的按键

    if(0==key_val)//真正按下

        printk("<0> key down! ");

}

irqreturn_t key_int(int irq, void *dev_id)

{

    //1.检测是否发生了按键中断

    //2.清除已经发生的按键中断

//前面的都是硬件相关的工作,必须在中断里面执行

//下面是硬件无关的工作,我们把它提到中断以外的work1_func函数去处理。

    //3.打印按键值

    schedule_work(work1);

    

    return 0;

}

void key_hw_init()

{

    unsigned int *gpio_config;

    unsigned short data;

    gpio_config = ioremap(GPNCON,4);

    data = readw(gpio_config);

    data &= ~0b11;

    data |= 0b10;

    writew(data,gpio_config);

    gpio_data = ioremap(GPNDAT,4);

}

int key_open(struct inode *node, struct file *filp)

{

    return 0;

}

struct file_operations key_fops =

{

    .open = key_open,

};

struct miscdevice key_miscdevice =

{

    .minor = 200,

    .name = "OK6410key",

    .fops = &key_fops,

};

static int key_init()

{

    misc_register(&key_miscdevice);

    //硬件初始化

    key_hw_init();

    //注册中断处理程序

    request_irq(IRQ_EINT(0),key_int, IRQF_TRIGGER_FALLING,"OK6410key",0);

    //2. 创建工作

    work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);

    INIT_WORK(work1, work1_func);

    //定时器初始化

    init_timer(&key_timer);

    key_timer.function=key_timer_func;

    

    //注册定时器

    add_timer(&key_timer);

    return 0;

}

static void key_exit()

{

    misc_deregister(&key_miscdevice);

    

}

module_init(key_init);

module_exit(key_exit);

编译成功,拷贝生成的.ko驱动文件到开发板运行。

原文地址:https://www.cnblogs.com/FORFISH/p/5188562.html