本文转自迅为: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. 2 Orchard汉化资源包的使用

    Orchard安装完毕之后我们就可以在后台尝试做一些基本的操作感受下Orchard提供的一些功能,比如添加一个页面.菜单.文章什么的.也可以试着新建一些部件.布局之类的感受下.个人建议摆弄一下了解下就 ...

  2. 【C#进阶系列】04 类型基础

    关于System.Object 所有类型都从System.Object派生而来. System.Object的公共方法中ToString()一般是返回对象的类型的全名,只有Int32这些类型将其重写后 ...

  3. AssemblyInfo文件

    程序生成版本信息 AssemblyInfo.cs主要用来设定生成的有关程序集的常规信息dll文件的一些参数 请看以下具体说明: //备注:  [assembly:AssemblyDescription ...

  4. Run python as a daemon process

    I am using `&`: why isn't the process running in the background?     No problem. We won't show y ...

  5. 变废为宝,将Discuz废弃的cache机制引入到memory体系中

    Discuz的source/class/cache目录,代表着相应的缓存机制,但实际上废弃很多年了. Discuz用Memory代表了缓存,里面内置了memcache等多种缓存驱动. 但很多人的开发环 ...

  6. UGUI之Toggle使用

    Toggle对象是一个开关.一般用于单选,可以用Toggle制作背包的选项卡 在场景中创建Toggle按钮.看看他有Toggle组件

  7. tomcat filewatchdog but has failed to stop it原因以及解决方法

    停止tomcat,有些时候会报The web application [/XXX] appears to have started a thread named [FileWatchdog] but ...

  8. 初识你---------Swift【下篇】

    Swift中的结构体 Swift的结构体对比OC来说,可以添加初始化方法.可以遵守代理协议等,同时:Swift的Bool类型的变量也是一个结构体,所以只能选择true和false. Swift中声明结 ...

  9. Ansible用于网络设备管理 part 0 安装和登录网络设备

    真实在是累了,但是又怕第二天早上又忘掉,在这先大概写写. 安装: http://www.tecmint.com/install-and-configure-ansible-automation-too ...

  10. win10应用部署到手机出现问题Exception from HRESULT: 0x80073CFD

    今天把应用部署到手机上时,出现了这样的问题 Exception from HRESULT: 0x80073CFD 具体错误是: Error Error : DEP0001 : Unexpected E ...