看了好长时间的中断,终于动手写了个基于中断的按键驱动。本次驱动不是十分成功,中断都进去了,但是打印出的信息有些问题。现在还没有找到原因。
这个中断编写时参照内核中的按键驱动进行编写的。
关于内核的中断例程刚开始看,有许多疑问:
怎么不见它配置寄存器呢?我在网上查的资料说是在request_irq中进行了底层硬件的配置?还有就是我看到网上有的6410的按键中断驱动和2440的按键中断驱动几乎一样,是不是同样的驱动,可以同时用在2440和6410的开发板上?我对此的理解是每种开发板上的系统都是与开发板硬件对应的,其用的头文件不同,所以相同的按键中断程序进行编译后可以放在不同的开发板的系统中。还有就是关于中断号,例程用的是系统已经定义好的,我在网上查资料说内核可以根据这个定义好的外部中断号来配置与其名字相对应的管脚。如果我自己随便定义了可以用的终端号的话,我就要自己来进行底层硬件的配置了?
对于上述问题进过看资料,我的理解是request_irq中注册的中断号irq是系统内核根据硬件已经定义好的,不是随便定义的。然后request_irq根据不同的中断号irq来初始化底层的硬件,对于不同的开发板虽然request_irq在申请时irq看着一样,例如都是IRQ_EINT0,但是在编译的时候根据不同板子上的系统,它编译出来的驱动的底层配置是不同的。
响应中断的时候内核根据硬件来确定是哪个中断号,然后根据申请的中断来进行处理。具体的可以参照韦东山老师的《嵌入式开发完全手册》一书中的中断部分的讲解。
下面开始编写驱动:
前面的流程和LED驱动的编写都差不多。
中断的注册在open函数中,这样可以避免在不需要的时候中断的被占用。
<pre name="code" class="html">static int button_open(struct inode *inode, struct file *file)
{ int i;
int err=0;
for(i=0;i<6;i++)
{
err=request_irq(button_irq[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, button_irq[i].name, (void *)&button_irq[i]);//双边沿触发,按键按下弹起都会触发中断
if(err)
{
break;
}
}
if(err) //中断的错误处理,注册失败要释放之前注册号的中断
{ i--;
for(;i>=0;i--)
{
free_irq(button_irq[i].irq, (void *)&button_irq[i]);
}
return -EBUSY; //错误返回,次返回表示中断号已被占用
}
return 0;
}
<span style="color:#ff0000;">IRQ_TYPE_EDGE_BOTH:表示双边沿触发。</span>
</pre><pre name="code" class="html">close函数,程序关闭的时候要释放中断号,避免占用浪费。
static int button_close(struct inode *inode, struct file *file)
{ int i;
for(i=0;i<6;i++)
free_irq(button_irq[i].irq, (void *)&button_irq[i]);
return 0;
}
read函数,向应用程序传递键值
static int button_read(struct file *file, char __user *buff, size_t size, loff_t *offt )
{
<span style="color:#ff0000;">while</span>( ev_press == 0)
{
if(file->f_flags&O_NONBLOCK) //判断是否有非阻塞的方式,
return -EAGAIN; //含有的非阻塞标志的返回值
else wait_event_interruptible(button_wait, ev_press); //ev_press不为1,加入等待队列
}
ev_press=0;
if(copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), size)))
return -EFAULT; //错误返回
else return 0;
}
此处用while循环更为严谨,因为等待可以被中断信号唤醒,所以用while循环,唤醒后再进行一次条件判断,如果条件不成立,则再次进行等待。
poll函数,实现轮询。
static int button_poll( struct file *file, struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_wait, wait);
if (ev_press)
mask |= POLLIN | POLLRDNORM; //可读
return mask;
}
阻塞不是发生在poll中的,而是发生在应用程序的select函数中。
下面是中断处理例程
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
struct button_irq_desc *button_irqs= (struct button_irq_desc *) dev_id;
int down;
printk("in"); //内核打印,判断是否进入中断
down = !s3c2410_gpio_getpin(button_irqs->pin);
if (down != (key_values[button_irqs->number] & 1))
{
key_values[button_irqs->number] = '0' + down;
ev_press = 1;
wake_up_interruptible(&button_wait);
}
return IRQ_HANDLED; //大部分条件下的中断返回值
}
以上就是驱动程序的主要部分。
加载驱动进行验证,
输入 exec 5</dev/mybutton就可以通过cat /proc/interrupts看到注册的外部驱动了
输入exec 5<&- 结束
应用程序编写的时候要注意,printf打印函数必须要跟 \n 结束
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(int argc,int *argv[])
{
int fd;
fd_set rfds;
int key_status[6];
if (argc>1 )
{
printf("only one part!\n");
return 0;
}
fd=open("/dev/mybutton",O_RDWR);
if(fd<0)
{
printf("err!\n");
return 0;
}
while(1)
{
int i;
int ret;
FD_ZERO(&rfds);
FD_SET(fd,&rfds);
select(fd + 1,&rfds,NULL,NULL,NULL);
if(FD_ISSET(fd,&rfds))
{
ret = read(fd, key_status,6);
if(ret==-1)
{
printf("read button faild!\n");
return 0;
}
for(i=0;i<6;i++)
{
if(key_status[i] == 0)
{
printf("key%d DOWN\n",i+1);
}
}
}
}
close(fd);
return 0;
}
通过select函数来查询。
FD_ZERO(fd_set *set)
清除一个文件描述符集。
FD_SET(int fd,fd_set *set)
将一个文件描述符加入文件描述符集中。
FD_CLR(int fd,fd_set *set)
将一个文件描述符从文件描述符集中清除。
FD_ISSET(int fd,fd_set *set)
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/gpio.h>
struct cdev cdev;
dev_t devno;
struct button_irq_desc{
int irq;
int pin;
int number;
char *name;
};
struct button_irq_desc button_irq[]=
{
{IRQ_EINT8 , S3C2410_GPG(0) , 0, "KEY0"},
{IRQ_EINT11, S3C2410_GPG(3) , 1, "KEY1"},
{IRQ_EINT13, S3C2410_GPG(5) , 2, "KEY2"},
{IRQ_EINT14, S3C2410_GPG(6) , 3, "KEY3"},
{IRQ_EINT15, S3C2410_GPG(7) , 4, "KEY4"},
{IRQ_EINT19, S3C2410_GPG(11), 5, "KEY5"},
};
static int ev_press = 0;
static char key_values [] = {'0', '0', '0', '0', '0', '0'};
static DECLARE_WAIT_QUEUE_HEAD(button_wait);
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
struct button_irq_desc *button_irqs= (struct button_irq_desc *) dev_id;
int down;
printk("in"); //内核打印,判断是否进入中断
down = !s3c2410_gpio_getpin(button_irqs->pin);
if (down != (key_values[button_irqs->number] & 1))
{
key_values[button_irqs->number] = '0' + down;
ev_press = 1;
wake_up_interruptible(&button_wait);
}
return IRQ_HANDLED; //大部分条件下的中断返回值
}
static int button_open(struct inode *inode, struct file *file)
{ int i;
int err=0;
for(i=0;i<6;i++)
{
err=request_irq(button_irq[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, button_irq[i].name, (void *)&button_irq[i]);//双边沿触发,按键按下弹起都会触发中断
if(err)
{
break;
}
}
if(err) //中断的错误处理,注册失败要释放之前注册号的中断
{ i--;
for(;i>=0;i--)
{
free_irq(button_irq[i].irq, (void *)&button_irq[i]);
}
return -EBUSY; //错误返回,次返回表示中断号已被占用
}
return 0;
}
static int button_close(struct inode *inode, struct file *file)
{ int i;
for(i=0;i<6;i++)
free_irq(button_irq[i].irq, (void *)&button_irq[i]);
return 0;
}
static int button_read(struct file *file, char __user *buff, size_t size, loff_t *offt )
{
while( ev_press == 0)
{
if(file->f_flags&O_NONBLOCK) //判断是否有非阻塞的方式,
return -EAGAIN; //含有的非阻塞标志的返回值
else wait_event_interruptible(button_wait, ev_press); //ev_press不为1,加入等待队列
}
ev_press=0;
if(copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), size)))
return -EFAULT; //错误返回
else return 0;
}
static int button_poll( struct file *file, struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_wait, wait);
if (ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
}
struct file_operations button_fops=
{
.owner = THIS_MODULE,
.open = button_open,
.release = button_close,
.read = button_read,
.poll = button_poll,
};
static int button_init()
{
cdev_init(&cdev,&button_fops);
alloc_chrdev_region(&devno, 0 , 1 , "mybutton");
cdev_add(&cdev, devno, 1);
return 0;
}
static int button_exit()
{
cdev_del(&cdev);
unregister_chrdev_region(devno,1);
}
module_init(button_init);
module_exit(button_exit);