linux驱动程序之定时器防按键抖动

时间:2022-07-03 23:28:52

前提:按键按下通过中断方式进行处理。本博文基于上一个博文改写:上一篇博文

目的:


使用定时器防止按键抖动产生中断。

方法:


每次发生按键中断后,推迟10ms进行处理,最终将会只执行一次定时器中断处理函数。这样就避免了按键抖动引起的误中断。

步骤:


1、定义一个定时器结构体

static struct timer_list button_timer;//定义一个定时器,用于按键消抖功能

2、在init函数中初始化这个定时器

init_timer(&button_timer);
button_timer.function = buttons_timer_function;
//button_timer.expires = 0; //没有配置时默认值是0
add_timer(&button_timer);
其中:

init_timer初始化这个定时器

button_timer.function 告诉内核定时器中断发生后调用的处理函数为:buttons_timer_function

add_timer  将这个定时器告诉内核,可以开始了

3、定义定时器处理函数:

static void buttons_timer_function(unsigned long data)
这个函数的功能在本例子中就是都取按键键值,并发送信号,可将上一篇博文中的中断处理函数中的内容直接交给这个函数,即:

static void buttons_timer_function(unsigned long data)
{
struct pin_desc * pindesc = irq_pd;
unsigned int pinval;



pinval = s3c2410_gpio_getpin(pindesc -> pin);
if(pinval)//松开
{
keyval = 0x80|pindesc->key_value;
}
else
{
keyval = pindesc->key_value;
}
ev_press =1;//中断发生
wake_up_interruptible(&button_wait_q);
kill_fasync(&button_async,SIGIO,POLL_IN);
}

注意:

设备描述符是从中断服务程序中获得,即irq_pd要从中断服务程序中获得(可定义一个全局变量)

4、修改按键中断服务程序

按键中断服务程序主要负责将到来的中断推迟10ms执行要进行的动作。

static irqreturn_t buttons_irq(int irq,void *dev_id)
{
irq_pd = (struct pin_desc *)dev_id;
/*10ms后启动定时器*/
mod_timer(&button_timer,jiffies+HZ/100);
return IRQ_HANDLED;
}
irq_pd = (struct pin_desc *)dev_id; 即获得设备描述符

mod_timer 修改定时器目的值,这个值是以jiffies为基准,即现在时刻的值是jiffies,程序要在10ms后进行处理,所以 +HZ/200  1HZ=1s  

5、在定时器服务程序中增加一个条件:

if(!pindesc)//防止初始化时add_timer后立即执行定时器中断
return;
防止在init函数中进行addtimer后,系统会马上执行定时器服务程序,这与我们想要的是不符的


完整驱动驱动程序:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/irqs.h>//这个在/opt/EmbedSky/linux-2.6.30.4/arch/arm/mach-s3c2410/include/mach 路径
#include <linux/interrupt.h>
#include <linux/poll.h>

MODULE_LICENSE("Dual BSD/GPL");

static struct class *buttondrv_class;
static struct class_devices *buttondrv_class_dev;

/* */
static DECLARE_WAIT_QUEUE_HEAD(button_wait_q);
/*中断事件标志,中断服务程序将他置1,read函数将他置0*/
static volatile int ev_press =0;

volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;

static struct timer_list button_timer;//定义一个定时器,用于按键消抖功能

static unsigned keyval;
static struct fasync_struct *button_async;

struct pin_desc{
unsigned int pin;
unsigned int key_value;
};
/*按键按下时是:0x01 0x02 0x03 0x04*/
/*按键松开时是:0x81 0x82 0x83 0x84*/
struct pin_desc pins_desc[4] =
{
{S3C2410_GPF1,0x01},
{S3C2410_GPF4,0x02},
{S3C2410_GPF2,0x03},
{S3C2410_GPF0,0x04},
};

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

static struct pin_desc *irq_pd;

