linux按键设备驱动
最近我在写字符设备驱动程序,前两天想自己动手写个按键的驱动程序,但是结果并不是我所期待的,希望您能帮我一下,谢谢啦!
程序源代码如下:
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/semaphore.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#define count 1
#define KEY_NUM 4
#define KEY_UP 1
#define MYKEY_DOWN 0
typedef unsigned char key_ret;
static struct kbd_dev
{
unsigned int keystatus[KEY_NUM]; /* 按键状态,但是我还没有使用它 */
struct cdev *cdev;
};
static struct key_info
{
int irq_no; /* 中断号 */
char gpio_port[4]; /* 端口号 */
int key_no; /* 键值 */
};
static struct key_info key_info_tab[4] =
{
{IRQ_EINT0, "GPF1", 1},
{IRQ_EINT1, "GPF2", 2},
{IRQ_EINT2, "GPF3", 3},
{IRQ_EINT4, "GPF4", 4},
};
static int major = 280;
static int minor = 0;
static struct kbd_dev *kbd_devp = NULL;
static struct timer_list key_timer[KEY_NUM]; /* 四个内核定时器,用于消抖 */
volatile unsigned long *gpfcon;
static irqreturn_t key_irq_handler(int irq, void *dev_id)
{
printk("handle with irq : %d,irq_no:%d\n",irq,*(int *)dev_id);
/* 禁止该中断 */
disable_irq_nosync(irq);
printk("disbale_irq_nosync :%d\n",irq);
/* 设置1s后处理定时处理函数 */
key_timer[*(int *)dev_id - 1].expires = jiffies + HZ;
add_timer(&key_timer[*(int *)dev_id - 1]);
return IRQ_HANDLED;
}
static void timer_delay(unsigned long which)
{
printk("the irq_no:%d down\n",which);
/* 使能对应的中断 */
enable_irq(which);
}
static ssize_t kbd_read (struct file *filp, char __user *user, size_t size, loff_t *off)
{
return 0;
}
static ssize_t kbd_write (struct file *filp, const char __user *user, size_t size, loff_t *off)
{
return 0;
}
static int kbd_open(struct inode *inode, struct file *filp)
{
return 0;
}
static int kbd_release (struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations kbd_fops =
{
.owner = THIS_MODULE,
.open = kbd_open,
.release = kbd_release,
.read = kbd_read,
.write = kbd_write,
};
static int kbd_init(void)
{
int devno; /* 设备号 */
int result,err;
int i;
devno = MKDEV(major,minor);
if(major) /* 如果主设备号等于0,则动态分配设备号 */
result = register_chrdev_region(devno, count, "kbd");
else
{
result = alloc_chrdev_region(&devno, 0, count, "kbd");
MAJOR(devno);
}
if(result) /* 如果分配失败,则返回 */
{
printk("alloc char device failure!\n");
return result;
}
/* 为设备结构分配内存空间 */
kbd_devp = kmalloc(sizeof(struct kbd_dev),GFP_KERNEL);
if(!kbd_devp)
{
printk("alloc memory failuere!\n");
return -ENOMEM;
goto unregister;
}
/* 为分配的设备结构的内存空间进行初始化 */
memset(kbd_devp, 0, sizeof(struct kbd_dev));
kbd_devp->cdev = cdev_alloc(); /* 分配一个字符设备结构给kbd_devp->dev */
cdev_init(kbd_devp->cdev, &kbd_fops); /* 初始化字符设备结构 */
kbd_devp->cdev->owner= THIS_MODULE;
err = cdev_add(kbd_devp->cdev, devno, count); /* 向系统添加字符设备结构 */
if(err == 0)
printk("cdev_add success!\n");
gpfcon = ioremap(0x56000050,4); /* 映射按键对应的物理地址 */
*gpfcon = 0x22a; /* 设置控制按键的端口为中断 */
for(i = 0; i < KEY_NUM; i++)
{
/* 注册这四个按键中断,低电平触发,给中断处理程序传入键值 */
if(request_irq(key_info_tab[i].irq_no, key_irq_handler, IRQ_TYPE_LEVEL_LOW,
key_info_tab[i].gpio_port, &key_info_tab[i].key_no))
{
printk("can't get irq:%i\n",key_info_tab[i].irq_no);
return 0;
}
kbd_devp->keystatus[i] = KEY_UP; /* 设置按键状态为松开 */
/* 初始化四个内核定时器,给定时器处理函数传入中断号 */
setup_timer(&key_timer[i], timer_delay, key_info_tab[i].irq_no);
}
printk("device add success!\n");
return 0;
unregister:
unregister_chrdev_region(MKDEV(major,minor), count);
return result;
}
static void kbd_exit(void)
{
int i;
for(i = 0; i < KEY_NUM; i++)
{
free_irq(key_info_tab[i].irq_no, &key_info_tab[i].key_no);
}
iounmap(gpfcon);
cdev_del(kbd_devp->cdev);
kfree(kbd_devp->cdev);
kfree(kbd_devp);
unregister_chrdev_region(MKDEV(major,minor), count);
printk("device remove success!\n");
}
module_init(kbd_init);
module_exit(kbd_exit);
MODULE_LICENSE("GPL");
我在我的开发板上使用# insmod key.ko 插入模块,使用命令# mknod /dev/kbd c 280 0创建设备节点,然后就直接按开发板的按键,串口上的输出是(这只是一个按键的信息,其他三个按键的输出信息与这个类似):
handle with irq : 16,irq_no:1
disbale_irq_nosync :16
the irq_no:16 down
handle with irq : 16,irq_no:1
disbale_irq_nosync :16
the irq_no:16 down
其中第1、2行同时输出的,然后等待1s,再输出3、4、5行,再等待1s,输出第6行。
我的疑问是:
输出1、2行之后,当前中断已经被屏蔽掉,直到1s后进入定时处理函数恢复中断,此中断才可以继续使用,我按键只按了一下,应该只显示前三行的信息,后三行的信息我不知道怎么来的。
我使用命令:
# cat /proc/devices
输出(其他信息省略掉了,一下类似):
Character devices:
280 kbd
# lsmod
Not tainted
key 3204 0 - Live 0xbf067000
# cat /proc/interrupts
CPU0
16: 18 s3c-ext0 GPF1
17: 10 s3c-ext0 GPF2
18: 2 s3c-ext0 GPF3
48: 2 s3c-ext GPF4
[解决办法]
原理图呢?谁知道你这是什么按键啊
[解决办法]
这应该是发生了抖动的问题,将
key_timer[*(int *)dev_id - 1].expires = jiffies + HZ;
add_timer(&key_timer[*(int *)dev_id - 1]);
放到
disable_irq_nosync(irq);
printk("disbale_irq_nosync :%d\n",irq);
前面试试看
[解决办法]
按下,中断一次,弹起,同样中断一次
可以这样试试: 按下不放手,过5或者10秒钟再放手,看看输出信息。