本文转自迅为:www.topeetboard.com

大家好,今天我们来学习一下 linux 中断处理驱动的编写,本节我们实现的功能是通过开发板上的按键来控制 led 发光二极管,在之前的章节我们学习了 led 驱动的编写,在掌握了 led 驱动的编写以后,如果要实现按键控制 led 的功能,大家可能会想到可以在 led 的驱动里面使用轮询的方式一直查询按键的状态,如果有按键按下就设置 led 的状态。通过这种方式可以实现按键控制 led 的功能, 但是通过这样的方式有一个缺点就是led 驱动会占用 cpu,这样 cpu 的利用率就大大降低了,所以我们可以通过中断的方式来实现。这样 cpu 就可以去做其他的事情了,当有按键中断触发的时候才会去设置 led。
ARM 架构 linux 内核中,有 5 种常见的异常,其中中断异常是其一,Linux 内核将所有中断统一编号,使用一个 irq_desc 结构体来描述这些中断,里面记录了中断名称、中断状态、中断标记、并提供了中断的底层硬件访问函数(如:清除、屏蔽、使能中断) ,提供了这个中断的处理函数入口,通过它还可以调用用户注册的的中断处理函数。linux 内核的中断体系已经很完善了,驱动工程师需要做的就是调用 request_irq 函数向内核注册中断处理函数,下面我们来看看 request_irq 函数的定义:
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
第一个参数 irq:中断号,与平台架构相关;
第二个参数 handler:用户中断处理函数;
第三个参数 flags:中断标记
第四个参数 devname:中断名字,可以通过 cat /proc/interrupts 查看;
第五个参数 dev_id:在 free_irq 中有用,也用做区分中断处理函数;

有注册就得对应着有注销,驱动的注销函数是 free_irq,其定义如下:
void free_irq(unsigned int irq, void *dev_id)
第一个参数 irq:中断号,与 request_irq 中的 irq 一致,用于定位 action 链表;
第二个参数 dev_id:用于在 action 链表中找到要卸载的表项;同一个中断的不同中断处理函数必须使用不通
的 dev_id 来区分,这就要求在注册中断共享时参数 dev_id 必须唯一。
下面我们来看一下中断按键的驱动,代码如下:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.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>
#define IRQ_DEBUG
#ifdef IRQ_DEBUG
#define DPRINTK(x...) printk("IRQ_CTL DEBUG:" x)
#else
#define DPRINTK(x...)
#endif
#define DRIVER_NAME "irq_test"
static int led_gpios[] = {
EXYNOS4_GPL2(0),
EXYNOS4_GPK1(1),
};
#define LED_NUM ARRAY_SIZE(led_gpios)
#if 1
static irqreturn_t eint9_interrupt(int irq, void *dev_id) {

printk("%s(%d)\n", __FUNCTION__, __LINE__);
if(gpio_get_value(led_gpios[0]))
gpio_set_value(led_gpios[0], 0);
else
gpio_set_value(led_gpios[0], 1);
return IRQ_HANDLED;
}
static irqreturn_t eint10_interrupt(int irq, void *dev_id) {
printk("%s(%d)\n", __FUNCTION__, __LINE__);
if(gpio_get_value(led_gpios[1]))
gpio_set_value(led_gpios[1], 0);
else
gpio_set_value(led_gpios[1], 1);
return IRQ_HANDLED;

}
#endif
static int irq_probe(struct platform_device *pdev)
{
int ret, i;
char *banner = "irq_test Initialize\n";
printk(banner);
for(i=0; i<LED_NUM; i++)
{
ret = gpio_request(led_gpios[i], "LED");
if (ret) {
printk("%s: request GPIO %d for LED failed, ret = %d\n", DRIVER_NAME,
led_gpios[i], ret);
return ret;
}
s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
gpio_set_value(led_gpios[i], 0);
}

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));
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));
#if 1
ret = request_irq(IRQ_EINT(9), eint9_interrupt,
IRQ_TYPE_EDGE_FALLING /*IRQF_TRIGGER_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 /*IRQF_TRIGGER_FALLING*/, "eint10", pdev);
if (ret < 0) {
printk("Request IRQ %d failed, %d\n", IRQ_EINT(10), ret);
goto exit;
}
#endif
return 0;
exit:
return ret;
}

static int irq_remove (struct platform_device *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);

MODULE_LICENSE("Dual BSD/GPL");
首先我们来看下这个驱动的入口函数 irq_test_init,定义如下:
static int __init irq_test_init(void)
{
return platform_driver_register(&irq_driver);
}
该函数想内核注册一个 irq_driver 类型的设备,irq_driver 定义如下:
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,
},
};
从这个结构里我们可以看到驱动的探测函数是 irq_probe,注销函数是 irq_remove,休眠调用的函数是
irq_suspend, 唤醒调用的函数是 irq_resume, 驱动的名字是 DRIVER_NAME, DRIVER_NAME 是个宏定义,
如下:
#define DRIVER_NAME "irq_test"

