一、什么是中断
一种硬件上的通知机制,用来通知CPU发生了某种需要立即处理的事件
分为:
- 内部中断 CPU执行程序的过程中,发生的一些硬件出错、运算出错事件(如分母为0、溢出等等),不可屏蔽
- 外部中断 外设发生某种情况,通过一个引脚的高、低电平变化来通知CPU (如外设产生了数据、某种处理完毕等等)
二、中断处理原理
任何一种中断产生,CPU都会暂停当前执行的程序,跳转到内存固定位置执行一段程序,该程序被称为总的中断服务程序,在该程序中区分中断源,然后进一步调用该中断源对应的处理函数。
中断源对应的处理函数被称为分中断处理程序,一般每一个分中断处理程序对应一个外设产生的中断
写驱动时,如果外设有中断,则需要编写一个函数(分中断处理程序)来处理这种中断
三、中断接口
3.1 中断申请
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
/*
参数:
irq:所申请的中断号
handler:该中断号对应的中断处理函数
flags:中断触发方式或处理方式
触发方式:IRQF_TRIGGER_NONE //无触发
IRQF_TRIGGER_RISING //上升沿触发
IRQF_TRIGGER_FALLING //下降沿触发
IRQF_TRIGGER_HIGH //高电平触发
IRQF_TRIGGER_LOW //低电平触发
处理方式:
IRQF_DISABLED //用于快速中断,处理中屏蔽所有中断
IRQF_SHARED //共享中断
name:中断名 /proc/interrupts
dev:传递给中断例程的参数,共享中断时用于区分那个设备,一般为对应设备的结构体地址,无共享中断时写NULL
返回值:成功:0 失败:错误码
*/
3.2 中断释放
void free_irq(unsigned int irq, void *dev_id);
/*
功能:释放中断号
参数:
irq:设备号
dev_id:共享中断时用于区分那个设备一般强转成设备号,无共享中断时写NULL
*/
3.3 中断处理函数原型
typedef irqreturn_t (*irq_handler_t)(int, void *);
/*
参数:
int:中断号
void*:对应的申请中断时的dev_id
返回值:
typedef enum irqreturn irqreturn_t; //中断返回值类型
enum irqreturn {
IRQ_NONE = (0 << 0),
IRQ_HANDLED = (1 << 0),
IRQ_WAKE_THREAD = (1 << 1),
};
返回IRQ_HANDLED表示处理完了,返回IRQ_NONE在共享中断表示不处理
*/
四、按键驱动
按键原理图:
exynos4412-fs4412.dts中增加节点
mykey2_node {
compatible = "mykey2,key2";
key2-gpio = <&gpx1 1 0>;//gpx1-1
interrupt-parent = <&gpx1>;
interrupts = <1 3>;//组内第二个<0 25 0> // 3 ->11 上升沿触发以及下降沿触发
};
首先初始化其中包括设备号分配,pnode指向设备节点,gpio口绑定,中断号分配
中断申请,等待队列初始化。
int __init fs4412key2_init(void)
{
int ret = 0;
dev_t devno = MKDEV(major,minor);
struct device_node *pnode=NULL;
pnode=of_find_node_by_path("/mykey2_node");
if(NULL==pnode)
{
printk("find node failed.\n");
return -1;
}
pgmydev=(struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev),GFP_KERNEL);
if(NULL==pgmydev)
{
printk("kmalloc fs4412key2 pgmydev failed.\n");
return -1;
}
pgmydev->gpio=of_get_named_gpio(pnode,"key2-gpio",0);
pgmydev->irqno=irq_of_parse_and_map(pnode,0);
/*申请设备号*/
ret = register_chrdev_region(devno,fs4412key2_num,"fs4412key2");
if(ret)
{
ret = alloc_chrdev_region(&devno,minor,fs4412key2_num,"fs4412key2");
if(ret)
{
printk("get devno failed\n");
kfree(pgmydev);
return -1;
}
major = MAJOR(devno);//容易遗漏,注意
}
/*给struct cdev对象指定操作函数集*/
cdev_init(&pgmydev->mydev,&myops);
/*将struct cdev对象添加到内核对应的数据结构里*/
pgmydev->mydev.owner = THIS_MODULE;
cdev_add(&pgmydev->mydev,devno,1);
init_waitqueue_head(&pgmydev->rq);
spin_lock_init(&pgmydev->lock);
ret=request_irq(pgmydev->irqno,key2_irq_handle,IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,"fs4412key2",pgmydev);
if(ret)
{
printk("request_irq failed.\n");
kfree(pgmydev);
pgmydev=NULL;
return -1;
}
return 0;
}
void __exit fs4412key2_exit(void)
{
dev_t devno = MKDEV(major,minor);
free_irq(pgmydev->irqno,pgmydev);
cdev_del(&pgmydev->mydev);
unregister_chrdev_region(devno,fs4412key2_num);
}
定义结构体
注意一个case:这里编译时一直报错int前面少写了;
检查后发现是fs4412_key2.h结构体后面少了一个;
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/of.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include "fs4412_key.h"
int major = 11;
int minor = 0;
int fs4412key2_num= 1;
struct fs4412key2_dev
{
struct cdev mydev;
int gpio;
int irqno;
int newflag;//用于判断是否有数据可以读取//
wait_queue_head_t rq;
struct keyvalue data;
spinlock_t lock;
};
struct fs4412key2_dev *pgmydev=NULL;
int fs4412key2_open(struct inode *pnode,struct file *pfile)
{
pfile->private_data =(void *) (container_of(pnode->i_cdev,struct fs4412key2_dev,mydev));
printk("fs4412key2_open is called\n");
return 0;
}
int fs4412key2_close(struct inode *pnode,struct file *pfile)
{
return 0;
}
//编写read函数//
ssize_t fs4412key2_read(struct file *pfile,char __user *puser,size_t count,loff_t *p_pos)
{
struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
int size = 0;
int ret = 0;
spin_lock(&pmydev->lock);
if(!pmydev->newflag)
{
if(pfile->f_flags & O_NONBLOCK)
{//非阻塞
spin_unlock(&pmydev->lock);
printk("O_NONBLOCK No Data Read\n");
return -1;
}
else
{//阻塞
spin_unlock(&pmydev->lock);
ret = wait_event_interruptible(pmydev->rq,pmydev->newflag==1);
if(ret)
{
printk("Wake up by signal\n");
return -ERESTARTSYS;
}
spin_lock(&pmydev->lock);
}
}
if(count > sizeof(struct keyvalue))
{
size = sizeof(struct keyvalue);
}
else
{
size = count;
}
ret = copy_to_user(puser,&pmydev->data,size);
if(ret)
{
spin_unlock(&pmydev->lock);
printk("copy_to_user failed\n");
return -1;
}
pmydev->newflag= 0;
spin_unlock(&pmydev->lock);
return size;
}
unsigned int fs4412key2_poll(struct file *pfile, poll_table *ptb)
{
struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
unsigned int mask=0;
poll_wait(pfile,&pmydev->rq,ptb);
spin_lock(&pmydev->lock);
if(pmydev->newflag)
{
mask |= POLLIN | POLLRDNORM;
}
spin_unlock(&pmydev->lock);
return mask;
}
struct file_operations myops = {
.owner = THIS_MODULE,
.open = fs4412key2_open,
.release = fs4412key2_close,
.read = fs4412key2_read,
.poll=fs4412key2_poll,
};
//中断处理函数
irqreturn_t key2_irq_handle(int no,void* arg)
{
struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
int status=0;
int status1=0;
int status2=0;
status1=gpio_get_value(pmydev->gpio);
mdelay(1);
status2=gpio_get_value(pmydev->gpio);
if(status1!=status2)
{
return IRQ_NONE;
}//按键防抖//
status=status1;
spin_lock(&pmydev->lock);
if(status==pmydev->data.status)
{
spin_unlock(&pmydev->lock);
return IRQ_NONE;
}
pmydev->data.code=KEY2;
pmydev->data.status=status;
pmydev->newflag=1;
spin_unlock(&pmydev->lock);
wake_up(&pmydev->rq);
return 0;
}
#ifndef FS4412_KEY_H
#define FS4412_KEY_H
enum KEYCODE
{
KEY2=1002,
KEY3,
KEY4,
};
enum KEYSTATUS
{
KEY_DOWN=0,
KEY_UP,
};
struct keyvalue
{
int code;
int status;
};
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include "fs4412_key.h"
int main(int argc,char *argv[])
{
int fd = -1;
struct keyvalue keydata = {0};
int ret=0;
if(argc < 2)
{
printf("The argument is too few\n");
return 1;
}
fd = open(argv[1],O_RDWR);
if(fd < 0)
{
printf("open %s failed\n",argv[1]);
return 2;
}
while((ret=read(fd,&keydata,sizeof(keydata)))==sizeof(keydata))
{
if(keydata.status==KEY_DOWN)
{
printf("key2 is down.\n");
}
else
{
printf("key2 is up.\n");
}
}
close(fd);
fd = -1;
return 0;
}