1.编写一个外部中断
1.获取外部中断编号
int gpio_to_irq(unsigned int gpio)
功能:通过Io口编号转换成对应的外部中断编号
参数:gpio 要获取的IO口编号
返回值:成功:中断编号; 失败:- EINVAL
2.注册中断函数
int request_irq(unsigned int irq,irq_handler_t handler, unsigned long flags, const char *name,void *dev_id);
功能:向内核注册一个中断服务函数,当发生中断号为 irq 的中断时候,会执行 handler 指针函数。
参数:
irq:中断编号(每个中断源有惟一的编号),这里的中断不是看硬件手册,这一点和裸机编程不同。
handler:中断服务函数指针。 原型 typedef irqreturn_t (*irq_handler_t)(int, void *)。
flags:中断属性,如快速中断,共享中断,如果是外部分中断还有:上升沿,下降沿触发中断这类标志。
flags | 16进制 | 意义 |
---|---|---|
#define IRQF_TRIGGER_NONE | 0x00000000 | 没有设置触发边沿 |
#define IRQF_TRIGGER_RISING | 0x00000001 | 设置触发边沿为上升沿 |
#define IRQF_TRIGGER_FALLING | 0x00000002 | 设置触发边沿为下降沿 |
#define IRQF_TRIGGER_HIGH | 0x00000004 | 设置触发方式 为高电平 |
#define IRQF_TRIGGER_LOW | 0x00000008 | 设置触发方式 为低电平 |
#define IRQF_TRIGGER_PROBE | 0x00000010 | 快速中断标志 |
#define IRQF_DISABLED | 0x00000020 | 指明该中断是独占中断,只能注册一次 |
#define IRQF_SHARED | 0x00000080 | 指明该中断是共享中断,可以注册多次 |
name:中断名字, 注册后会出现/proc/irq/irq号/name文件夹出现。
dev_id: 这个参数是传递给中断服务函数。对共享中断来说, 这个参数一定有要,当注销共享中断中的其中一个时, 用这个来标识要注销哪一个。 对于有惟一入口的中断,可以传递 NULL,但是一般来说都会传递一个有意义指针,在中断程序中使用,以方便编程。
返回值:
0 表示成功
-EINVAL (无效参数22)表示中断号无效。
-EBUSY (设备或者资源忙16)表示中断已经被占用。
3.注销中断函数
void free_irq(unsigned int irq,void * dev_id)
功能:从内核中断链表上删除一个中断结构
参数:
irq:中断编号
dev_id: 要和注册中断函数的最后一个参数 dev_id 相同
4.使能中断函数
void enable_irq(unsigned int irq);
参数:irq,要使能的中断对应的编号;
说明:
5.禁止中断函数
void disable_irq_nosync(unsigned int irq);
void disable_irq(unsigned int irq);
参数:
irq,要禁止的中断对应的编号。
注意:在中断服务程序中不能使用 disable_irq 这个函数,否则内核崩溃,可以使用 disable_irq_nosync。
disable_irq:函数调用后,函数不会马上返回,而等待中断程序执行完成才返回,在中断调用会导致死锁。
disable_irq_nosync:调用后,函数马上返回
代码例子(编写按键中断驱动程序):
但是,这个东西有个缺点,就是按键会抖动,所以你按下一次,它会识别到很多次。
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
//按键数量
#define BTN_SIZE 4键缓冲区,'0'表示没有按键,'1'表示按下了
static char keybuf[] = {"0000"};
/*把一个当成一个对象来看待,方便编程,定义一个描述按键结构*/
struct button_desc {
int gpio; //存放io口编号
int number; //存放按键编号,根据自己需要设计,
char *name; //按键名字,随便,但是要有意义
};
/* 定义4个按键的信息 */
static struct button_desc buttons[] = {
{ EXYNOS4_GPX3(2), 0, "KEY0" },
{ EXYNOS4_GPX3(3), 1, "KEY1" },
{ EXYNOS4_GPX3(4), 2, "KEY2" },
{ EXYNOS4_GPX3(5), 3, "KEY3" },
};
//中断服务函数
irqreturn_t key_isr(int irq, void* dev)
{
//存放按键状态
int dn = 0;
int index = 0;
//这里进行还原原来的类型
struct button_desc *bdata = (struct button_desc *)dev;
//把读取到的结果取逻辑非,因为程序设计使用正逻辑。'1'表示按下。
dn = !gpio_get_value(bdata->gpio);
//取得当前中断对应 的按键编号
index = bdata->number;
//把按键状态更新到对应的按缓冲中
keybuf[index] = dn + '0';
//输出按键提示
printk("%s %s\r\n", bdata->name, dn ? "down" : "up");
return IRQ_HANDLED;
}
static ssize_t tiny4412_read (struct file *flp, char __user *buff,size_t count, loff_t * off)
{
int ret = 0;
//用户传递0,直接返回
if(!count) {
return 0;
}
//修正参数
if(count > BTN_SIZE ) {
count = BTN_SIZE;
}
//复制数据到用户空间
ret = copy_to_user(buff, keybuf, count);
if(ret) {
printk("error:copy_to_user\r\n");
return -EFAULT;
}
return count;
}
static const struct file_operations dev_fops = {
.read = tiny4412_read,
.owner = THIS_MODULE,
};
#define LEDS_MAJOR 255 //255
#define DEVICE_NAME "mybtn"
static struct miscdevice misc = {
.minor = LEDS_MAJOR, //次设备号
.name = DEVICE_NAME,//设备名
.fops = &dev_fops, //文件操作方法
};
static int __init btn_init(void)
{
int ret;
int irq;
int i;
int flags;
flags = IRQ_TYPE_EDGE_BOTH; //设置为双边触发
for ( i = 0; i < 4 ; i++ ) {
//得到中断号
irq = gpio_to_irq(buttons[i].gpio); //keyX
//注册中断
ret = request_irq(irq, key_isr, flags, buttons[i].name, (void*)&buttons[i]);
if(ret < 0) {
break;
}
}
//如果不是全部成功,则反向注销已经注册的中断
if(ret < 0) {
for ( --i; i; i-- ) {
irq = gpio_to_irq(buttons[i].gpio); //keyX
disable_irq(irq);
free_irq(irq, (void*)&buttons[i]);
}
return ret;
}
//注册杂项设备
ret = misc_register(&misc); //注册混杂设备
return ret;
}
static void __exit btn_exit(void)
{
int i = 0;
int irq;
//注销中断
for (i = 0; i < 4; i++) {
irq = gpio_to_irq(buttons[i].gpio); //keyX
disable_irq(irq);
free_irq(irq, (void*)&buttons[i]);
}
//注销杂项设备
misc_deregister(&misc);
printk(KERN_EMERG "Goodbye,cruel world!, priority = 0\n");
}
module_init(btn_init);
module_exit(btn_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jian");
MODULE_DESCRIPTION("button");