中断之独立按键

文章目录

一、中断的基础知识

1.什么是中断

中断是指 CPU 在执行程序的过程中,出现突发事件去处理,CPU 需要停止当前程序的执行,转去处理突发事件,处理完成之后再返回原程序部分。

2.硬件中断和软件中断

硬件中断一般指外设发出的中断请求以及内部硬件产生的中断(计算溢出,除数为 0,掉电等)
软件中断,典型的是中断处理程序的下半部操作.

3.中断优先级

系统根据中断事件的重要性和紧迫程度,将中断源分为若干个等级,优先级高的先执行。

二、按键的硬件连接

底板上有 5 个在一起的独立按键,以 HOME 和 BACK 为例,先来看一下原理图,如下图所示。
中断之独立按键
通过硬件连接可知,默认状态给中断的 IO 是高电平。所以可以判断后面对中断触发方式要为下降沿触发。

两个网络 UART_RING 和 SIM_DET。
对应的,对应的 GPIO 为“EXYNOS4_GPX1(1)”,中断号为“XEINT9
对应的 GPIO 为“EXYNOS4_GPX1(2)”中断号为“XEINT10”

中断之独立按键
可以看到将管脚配置为外部中断模式需要设置为 0xF。
最后再带大家看一个部分,在 Datasheet 的 56 章,找到 Debouncing Filter,这是4412 对于按键自动防抖的部分,如下图所示,可以看到有自带的防抖。

三、中断相关函数

1.request_irq

Linux 中的中断在使用前,都需要申请。中断申请函数是“request_irq”,在头文件“include/linux/interrupt.h”中,如下图所示。
中断之独立按键

中断申请函数 request_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags,const char *name, void *dev)有下面几个参数。
参数 unsigned int irq:irq 是中断号
参数 irq_handler_t handler:handler 是向系统登记的处理函数
参数 unsigned long flags:irqflags 是触发标志位
参数 const char *name:devname 是中断名称,可以通过注册之后可以通过“cat/proc/interrupts”查看
参数 void *dev:dev_id 是设备

2. free_irq

和上面中断申请函数对应的就是中断释放函数 free_irq,卸载驱动的时候需要调用,如下图所示,也是在头文件“include/linux/interrupt.h”中。
中断之独立按键

中断释放函数 extern void free_irq(unsigned int, void *);的参数如下。
参数 1:irq 是中断号
参数 2:dev_id 是设备

3.irqreturn_t

产生中断之后,会调用中断处理函数 irqreturn_t,这个函数也是在头文件
“include/linux/interrupt.h”中如下图所示。
中断之独立按键

该函数为 extern irqreturn_t no_action(int cpl, void *dev_id);
中断函数类型为 irqreturn_t
参数 int cpl:中断号
参数 void *dev_id:设备

4.中断号

在初始化文件“drivers/gpio/gpio-exynos4.c”中,如下图所示

vim drivers/gpio/gpio-exynos4.c

中断之独立按键
选取的两个中断管脚都属于“GPX1”,所以如上图所示,中断编号的基础数值是IRQ_EINT(8),那么 GPX1CON[0]和 GPX1CON[1]则对应着中断号 IRQ_EINT(9)和IRQ_EINT(10)。

如果想调用其他外部中断也可以通过这种方式来查阅中断号,不过前提是这个中断没有被占用。

四、实验操作

外部中断的实际应用一般是集成在一些类似声卡、显卡、其他总线设备中,实际的应用也就是调用前面提到的头文件和函数,在驱动初始化的时候申请中断,然后针对具体的驱动写中断函数处理函数。例如声卡中调音量的按键,就是将按键部分的代码集成到声卡中,检测到按键就可以对应的调低调高音量。

这里注册一个简单的字符驱动,这样的话会便于大家的理解。要完成的功能就是按键中断产生之后打印数据。

1.注册设备

如下图所以,在平台文件“arch/arm/mach-exynos/mach-itop4412.c”中添加注册设备的代码。

vim arch/arm/mach-exynos/mach-itop4412.c

