写点什么

Linux 驱动开发 - 外部中断的注册使用 (按键为例)

作者:DS小龙哥
  • 2022 年 4 月 19 日
  • 本文字数:2107 字

    阅读完需:约 7 分钟

1. 外部中断介绍

前面有篇文章使用杂项设备完成了按键驱动的编写,实现了按键轮询检测,通过 read 函数向应用层传递按键值,这篇文章使用按键为例,介绍 Linux 内核里中断的注册方法,使用中断的方式检测按键是否按下,中断在单片机、设备驱动开发里使用的都非常多,可以更加实时的检测到按键触发的情况。


Linux 内核提供了中断的注册接口:


(1)注册中断


头文件       include\linux\interrupt.h
定义文件 include\linux\interrupt.h
函数原型 int request_irq(unsigned int irq,/*做实参传递给中断服务函数第1个参数*/ irq_handler_t handler, /*中断服务函数指针*/ unsigned long flags, const char *name, void *dev_id); /*做实参传递给中断服务函数第2个参数*/
函数功能 向内核注册一个中断服务函数;当发生中断号为 irq 的中断时候,会执行 handler 指针函数。
函数参数 irq:中断编号(每个中断源有惟一的编号)。
handler:中断服务函数指针。 原型 typedef irqreturn_t (*irq_handler_t)(int, void *)。flag:中断的标志,用来描述本中断的基本特征的。 有固定的值,由中断源的特征决定; 比如外中断有:上升沿,下降沿触发中断这类标志。name:中断名字, 注册后会出现cat /proc/interruptsdev_id: 这个参数是传递给中断服务函数。 对共享中断来说, 这个参数一定有要; 当注销共享中断中的其中一个时, 用这个来标识要注销哪一个。 对于有惟一入口的中断,可以传递 NULL; 但是一般来说都会传递一个有意义指针,在中断程序中使用, 以方便编程。
返回值 0 表示成功-EINVAL (无效参数22)表示中断号无效。-EBUSY (设备或者资源忙16)表示中断已经被占用。
复制代码


(2)注销中断


void free_irq(unsigned int irq,void * dev_id)irq: 要注销的中断号dev_id:其实就是注册时候使用的dev参数,在共享中断必不可少,不能传递NULL。注意:为了防止在注销时同时发生中断,调用时候,先禁止中断。
复制代码


(3)中断开启与关闭


禁止中断void disable_irq_nosync(unsigned int irq);void disable_irq(unsigned int irq);参数:irq,要禁止的中断对应的编号。 注意:在中断服务程序中不能使用 disable_irq 这个函数,否则内核崩溃,可以使用 disable_irq_nosync。disable_irq:函数调用后,函数不会马上返回,而等待中断程序执行完成才返回,在中断调用会导致死锁。disable_irq_nosync:调用后,函数马上返回。
使能中断void enable_irq(unsigned int irq);参数:irq,要使能的中断对应的编号
复制代码


(4)获取 irq 中断号


int gpio_to_irq(unsigned gpio);
复制代码

2. 外部中断驱动编写

2.1 按键原理图


2.2 驱动示例代码

insmod 安装驱动之后就直接注册按键中断,没有注册字符设备框架,当按键按下之后,直接在驱动层通过printk打印数据提示到终端。


#include <linux/kernel.h>#include <linux/module.h>#include <linux/interrupt.h>#include <linux/irq.h>#include <linux/gpio.h>#include <mach/gpio.h>#include <plat/gpio-cfg.h>
/*存放按键的信息*/struct m_key_info{ int gpio; char name[50]; int val; int irq;};
struct m_key_info key_info[]={ {EXYNOS4_GPX3(2),"key_irq_1",0x01}, {EXYNOS4_GPX3(3),"key_irq_2",0x02}, {EXYNOS4_GPX3(4),"key_irq_3",0x03}, {EXYNOS4_GPX3(5),"key_irq_4",0x04},};

/*中断服务函数*/static irqreturn_t key_irq_handler(int irq, void *dev){ struct m_key_info *p=(struct m_key_info*)dev; if(gpio_get_value(p->gpio)==0) //判断按键是否按下 { printk("按键值:%#x\n",p->val); } else { printk("按键值:%#x\n",p->val|0x80); } return IRQ_HANDLED;}
static int __init tiny4412_interrupt_drv_init(void){ int i; for(i=0;i<sizeof(key_info)/sizeof(key_info[0]);i++) { /*1. 获取中断号*/ key_info[i].irq=gpio_to_irq(key_info[i].gpio);
/*2. 注册中断*/ if(request_irq(key_info[i].irq,key_irq_handler,IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,key_info[i].name,&key_info[i])) { printk("中断号%d注册失败:%s\n",key_info[i].irq,key_info[i].name); } } printk("按键中断 驱动注册-安装成功.\n"); return 0;}
static void __exit tiny4412_interrupt_drv_exit(void){ /*注销中断*/ int i=0; for(i=0;i<sizeof(key_info)/sizeof(key_info[0]);i++) { free_irq(key_info[i].irq,&key_info[i]); } printk("按键中断 驱动注销成功.\n");}
/*驱动入口*/module_init(tiny4412_interrupt_drv_init);/*驱动出口*/module_exit(tiny4412_interrupt_drv_exit);/*许可证*/MODULE_LICENSE("GPL");
复制代码

2.3 makefile 代码

KER_DRI=/home/wbyq/work/linux-3.5/linux-3.5all:  make -C $(KER_DRI) M=`pwd` modules  cp *.ko /home/wbyq/work/rootfs/code -f  make -C $(KER_DRI) M=`pwd` modules cleanobj-m += interrupt_key.o
复制代码


发布于: 刚刚阅读数: 2
用户头像

DS小龙哥

关注

之所以觉得累,是因为说的比做的多。 2022.01.06 加入

熟悉C/C++、51单片机、STM32、Linux应用开发、Linux驱动开发、音视频开发、QT开发. 目前已经完成的项目涉及音视频、物联网、智能家居、工业控制领域

评论

发布
暂无评论
Linux驱动开发-外部中断的注册使用(按键为例)_4月月更_DS小龙哥_InfoQ写作平台