接下来我们看看驱动的探测函数 irq_probe,这个函数也是这个驱动里面最主要的函数,这个函数一开始掉欧
勇 printk 打印一条信息,接着是一个 for 循环,初始化 led 的 GPIO,如下:
for(i=0; i<LED_NUM; i++)
{
ret = gpio_request(led_gpios[i], "LED");
if (ret) {
printk("%s: request GPIO %d for LED failed, ret = %d\n", DRIVER_NAME,
led_gpios[i], ret);
return ret;
}
s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
gpio_set_value(led_gpios[i], 0);
}
然后是初始化两个按键的 gpio,设置为中断模式,如下所示:
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));
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));
接着调用中断注册函数 request_irq 向内核注册中断处理函数,如下:
ret = request_irq(IRQ_EINT(9), eint9_interrupt,
IRQ_TYPE_EDGE_FALLING /*IRQF_TRIGGER_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 /*IRQF_TRIGGER_FALLING*/, "eint10", pdev);
if (ret < 0) {
printk("Request IRQ %d failed, %d\n", IRQ_EINT(10), ret);
goto exit;
}
上面的中断注册函数分别注册了两个中断处理函数 eint9_interrupt 和 eint10_interrupt。
eint9_interrupt 的定义如下:
static irqreturn_t eint9_interrupt(int irq, void *dev_id) {
printk("%s(%d)\n", __FUNCTION__, __LINE__);
if(gpio_get_value(led_gpios[0]))
gpio_set_value(led_gpios[0], 0);
else
gpio_set_value(led_gpios[0], 1);
return IRQ_HANDLED;
}
这个函数首先会打印一句信息,然后是获取 led 的状态,把状态取反。
eint10_interrupt 的定义如下:
static irqreturn_t eint10_interrupt(int irq, void *dev_id) {
printk("%s(%d)\n", __FUNCTION__, __LINE__);
if(gpio_get_value(led_gpios[1]))
gpio_set_value(led_gpios[1], 0);
else
gpio_set_value(led_gpios[1], 1);
return IRQ_HANDLED;
}
这个函数和 eint9_interrupt 的功能类似。
其他的函数就和前面讲的 led 驱动里面的函数基本一样了, 唯一的区别是我们在注销函数 irq_remove 里面使
用了 free_irq 函数来注销之前初测的中断处理函数。
把这个驱动放到内核的 driver/char 目录下面,如下图所示:

然后打开 driver/char 目录下面的 Makefile,添加“obj-y += itop4412_irq.o” ,如下图所示:

然后打开 arch/arm/mach-exynos/mack-itop4412.c 文件,找到“struct platform_device
s3c_device_buzzer_ctl” ,如下图所示:

然后在它的下面添加下面的信息:
struct platform_device s3c_device_irq_test = {
.name = "irq_test",
.id = -1,
};
如下图所示:

然后找到“&s3c_device_buzzer_ctl,”这一行,如下图所示:

在这一行的下面添加“&s3c_device_irq_test,” ,如下图所示:

然后保存并退出。因为本章实验使用到了 led 和按键,所以我们要把内核里面的 led 驱动和按键的驱动去掉,
在内核目录下使用 make menuconfig 命令打开内核配置界面,如下图所示:


进入到“Device Drivers” ,如下图所示:

然后进入到“Input device support”界面,如下图所示:

然后进入到“Keyboards”界面,如下图所示:

然后取消掉“GPIO Buttons” ,如下图所示:

然后返回到“Device Drivers”界面,如下图所示:

然后选择“Character devices” ,进入 Character devices 界面,如下图所示:

然后取消“Enable LEDS config” ,如下图所示:

然后保存并退出内核配置界面,使用 make 命令编译内核,如下图所示:

编译完成后,如下图所示:

然后把编译生成的 zImage 烧写到 iTOP-4412 开发板上,烧写完成后启动开发板,系统起来以后,我们可以
按开发板上的 BACK 或 HOME 按键,来看下运行结果。
当我们按下 BACK 按键时,可以看到 led 会点亮,在次按下,led 就会熄灭,同事串口会打印信息,如下图所
示:

我们按下 HOME 键,可以看到另外一个 led 会点亮,再次按下,led 就会熄灭,同事串口会打印信息,如下
图所示:

至此,linux 下中断驱动我们就已经完成了。

