l中断的实现
转自:http://blog.chinaunix.net/uid-25014876-id-90740.html
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
一、什么是中断
中断分两种:
1)中断,又叫外部中断或异步中断,它的产生是由于外设向处理器发出中断请求。其中外部中断也有两种,这是由配置寄存器设定的:普通中断请求(IRQ)和快速中断请求(FIQ)。一般地,linux下很少使用快速中断请求。
2)异常,又叫内部中断或同步中断,它的产生是由于处理器执行指令出错。
在以下的内容我是要介绍由于外部设备产生的中断。
这里我还有两个名词要说清楚
1)中断请求线:在后面也叫中断号,每个中断都会通过一个唯一的数值来标识,而这个值就称做中断请求线
2)在2440芯片中,位。具体可以查看芯片手册。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
二、什么是中断处理函数
在相应一个中断是,内核会执行该信号对应的一个函数,该函数就叫做该中断对应的中断处理函数。一般来说,中断的优先级是最高的,一但接收到中断,内核就会调用对应的中断处理函数。
中断处理函数运行在中断上下文中。中断上下文与内核上下文有一点区别:
内核上下文是指应用层调用系统调用陷入内核执行,内核代表陷入的进程执行操作。函数中可以通过current查看当前进程(即应用层的进程)的信息,并且可以睡眠。
中断上下文中,不能通过current查看调用它的应用层进程的信息,同时,处于中断上下文时,不能睡眠。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
一、从硬件角度看中断
中断的产生到处理器获得中断这段过程中,还要通过中断处理器来筛选信号。
先温习一下S3C2440芯片手册的知识:中断是如何产生的,中断处理器本身如何处理中断。先看一下一幅经典的图,这是介绍中断控制器的工作流程:
从硬件上的分类,有两种不同的中断类型:
1)自己占有SORCPND寄存器的一位(without sub-register)。
2)几个中断共同享用SRCPND寄存器的一位(with sub-register)。
其实两种都差不多,只是多了两步的检测。我以自己占用一位的中断来举例,如EINT1,在我的开发板,EINT1上接了一个按键。
1)当我按下按键产生电平变化,传到S3C2440的中断控制器上(即将要进入上面图的流程图)。
2)首先,信号要经过寄存器SRCPND,SRCPND是用来配置当前的处理器要接收什么中断,如果该寄存器配置成接收EINT1中断(对应位置一),则允许继续下一步。
3)然后,信号经过寄存器MASK,这是用来设置当前系统需要屏蔽的中断。注意,这里的屏蔽跟上一个寄存器的不接收中断是不一样的。这里的屏蔽是指,中断是接受了,但是由于某种原因,先暂时不屏蔽产生的中断。
4)通过INTPND寄存器,查看当前是否有相同的中断已经被请求(如果是,INTPND对应位置一)。
5)如果没有相同的中断在请求,中断处理器才会把这个信号传给处理器,这时处理器才会知道有EINT0的中断真正来了,要对信号进行处理了。
注:如果设定了EINT0是快速中断模式(FIQ),中断通过SRCPND寄存器后就会通过MODE寄存器的判断,确定是FIQ后,中断控制器优先将该中断传给CPU处理。
6)对应传来的中断类型(IRQ或FIQ),通过CPSR寄存器切换到对应的工作模式(ARM有七种工作模式)。
7)切换工作模式后,进入指定的中断处理入口执行中断处理函数。
步在linux下的实现相对复杂,不像在裸板程序,只需要切换一下工作模式,执行相应的函数就可以了。迟点会介绍linux如何实现。
来个流程图比较只在,同时来个类比,处理器是老板,中断处理器是小秘:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
四、注册和释放中断处理函数
上面的介绍只是讲解了一个设备产生中断后要经过怎么样的步骤才能让处理器接收到中断信号。传入处理器后,接下来的工作就是由内核来实现了,那是一个复杂的机制,我们这里先不说。但是,内核提供了相关的接口给我们,我们只要通过接口告诉内核,当来了指定中断时,内核你该执行哪个中断处理函数。
注册中断处理函数:
/*include */
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
使用:
将中断号irq与中断处理函数handler对应
参数:
irq:指定要分配的中断号,中断号的定义在“include/mach/irqs.h”中。注意,不管是单独占有中断请求线的中断,还是共享中断请求线的每个中断,都有一个对应的中断号。,所以,调用该函数不需要考虑是哪种中断(是否共享寄存器),你想哪种中断响应,你就填对应的中断号。
handler:中断处理函数指针。
irqflags:中断处理标记,待会介绍:
devname:该字符串将显示在/proc/irq和/pro/interrupt中。
dev_id:ID 号,待会会介绍。
返回值:成功返回0,失败返回非0。
注册函数需要注意两件事:
1)该函数会睡眠。
2)必须判断返回值。
中断处理标志irqflags,这里先介绍几个待会要用的:
/*linux-2.6.29/include/linux/interrupt.h*/
29 #define IRQF_TRIGGER_NONE 0x00000000
30 #define IRQF_TRIGGER_RISING 0x00000001 //上升沿触发中断
31 #define IRQF_TRIGGER_FALLING 0x00000002 //下降沿触发中断
32 #define IRQF_TRIGGER_HIGH 0x00000004 //高电平触发中断
33 #define IRQF_TRIGGER_LOW 0x00000008 //低电平触发中断
34 #define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
35 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
36 #define IRQF_TRIGGER_PROBE 0x00000010
释放中断处理函数:
void free_irq(unsigned int irq, void *dev_id)
编写中断处理函数:
中断处理函数声明如下:
static irqreturn_t intr_handler(int irq, void *dev_id)
先看第一个参数irq,这是调用中断处理函数时传给它的中断号,对于新版本的内核,这个参数已经用处不大,一般只用于打印。
第二个参数dev_id,这个参数与request_irq()的参数dev_id一致,由于待会的程序我并不需要用这个参数,所以先不介绍。
再看返回值,中断处理函数的返回值有三个:
/*linux-2.6.29/include/linux/interrupt..h*/
21 #define IRQ_NONE (0) //如果产生的中断并不会执行该中断处理函数时返回该值
22 #define IRQ_HANDLED (1) //中断处理函数正确调用会返回
23 #define IRQ_RETVAL(x) ((x) != 0) //指定返回的数值,如果非0,返回IRQ_HADLER,否则
26 #ifndef IRQ_NONE //返回IRQ_NONE。
接下来就要写函数了,在我的开发板中,有一个按键是对应EINT1,我要实现的操作是,当我按下按键,终端打印”key down”。在这个程序中,我并没有使用dev_id。这将在会以后的章节介绍。
/*6th_irq_1/1st/test.c*/
1 #include
2 #include
3
4 #include
5
。。。省略。。。
13 irqreturn_t irq_handler(int irqno, void *dev_id) //中断处理函数
14 {
15 printk("key down\n");
16 return IRQ_HANDLED;
17 }
18
19 static int __init test_init(void) //模块初始化函数
20 {
21 int ret;
22
23 /*注册中断处理函数,必须查看返回值
24 * IRQ_EINT1:中断号,定义在"include/mach/irqs.h"中
25 * irq_handler:中断处理函数
26 * IRQ_TIRGGER_FALLING:中断类型标记,下降沿触发中断
27 * ker_INT_EINT1:中断的名字,显示在/proc/interrupts等文件中
28 * NULL;现在我不使用dev_id,所以这里不传参数
29 */
30 ret = request_irq(IRQ_EINT1, irq_handler, IRQF_TRIGGER_FALLING,
31 "key INT_EINT1", NULL);
32 if(ret){
33 P_DEBUG("request irq failed!\n");
34 return -1;
35 }
36 printk("hello irq\n");
37 return 0;
38 }
39
40 static void __exit test_exit(void) //模块卸载函数
41 {
42 free_irq(IRQ_EINT1, NULL);
43 printk("good bye irq\n");
44 }
45
46 module_init(test_init);
47 module_exit(test_exit);
48
49 MODULE_LICENSE("GPL");
50 MODULE_AUTHOR("xoao bai");
51 MODULE_VERSION("v0.1");
接下来验证一下:
[root: 1st]# insmod test.ko
hello irq
[root: 1st]# key down //按下按键显示
key down
key down
key down
[root: 1st]# cat /proc/interrupts
CPU0
17: 11 s3c-ext0 key INT_EINT1 显示我注册和中断名字
30: 423482 s3c S3C2410 Timer Tick
32: 0 s3c s3c2410-lcd
51: 2782 s3c-ext eth0
70: 49 s3c-uart0 s3c2440-uart
71: 69 s3c-uart0 s3c2440-uart
79: 0 s3c-adc s3c2410_action
80: 0 s3c-adc adc, s3c2410_action
83: 0 - s3c2410-wdt
Err: 0
[root: key INT_EINT1]# rmmod test //卸载
good bye irq
[root: key INT_EINT1]# cat /proc/interrupts //卸载后,我的中断名字消失了
CPU0
30: 828977 s3c S3C2410 Timer Tick
32: 0 s3c s3c2410-lcd
51: 3202 s3c-ext eth0
70: 192 s3c-uart0 s3c2440-uart
71: 277 s3c-uart0 s3c2440-uart
79: 0 s3c-adc s3c2410_action
80: 0 s3c-adc adc, s3c2410_action
83: 0 - s3c2410-wdt
Err: 0
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
五、proc/interrupt
接下来,稍稍介绍一下proc/interrupt
[root: 1st]# cat /proc/interrupts
CPU0
17: 11 s3c-ext0 key INT_EINT1 显示我注册和中断名字
首先,第一列是中断号,之前的程序应该有人会有疑问:
中断号在哪里找的?
在S3C2440中,这些中断号定义在文件"include/mach/irqs.h"中,在这里,可以找到对应的中断:
25 /* main cpu interrupts */
26 #define IRQ_EINT0 S3C2410_IRQ(0) /* 16 */
27 #define IRQ_EINT1 S3C2410_IRQ(1)
28 #define IRQ_EINT2 S3C2410_IRQ(2)
29 #define IRQ_EINT3 S3C2410_IRQ(3)
30 #define IRQ_EINT4t7 S3C2410_IRQ(4) /* 20 */
在这里我标了两处红笔:
第一处:可以看到,S3C2440所有的中断号在原来的基值上加了16构成中断号,但不同的芯片或许有不同的定义方法。
第二处:有些中断号是共享的。在S3C2440中,EINT4---EINT7是共享寄存器SRCPND中的一位,所以,linux系统给这样的中断分配了一个共享的中断号。那就是说,如果你使用IRQ_EINT4t7,当收到这些中断时,都会调用对应的中断处理函数。这就需要在中断处理函数中通过第一个传参irq来辨别中断并执行相应的操作。如:
13 irqreturn_t irq_handler(int irqno, void *dev_id) //中断处理函数
14 {
15 switch(irqno){
16 。。。。}
17 }
那肯定有人会说,这太麻烦了吧,有没有更好的办法处理共享中断号?
那当然是有,继续看文件"include/mach/irqs.h":
61 /* interrupts generated from the external interrupts sources */
62 #define IRQ_EINT4 S3C2410_IRQ(32) /* 48 */
63 #define IRQ_EINT5 S3C2410_IRQ(33)
64 #define IRQ_EINT6 S3C2410_IRQ(34)
65 #define IRQ_EINT7 S3C2410_IRQ(35)
66 #define IRQ_EINT8 S3C2410_IRQ(36)
看到了吧?内核把共享的中断分离出来,只要使用这些标记就可以了。
其实上面我只是想说明:无论在硬件上ARM是怎么实现中断的(是否共享),在内核看来所有的中断都是一样的,都可以独自获得一个中断号。
第二列“11”是对应处理器响应该中断的次数。
第三列“s3c-ext0”是处理这个中断的中断控制器
第四列一看就知道调用irq_request()时定义的中断名字。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
六、总结
其实要实现中断,大部分的工作已经给内核包了,我们只需要做的就是告诉内核,当来了什么中断要执行怎么样的函数,这也是今天介绍的重点,其实步骤很简单:
1)调用两个函数:requesr_irq和free_irq。
2)实现中断处理函数:irq_handler()。
还有没讲的知识:
1)还有几个irqflag没介绍。
2)没有介绍dev_id。
可能有人会加载上面的模块失败,这也是我今天没介绍的只是,共享中断号。这里说的共享和硬件的共享不一样性质,下节会介绍。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
l中断的实现的更多相关文章
- Linux中断概述
中断和异常 1.1中断的由来及实质 Linux内核要管理计算机上的硬件设备,首先要和他们通信.而处理器的速度跟外围硬件设备的速度往往不在一个数量级上,因此,如果内核采取让处理器向硬件发出一个请求,然后 ...
- (笔记)Linux内核学习(六)之并发和同步概念
一 临界区和竞争条件 临界区:访问和操作共享数据的代码段. 竞争条件:多个执行线程处于同一个临界区中. 处于竞争条件:造成访问的数据或者资源不一致状态: 对资源i的访问:ProcessA和B访问后得到 ...
- 内存映射MMAP和DMA【转】
转自:http://blog.csdn.net/zhoudengqing/article/details/41654293 版权声明:本文为博主原创文章,未经博主允许不得转载. 这一章介绍Linux内 ...
- (笔记)Linux内核学习(一)之内核介绍
内核与操作系统: 内核是操作系统的核心部分,包含了系统运行的核心过程,决定系统的性能,操作系统启动内核被装入到RAM中: 操作系统与底层硬件设备交互和为运行应用程序提供执行环境. Linux内核与微内 ...
- BSP
1 BSP概述 BSP即Board Support Package,板级支持包.它来源于嵌入式操作系统与硬件无关的设计思想,操作系统被设计为运行在虚拟的硬件平台上.对于具体的硬件平台,与硬 ...
- STM32 GPIO输入输出(基于HAL库)
一.基础认识 GPIO全名为General Purpose Input Output,即通用输入输出.有时候简称为"IO口".通用,说明它是常见的.输入输出,就是说既能当输入口使用 ...
- STM32 HAL库之串口详细篇
一.基础认识 (一) 并行通信 原理:数据的各个位同时传输 优点:速度快 缺点:占用引脚资源多,通常工作时有多条数据线进行数据传输 8bit数据传输典型连接图: 传输的数据是二进制:11101010, ...
- STM32 ADC详细篇(基于HAL库)
一.基础认识 ADC就是模数转换,即将模拟量转换为数字量 l 分辨率,读出的数据的长度,如8位就是最大值为255的意思,即范围[0,255],12位就是最大值为4096,即范围[0,4096] l ...
- JavaWeb 后端 <二> 之 Servlet 学习笔记
一.Servlet概述 1.什么是Servlet Servlet是一个运行在服务器端的Java小程序,通过HTTP协议用于接收来自客户端请求,并发出响应. 2.Servlet中的方法 public v ...
随机推荐
- c语言 函数传输传递的三种方式(值、指针、引用)
本文摘自<彻底搞定c指针> 一.三道考题开讲之前,我先请你做三道题目.(嘿嘿,得先把你的头脑搞昏才行……唉呀,谁扔我鸡蛋?)考题一,程序代码如下:void Exchg1(int x, in ...
- JavaScript 技巧总结
日期1. 日期时间戳 +new Date() = new Date().getTime() 数组1. 类数组转数组 var arr = Array.prototype.slice.call(argum ...
- Java开源数据库管理工具
SQuirreL SQL Client SQuirreL SQL Client 是一个用 Java 编写的程序,它允许您查看数据库的内容.发出 SQL 命令,以及如您将看到的,执行许多其他功能.构 ...
- javascript模板方法模式
一:什么是模板方法模式: 模板方法模式由二部分组成,第一部分是抽象父类,第二部分是具体实现的子类,一般的情况下是抽象父类封装了子类的算法框架,包括实现一些公共方法及封装子类中所有方法的执行顺序,子类可 ...
- matlab figure 论文级别绘图
1.将figure调整为最大: figure;set(gcf,'outerposition',get(0,'screensize')); 2.获得figure中的大小 [x,y] = ginput 3 ...
- CDNJS:使用JavaScript CDN加速网站载入速度
先介绍一下: 内容传递网络(CDN)或者叫内容分发网络,他的作用是给不同区域的访客以其最快的网速.比如,你的网站是开在美国的,但很多访客来自中国,无疑他们会觉得速度很慢,那么,怎么为他们提速呢?简单来 ...
- 百度地图代码API
百度地图代码API: http://api.map.baidu.com/lbsapi/creatmap/index.html
- SpringMVC关于json、xml自动转换的原理研究
SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnblogs.com/fangjian0423/p/springMVC ...
- spring 注解的总结
一.java内置注解 1.@Target 表示该注解用于什么地方,可能的 ElemenetType 参数包括: ElemenetType.CONSTRUCTOR 构造器声明 ElemenetTyp ...
- HNU 12845 Ballot Analyzing Device
题目链接:http://acm.hnu.cn/online/?action=problem&type=show&id=12845&courseid=270 解题报告:有m个认给 ...