/*
* 确定按键值
*/
static irqreturn_t buttons_irq(int irq,void *dev_id)
{
irq_pd = (struct pin_desc *)dev_id;
/*10ms后启动定时器*/
mod_timer(&button_timer,jiffies+HZ/200);
return IRQ_HANDLED;
}
static int button_dev_open(struct inode *inode ,struct file* file)
{
if(file->f_flags & O_NONBLOCK)
{
//非阻塞
if(down_trylock(&button_lock))//如果无法获取这个信号量
{
return EBUSY;
}
}
else
{
//阻塞
down(&button_lock);
}

//配置按键的引脚 GPF0,1,2,4为输入引脚
request_irq(IRQ_EINT1,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key1",&pins_desc[0]);
request_irq(IRQ_EINT4,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key2",&pins_desc[1]);
request_irq(IRQ_EINT2,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key3",&pins_desc[2]);
request_irq(IRQ_EINT0,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key4",&pins_desc[3]);

return 0;
}
ssize_t button_dev_read(struct file *file,char __user *buf,size_t size,loff_t *ppos)
{
if(size !=1)
{
return -EINVAL;
}
if(file->f_flags & O_NONBLOCK)//阻塞
{
if(!ev_press)
return -EAGAIN;
}
else//阻塞
{
/*如果没有按键动作发生 就休眠*/
wait_event_interruptible(button_wait_q,ev_press);
}

/*如果有按键动作发生,直接返回*/
copy_to_user(buf,&keyval,1);
ev_press = 0;
return 0;
}
int button_dev_close(struct inode* inode ,struct file *file)
{
free_irq(IRQ_EINT1,&pins_desc[0]);
free_irq(IRQ_EINT4,&pins_desc[1]);
free_irq(IRQ_EINT2,&pins_desc[2]);
free_irq(IRQ_EINT0,&pins_desc[3]);

/*释放信号量*/
up(&button_lock);
return 0;
}

static int button_dev_fasync(int fd,struct file *filp,int on)
{
printk("button_dev_fasync \n");
return fasync_helper(fd,filp,on,&button_async);
}
static struct file_operations button_sdv_fops =
{
.owner = THIS_MODULE,
.open = button_dev_open,
.read = button_dev_read,
.release = button_dev_close,
.fasync= button_dev_fasync,
};
int major;

static void buttons_timer_function(unsigned long data)
{
struct pin_desc * pindesc = irq_pd;
unsigned int pinval;

if(!pindesc)//防止初始化时add_timer后立即执行定时器中断
return;

pinval = s3c2410_gpio_getpin(pindesc -> pin);
if(pinval)//松开
{
keyval = 0x80|pindesc->key_value;
}
else
{
keyval = pindesc->key_value;
}
ev_press =1;//中断发生
wake_up_interruptible(&button_wait_q);
kill_fasync(&button_async,SIGIO,POLL_IN);
}

static int button_dev_init(void)//入口函数
{
init_timer(&button_timer);
button_timer.function = buttons_timer_function;
//button_timer.expires = 0; //没有配置时默认值是0
add_timer(&button_timer);

major = register_chrdev(0,"button_drv",&button_sdv_fops);

buttondrv_class = class_create(THIS_MODULE,"button_drv");
if(IS_ERR(buttondrv_class))
return PTR_ERR(buttondrv_class);
buttondrv_class_dev= device_create(buttondrv_class,NULL,MKDEV(major,0),NULL,"wq_button");
if(unlikely(IS_ERR(buttondrv_class_dev)))
return PTR_ERR(buttondrv_class_dev);

/*映射物理地址*/
gpfcon = (volatile unsigned long *) ioremap(0x56000050 ,16);
gpfdat = gpfcon + 1;

return 0;
}
static void button_dev_exit(void)
{
unregister_chrdev(major,"button_drv");
device_unregister(buttondrv_class_dev);
class_destroy(buttondrv_class);

iounmap(gpfcon);
}
module_init(button_dev_init);
module_exit(button_dev_exit);

完整测试程序:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>

int fd;
void my_signal_fun(int signum)
{
unsigned char key_val;
read(fd,&key_val,1);
printf("key_val: 0x%x\n",key_val);
}
int main(int argc, char **argv)
{
int ret=0;
int Oflags;
unsigned char key_val;
signal(SIGIO,my_signal_fun);
fd = open("/dev/wq_button",O_RDWR);//默认以阻塞方式打开
if(fd<0)
{
printf("can't open \n");
}


while(1)
{
unsigned char key_val;
ret = read(fd,&key_val,1);
printf("key_val: 0x%x ret=%d\n",key_val,ret);
}
return 0;
}