迅为iTOP-4412嵌入式开发板实现中断驱动例程的更多相关文章

  1. ARM开发板实现双系统引导的一种方法——基于迅为iTOP-4412开发板

    前言 本文所用的uboot代码为迅为官方提供,开发板是迅为iTOP-4412开发板.本文如有错误,欢迎指正. 首先,我们确定一下系统启动的流程:首先启动uboot,uboot启动内核并挂载rootfs ...

  2. 【4412嵌入式开发板学习笔记】认识uboot

    转自迅为讨论群:http://www.topeetboard.com 重要说明:这份笔记不是4412开发配套的,是我在网上看视频的时候下载上课老师的笔记后修改的.所以我试了一下笔记上的uboot命令, ...

  3. 迅为i.MX6Q嵌入式开发板

    工业级核心板:核心板10层高速PCB设计,充分保证电磁兼容. 01. 处理器:开发板默认是四核商业扩展级芯片,可根据用户需求更换单核.双核.工业级.汽车级处理器,批量更省成本. 02. 扩展引脚:32 ...

  4. 嵌入式开发板iTOP4412学习开发板

    网站:http://www.topeetboard.com 淘宝:https://item.taobao.com/item.htm?_u=okcahs0f42a&id=38712193806 ...

  5. 迅为-ARM嵌入式开发一体化工业9.7寸屏幕 平板式智能触控屏

    产品名称:迅为9.7寸IPS高清屏幕 适用于:[iTOP-4412精英版][iTOP-4412全能版][iTOP-4418开发板][迅为-iMX6开发板] 分辨率:1024*768 触摸屏类型:电容屏 ...

  6. [嵌入式开发入门]4412开发板从零建立Linux最小系统

    本文转自iTOP-4412开发板实战教程书籍 http://www.topeetboard.com iTOP-4412开发板不仅可以运行Android,还可以运行简单的Linux最小文件系统. 最小L ...

  7. ARM嵌入式开发板

    iTOP-4412 ARM嵌入式开发板----主要特点 iTOP-4412开发平台是北京迅为电子研发设计的嵌入式开发板平台,核心板配备64位双通道2GB DDR3,16GBEMMC存储,三星原厂S5M ...

  8. 嵌入式开发中常见3个的C语言技巧

    Hey,大家好!我是CrazyCatJack.今天我来说几个在嵌入式开发中常用的C语言技巧吧.也许你曾经用过,也许你只是见到过但是没有深入理解.那么今天好好补充下吧^_^ 1.指向函数的指针 指针不光 ...

  9. ARM-linux嵌入式开发平台搭建1

    初学嵌入式开发,由于是自学,走了很多弯路,现总结一下嵌入式ARM-LINUX开发环境搭建步骤: 1.安装linux系统,由于初学,我选择fedora 14.安装的具体步骤就不详细说了. 2.安装NFS ...

随机推荐

  1. Oracle基本操作汇总

    --10g 新增的表空间类型:大文件 (Bigfile) 表空间.--10g 数据库在创建的时候,会指定默认的表空间类型.如果不特殊指定的话,默认为 SMALLFILE 类型的表空间.SELECT * ...

  2. PHP验证邮箱地址代码

    PHP验证邮箱代码: function isEmail($email) { return strlen($email) > 6 && preg_match("/^[\w ...

  3. 重新想象 Windows 8 Store Apps (53) - 绑定: 与 ObservableCollection CollectionViewSource VirtualizedFilesVector VirtualizedItemsVector 绑定

    [源码下载] 重新想象 Windows 8 Store Apps (53) - 绑定: 与 ObservableCollection CollectionViewSource VirtualizedF ...

  4. ahjesus用forever管理nodejs服务

    全局安装forever npm install -g forever 查看帮助 forever -h 查看安装位置 whereis forever 编写自己的sh文件 forever -p web文件 ...

  5. Android应用开发基础之六:页面跳转和数据传递

    创建第二个Activity 需要在清单文件中为其配置一个activity标签 标签中如果带有这个子节点,则会在系统中多创建一个快捷图标 <intent-filter> <action ...

  6. 跟踪js文件作为iframe页面不起作用时(淘宝天猫)

    跟踪文件 (function(win, doc) { var s = doc.createElement("script"), h = doc.getElementsByTagNa ...

  7. [TypeScript] TypeScript对象转JSON字符串范例

    [TypeScript] TypeScript对象转JSON字符串范例 Playground http://tinyurl.com/njbrnrv Samples class DataTable { ...

  8. IOS网络编程之请求内容

    资料均来自互联网,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任. 人魔七七:http://www.cnblogs.com/qiqibo/ 一个http请求只要由三 ...

  9. __proto__

    proto 以前要访问原型, 必须使用构造函数来实现. 无法直接使用实例对象来访问原型. 火狐最早引入属性 __proto__ 表示使用实例对象引用原型. 但是早期是非标准的. 通过该属性可以允许使用 ...

  10. 删除src值为空的img标签

    今天刚刚完成了一个官网的前后台整站建设,虽然不是很复杂,但感觉获益良多.由于涉及到一点后台问题,所以期间遇到了不少问题.学到的东西,得作个总结.今天先讲讲img的路径问题.由于现在很多网站喜欢全屏大图 ...