中断之独立按键
为了简单,可以也可以不定义宏变量,强制注册设备。(没有编写#ifdef)

2.添加设备调用的代码

还是在上一个文件里
中断之独立按键

3.menuconfig配置

然后打开 menuconfig 配置文件,将使用这两个中断的驱动卸载掉。
Device Drivers —>
Input device support —>
Keyboards —>
取消 GPIO Buttons —>
中断之独立按键
重新设置之后,将内核重新编译,并将生成的二进制 zImage 文件烧写到开发板中替换原来的内核。

4.编写驱动

1.驱动固定头文件

如下图所示,头文件部分,以后写 4412 的驱动可以将这些头文件一股脑的添加到代码前面。

/* 以后写驱动可以将头文件一股脑的加载到代码前面 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <mach/regs-gpio.h>
#include <asm/io.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>

/* 中断函数头文件 */
#include <linux/interrupt.h>
#include <linux/irq.h>

2.应用函数irq_probe

首先对中断 IO 进行检测,是否被占用了,处理方式一般就是申请 IO,看是否成功,申请成功之后就将 GPIO 配置为上拉模式,然后调用 gpio_free 将其释放。

其中用到了参数“IRQ_TYPE_EDGE_FALLING”,这个代表下降沿触发,这个宏定义在头文件“include/linux/irq.h”中,如下图所示。

中断之独立按键

3.Makefile

省略,和前面的都是一样的

4.itop4412_irq.c

/* 以后写驱动可以将头文件一股脑的加载到代码前面 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <mach/regs-gpio.h>
#include <asm/io.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>

/* 中断函数头文件 */
#include <linux/interrupt.h>
#include <linux/irq.h>

#define IRQ_DEBUG

#ifndef IRQ_DEBUG
#define DPRINTK(x...) printk("IRQ_CTL DEBUG:" x)
#else
#define DPRINTK(x...)
#endif

#define DRIVER_NAME "irq_test"

MODULE_LICENSE("Dual BSD/GPL");


/* 定义中断处理函数 */
static irqreturn_t eint9_interrupt(int irq,void *dev_id)
{
	printk("%s(%d)\n",__FUNCTION__,__LINE__);
	return IRQ_HANDLED;
}

static irqreturn_t eint10_interrupt(int irq,void *dev_id)
{
	printk("%s(%d)\n",__FUNCTION__,__LINE__);
	/*上面使用了一句比较特殊的代码“printk("%s(%d)\n", __FUNCTION__,__LINE__);
	”这句代码在调试的过程中非常有用,就是打印当前所在的函数以及对应的行,
	 在后面测试的时候就可以看到其效果。*/
	return IRQ_HANDLED;
}

static int irq_probe(struct platform_device *pdev)
{
	int ret;
	char *banner = "irq_test Initialize!\n";
	
	printk(banner);
	
	/* 第一个IO */
	ret = gpio_request(EXYNOS4_GPX1(1),"EINT9");
	if(ret)
	{
		printk("%s:request GPIO %d for EINT9 failed,ret = %d!\n",DRIVER_NAME,EXYNOS4_GPX1(1),ret);
		return ret;
	}
	
	s3c_gpio_cfgpin(EXYNOS4_GPX1(1),S3C_GPIO_SFN(0xF));
	s3c_gpio_setpull(EXYNOS4_GPX1(1),S3C_GPIO_PULL_UP);
	gpio_free(EXYNOS4_GPX1(1));
	
	/* 第二个IO*/
	ret = gpio_request(EXYNOS4_GPX1(2),"EINT10");
	if(ret)
	{
		printk("%s:request GPIO %d for EINT10 failed,ret = %d!\n",DRIVER_NAME,EXYNOS4_GPX1(2),ret);
		return ret;
	}
	
	s3c_gpio_cfgpin(EXYNOS4_GPX1(2),S3C_GPIO_SFN(0xF));
	s3c_gpio_setpull(EXYNOS4_GPX1(2),S3C_GPIO_PULL_UP);
	gpio_free(EXYNOS4_GPX1(2));
	
	/* 中断 */
    ret = request_irq(IRQ_EINT(9),eint9_interrupt,IRQ_TYPE_EDGE_FALLING,"eint9",pdev);
	if(ret < 0)
	{
		printk("Request IRQ %d failed,%d\n",IRQ_EINT(9),ret);
		goto exit;
	}
	
	ret = request_irq(IRQ_EINT(10),eint10_interrupt,IRQ_TYPE_EDGE_FALLING,"eint10",pdev);
	if(ret < 0)
	{
		printk("Request IRQ %d failed,%d\n",IRQ_EINT(10),ret);
		goto exit;
	}
	return 0;
exit:
	return ret;
}


static int irq_remove(struct platform_device *pdev)
{
	free_irq(IRQ_EINT(9),pdev); // 释放中断
	free_irq(IRQ_EINT(10),pdev); // 释放中断
	
	return 0;
}

static int irq_suspend(struct platform_device *pdev,pm_message_t state)
{
	DPRINTK("irq suspend:power off!\n");
	return 0;
}

static int irq_resume(struct platform_device *pdev)
{
	DPRINTK("irq resume:power on!\n");
	return 0;
}

/* 驱动模块的入口函数和出口函数 */
static struct platform_driver irq_driver = {
	.probe = irq_probe,
	.remove = irq_remove,
	.suspend = irq_suspend,
	.resume = irq_resume,
	.driver = {
			.name = DRIVER_NAME,
			.owner = THIS_MODULE,
	},
};



static void __exit irq_test_exit(void)
{
	platform_driver_unregister(&irq_driver); // 卸载驱动
}

static int __init irq_test_init(void)
{
	return platform_driver_register(&irq_driver); // 注册驱动
}

module_init(irq_test_init);
module_exit(irq_test_exit);

5.编译

新建文件夹

mkdir irq_test

中断之独立按键
编译;
中断之独立按键

6.开发板运行

1.加载驱动

insmod itop4412_irq.ko

中断之独立按键

2.查看申请的中断

cat /proc/interrupts

中断之独立按键

3.测试

按几下HOME和BACK按键,会出现类似下面的打印信息
中断之独立按键
最后再使用命令“cat /proc/interrupts”查看申请的中断,如下图所示,已经检测到这两个中断分别触发了几次。
中断之独立按键

上一篇:【正点原子Linux连载】第五十一章 Linux中断实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0


下一篇:c – 文件存在时fopen失败