环境:tiny6410、fedaro9.linux2.6.28.6
原理:当按键中断发生时,由于会产生抖动,故我们按下一次实际上产生了多次中断。解决办法是,本来由按键中断获取的按键值并初始化定时器的时间值,改为在定时器函数中获取按键值。
驱动程序:
<span style="font-size:18px;">#include <linux/interrupt.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/poll.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#define __IRQT_FALEDGE IRQ_TYPE_EDGE_FALLING
#define __IRQT_RISEDGE IRQ_TYPE_EDGE_RISING
#define __IRQT_LOWLVL IRQ_TYPE_LEVEL_LOW
#define __IRQT_HIGHLVL IRQ_TYPE_LEVEL_HIGH
#define IRQT_NOEDGE (0)
#define IRQT_RISING (__IRQT_RISEDGE)
#define IRQT_FALLING (__IRQT_FALEDGE)
#define IRQT_BOTHEDGE (__IRQT_RISEDGE|__IRQT_FALEDGE)
#define IRQT_LOW (__IRQT_LOWLVL)
#define IRQT_HIGH (__IRQT_HIGHLVL)
#define IRQT_PROBE IRQ_TYPE_PROBE
static struct class *sevthdrv_class;
static struct device *sevthdrv_class_dev;
static unsigned int key_num=0;
volatile unsigned long *gpncon = NULL;
volatile unsigned long *gpndat = NULL;
static atomic_t canopen = ATOMIC_INIT(1);
static struct timer_list buttons_timer;
//struct semaphore button_lock;
static DECLARE_MUTEX(button_lock);
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
static volatile int ev_press = 0;
static struct fasync_struct *button_async_queue=NULL;
struct pin_desc *pindesc;
struct pin_desc{
unsigned int pin;
unsigned int key_value;
};
/*按下时是0x01,0x02,0x03,0x04,0x05*/
/*松开时是0x81,0x82,0x83,0x84,0x85*/
struct pin_desc pins_desc[5] = {
{S3C64XX_GPN(0), 0x01},
{S3C64XX_GPN(1), 0x02},
{S3C64XX_GPN(2), 0x03},
{S3C64XX_GPN(3), 0x04},
{S3C64XX_GPN(4), 0x05},
};
static irqreturn_t sevth_buttons_interrupt(int irq, void *dev_id)
{
pindesc = (struct pin_desc *)dev_id;
<span style="color:#33cc00;">mod_timer(&buttons_timer, jiffies+HZ/50);</span>
return IRQ_HANDLED;
}
static int sevth_drv_open(struct inode *inode, struct file *file)
{
//printk("sevth_drv_open");
if(!atomic_dec_and_test(&canopen))
{
atomic_inc(&canopen);
return -EBUSY;
}
//down_interruptible(&button_lock);
/*设置每个按键的io口中断*/
request_irq(IRQ_EINT(0),sevth_buttons_interrupt,IRQ_TYPE_EDGE_BOTH,"KEY0",&pins_desc[0]);
request_irq(IRQ_EINT(1),sevth_buttons_interrupt,IRQ_TYPE_EDGE_BOTH,"KEY1",&pins_desc[1]);
request_irq(IRQ_EINT(2),sevth_buttons_interrupt,IRQ_TYPE_EDGE_BOTH,"KEY2",&pins_desc[2]);
request_irq(IRQ_EINT(3),sevth_buttons_interrupt,IRQ_TYPE_EDGE_BOTH,"KEY3",&pins_desc[3]);
request_irq(IRQ_EINT(4),sevth_buttons_interrupt,IRQ_TYPE_EDGE_BOTH,"KEY4",&pins_desc[4]);
return 0;
}
static ssize_t sevth_drv_read(struct file *file, char __user * buffer,
size_t size, loff_t * pos)
{
wait_event_interruptible(button_waitq, ev_press);
ev_press = 0;
//printk("copy_to_use is %d \n",key_num);
copy_to_user(buffer,&key_num,1);
return 0;
}
static int sevth_drv_close(struct inode *inode, struct file *file)
{
atomic_inc(&canopen);
free_irq(IRQ_EINT(0), &pins_desc[0]);
free_irq(IRQ_EINT(1), &pins_desc[1]);
free_irq(IRQ_EINT(2), &pins_desc[2]);
free_irq(IRQ_EINT(3), &pins_desc[3]);
free_irq(IRQ_EINT(4), &pins_desc[4]);
//up(&button_lock);
return 0;
}
static unsigned int sevth_drv_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;
}
sevth_fasync(int fd, struct file *filep, int mode)
{
int ret;
ret = fasync_helper(fd, filep, mode, &button_async_queue);
return ret;
}
<span style="color:#33cc00;">static void buttons_timer_function(unsigned long data)
{
unsigned int pinval;
//pinval = pindesc->pin;
pinval = gpio_get_value(pindesc->pin);
printk("funtion button_irq pinval is %d\n",pinval);
if(pinval)
{
/*松开 */
key_num = 0x80 | pindesc->key_value;
}else
{
/*按下*/
key_num = pindesc->key_value;
}
ev_press = 1;
wake_up_interruptible(&button_waitq);
kill_fasync(&button_async_queue, SIGIO, POLL_IN);
}</span>
static struct file_operations sevth_drv_fops = {
.owner =THIS_MODULE,
.open =sevth_drv_open,
.read =sevth_drv_read,
.release = sevth_drv_close,
.poll = sevth_drv_poll,
.fasync = sevth_fasync,
};
int major;
int sevth_drv_init(void)
{
major = register_chrdev(0,"sevth_drv",&sevth_drv_fops);
sevthdrv_class= class_create(THIS_MODULE, "sevthdrv");
if(IS_ERR(sevthdrv_class))
{
printk("Err: failed increating sevthdrv_class.\n");
return -1;
}
sevthdrv_class_dev = device_create(sevthdrv_class,NULL,MKDEV(major,0),NULL,"test_button");
printk("sevth_drv_init\n");
gpncon = (volatile unsigned long *)ioremap(0x7F008830,30);
gpndat = gpncon + 1;
<span style="color:#33cc00;"><span style="white-space: pre;"></span>init_timer(&buttons_timer);//初始化定时器
<span style="white-space: pre;"></span>buttons_timer.function = buttons_timer_function;//给定时器添加处理函数
<span style="white-space: pre;"></span>add_timer(&buttons_timer); //将定时器注册进内核,此时定时器启动了</span>
return 0;
}
int sevth_drv_exit(void)
{
unregister_chrdev(major,"sevth_drv");
device_destroy(sevthdrv_class,MKDEV(major, 0));
class_destroy(sevthdrv_class);
iounmap(gpncon);
return 0;
}
module_init(sevth_drv_init);
module_exit(sevth_drv_exit);
MODULE_LICENSE("GPL");</span>
测试程序:
<span style="font-size:18px;">#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<signal.h>
/*
fifthdrvtest
*/
int fd;
int ret;
void my_signal_fun(int signum)
{
static char key_val;
read(fd,&key_val,1);
printf("key_val: 0x%x\n",key_val);
}
int main(int argc,char **argv)
{
int oflags;
fd=open("/dev/test_button",O_RDWR);
if(fd<0)
{
printf("can't open!\n");
return -1;
}
/*void (*signal)(int signo,void(*func)(int))(int )
*signo : 信号名,func;信号处理程序
*/
signal(SIGIO,my_signal_fun);
/*F_SETOWN设置接收SIGIO和SIGURG信号的进程ID*/
fcntl(fd,F_SETOWN,getpid());
/*F_GETFL 获得文件状态标志 */
oflags = fcntl(fd,F_GETFL);
/*F_SETFL设置文件状态标志*/
fcntl(fd,F_SETFL,oflags | FASYNC);
while(1){
sleep(1000);
}
return 0;
}
</span>