6

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

 2 years ago
source link: https://blog.51cto.com/u_11822586/5235391
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

原创

DS小龙哥 2022-04-21 09:09:22 博主文章分类:Linux驱动及系统编程 ©著作权

文章标签 linux 文章分类 嵌入式Linux 嵌入式 阅读数174

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/interrupts
dev_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 按键原理图

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

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

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.5
all:
	make -C $(KER_DRI) M=`pwd` modules
	cp *.ko /home/wbyq/work/rootfs/code -f
	make -C $(KER_DRI) M=`pwd` modules clean
obj-m += interrupt_key.o
  • 收藏
  • 评论
  • 分享
  • 举报

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK