按键定时器去抖动
按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,开关不会马上稳定地接通或断开。因而在闭合及断开的瞬间总是伴有一连串的抖动的。按键去抖动的方法主要有两种,一种是硬件电路去抖动;另一种就是软件延时去抖动。而延时又一般分为了两种,一种是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!\n");
}
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驱动文件到开发板运行。