tiny6410裸机实验第8章--------------中断(原理及代码)
2014-03-06 22:22
288 查看
【说明】
前面我们提到了,中断也是异常的一种,那为什么单独用一章介绍中断呢?因为中断相对于其他异常要复杂一些,而且我们只有理解了中断才能为以后理解操作系统做准备。中断的原理其实和所有异常都差不多的。中断异常就是IRQ异常
【中断来源】
当外部设备需要与CPU通讯的时候,就发出一个中断,到达中断控制器,由中断控制器裁决是否向CPU发送此中断,当有多个中断发生时,决定哪个中断被发出,那是不是中断控制器发出的中断一定会被响应呢?不是 的,还记得吗,CPSR中有个中断开关,如果关了,中断就会被CPU屏蔽!
【IRQ异常】
CPU在每次执行指令之前都要检查一下是否有中断发生了,如果有就会发生IRQ异常
处理过程和其他异常是一样的
1】硬件会干下面几件事情
1)CPU进入IRQ模式
2)CPSR赋给SPSR_IRQ
3)切换R13,R14到IRQ的R13, R14
4)SWI指令的下一条指令地址保存到R14
5)跳异常入口地址运行
2】软件要干
1)在异常入口地址放一条跳转指令
2)保存现场
3)处理中断
4)恢复现场
【按键实例】
我们举个例子,写个程序,当按下按键的时候触发中断。
1】配置按键为中断模式
我要首先要让按键所对应的引脚变为中断模式,这样它才有能力发出中断。看看原理图。是哪个引脚呢,我们就看前6个按键吧。。
![](http://img.blog.csdn.net/20140306213145671?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGludXhfZm9yX3lvdQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](http://img.blog.csdn.net/20140306213234343?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGludXhfZm9yX3lvdQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
1)前6个按键对应GPN0到GPN5。。所以要配置这几个引脚成为中断引脚
![](http://img.blog.csdn.net/20140306213741593?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGludXhfZm9yX3lvdQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](http://img.blog.csdn.net/20140306213818875?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGludXhfZm9yX3lvdQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
2】设置中断触发方式
光配置成中断引脚还不够,还需要设置按哪种方式发生中断
![](http://img.blog.csdn.net/20140306213946640?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGludXhfZm9yX3lvdQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](http://img.blog.csdn.net/20140306214035781?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGludXhfZm9yX3lvdQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
1)000代表低电平出发,如果一直处于低电平会源源不断触发中断
2)001代表高电平出发,如果一直处于高电平会源源不断触发中断
3)01X代表下降沿触发,只有高电平变为低电平那一瞬间触发中断
4)10X代表上升沿触发,只有低电平变为高电平那一瞬间触发中断
5)11X代表双边沿触发,只有电平变化那一瞬间触发中断
3】关闭屏蔽
默认情况下,这5个中断是屏蔽的。我们需要取消屏幕,就要设置下下面的寄存器,它也在手册的GPIO那一章。
![](http://img.blog.csdn.net/20140306214639921?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGludXhfZm9yX3lvdQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](http://img.blog.csdn.net/20140306214708312?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGludXhfZm9yX3lvdQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
显然是要全清为0
4】使能中断
上边都是在设置GPIO管脚,现在需要设置一下6410的中断控制器,它就像一个管家,所有的中断都需要经过它,所以它身上还有一个大开关
6410有2组中断
![](http://img.blog.csdn.net/20140306215658312?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGludXhfZm9yX3lvdQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
看看我们的中断属于哪一组?没错,,第0组
![](http://img.blog.csdn.net/20140306215745015?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGludXhfZm9yX3lvdQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
我们如何打开这个中断开关呢,用这个寄存器
![](http://img.blog.csdn.net/20140306215944234?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGludXhfZm9yX3lvdQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](http://img.blog.csdn.net/20140306220004906?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGludXhfZm9yX3lvdQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
显然我们要设置低2位为11
【开中断】
这个开中断是所有中断的总开关,在CPSR中设定,它属于CPU的状态,而不是中断控制器的状态。也就是无论你中断控制器发不发中断,如果我CPSR中禁止中断了,你怎么发都没用。还记得那张图吗,其中的I位就是中断禁止位,要设置为0
![](http://img.blog.csdn.net/20140306220750703?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGludXhfZm9yX3lvdQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
【中断处理】
当发生中断的时候,我们需要知道到底是哪个中断发生了。我们可以查查这个寄存器
![](http://img.blog.csdn.net/20140306221935046?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGludXhfZm9yX3lvdQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](http://img.blog.csdn.net/20140306222004500?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGludXhfZm9yX3lvdQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
那我们咋么知道按键是按下还是松开呢,,直接查询GPNDAT的状态就可以了
【保存和恢复现场】
代码中使用了如下的方式保存和恢复现场,如果不熟悉这两条指令大家应该去学习一下ARM汇编,这两条是批量内存操作,很好理解的
stmdb sp!, {r0-r12, lr}
。。。。。
ldmia sp!, {r0-r12, pc}^
【源代码】
start.S
irq.c
前面我们提到了,中断也是异常的一种,那为什么单独用一章介绍中断呢?因为中断相对于其他异常要复杂一些,而且我们只有理解了中断才能为以后理解操作系统做准备。中断的原理其实和所有异常都差不多的。中断异常就是IRQ异常
【中断来源】
当外部设备需要与CPU通讯的时候,就发出一个中断,到达中断控制器,由中断控制器裁决是否向CPU发送此中断,当有多个中断发生时,决定哪个中断被发出,那是不是中断控制器发出的中断一定会被响应呢?不是 的,还记得吗,CPSR中有个中断开关,如果关了,中断就会被CPU屏蔽!
【IRQ异常】
CPU在每次执行指令之前都要检查一下是否有中断发生了,如果有就会发生IRQ异常
处理过程和其他异常是一样的
1】硬件会干下面几件事情
1)CPU进入IRQ模式
2)CPSR赋给SPSR_IRQ
3)切换R13,R14到IRQ的R13, R14
4)SWI指令的下一条指令地址保存到R14
5)跳异常入口地址运行
2】软件要干
1)在异常入口地址放一条跳转指令
2)保存现场
3)处理中断
4)恢复现场
【按键实例】
我们举个例子,写个程序,当按下按键的时候触发中断。
1】配置按键为中断模式
我要首先要让按键所对应的引脚变为中断模式,这样它才有能力发出中断。看看原理图。是哪个引脚呢,我们就看前6个按键吧。。
1)前6个按键对应GPN0到GPN5。。所以要配置这几个引脚成为中断引脚
2】设置中断触发方式
光配置成中断引脚还不够,还需要设置按哪种方式发生中断
1)000代表低电平出发,如果一直处于低电平会源源不断触发中断
2)001代表高电平出发,如果一直处于高电平会源源不断触发中断
3)01X代表下降沿触发,只有高电平变为低电平那一瞬间触发中断
4)10X代表上升沿触发,只有低电平变为高电平那一瞬间触发中断
5)11X代表双边沿触发,只有电平变化那一瞬间触发中断
3】关闭屏蔽
默认情况下,这5个中断是屏蔽的。我们需要取消屏幕,就要设置下下面的寄存器,它也在手册的GPIO那一章。
显然是要全清为0
4】使能中断
上边都是在设置GPIO管脚,现在需要设置一下6410的中断控制器,它就像一个管家,所有的中断都需要经过它,所以它身上还有一个大开关
6410有2组中断
看看我们的中断属于哪一组?没错,,第0组
我们如何打开这个中断开关呢,用这个寄存器
显然我们要设置低2位为11
【开中断】
这个开中断是所有中断的总开关,在CPSR中设定,它属于CPU的状态,而不是中断控制器的状态。也就是无论你中断控制器发不发中断,如果我CPSR中禁止中断了,你怎么发都没用。还记得那张图吗,其中的I位就是中断禁止位,要设置为0
【中断处理】
当发生中断的时候,我们需要知道到底是哪个中断发生了。我们可以查查这个寄存器
那我们咋么知道按键是按下还是松开呢,,直接查询GPNDAT的状态就可以了
【保存和恢复现场】
代码中使用了如下的方式保存和恢复现场,如果不熟悉这两条指令大家应该去学习一下ARM汇编,这两条是批量内存操作,很好理解的
stmdb sp!, {r0-r12, lr}
。。。。。
ldmia sp!, {r0-r12, pc}^
【源代码】
start.S
.global _start _start: // 异常向量表 b reset /* 复位时,cpu跳到0地址 */ b halt /* cpu遇到不能识别的指令时 */ b halt /* swi异常,进入svc模式 */ b halt /* 预取中止异常 */ b halt /* 数据访问异常 */ b halt /* 没用到 */ ldr pc, _irq /* 中断异常 */ b halt /* 快中断异常 */ reset: // 外设地址告诉cpu ldr r0, =0x70000000 orr r0, r0, #0x13 mcr p15,0,r0,c15,c2,4 // 关看门狗 ldr r0, =0x7E004000 mov r1, #0 str r1, [r0] // 设置栈 ldr sp, =8*1024 // 初始化时钟 bl clock_init // 初始化ddr bl sdram_init // 初始化nandflash bl nand_init // 初始化irq bl irq_init // 开中断 mov r0, #0x53 msr CPSR_cxsf, r0 // 重定位,把程序的代码段、数据段复制到它的链接地址去 adr r0, _start ldr r1, =_start ldr r2, =bss_start sub r2, r2, r1 cmp r0,r1 beq clean_bss bl copy2ddr cmp r0, #0 bne halt // 清BSS,把BSS段对应的内存清零 clean_bss: ldr r0, =bss_start ldr r1, =bss_end mov r3, #0 cmp r0, r1 beq on_ddr clean_loop: str r3, [r0], #4 cmp r0, r1 bne clean_loop on_ddr: // 跳转 ldr pc, =main // 中断异常 _irq: .word irq irq: /* 保存现场 */ ldr sp, =0x54000000 sub lr, lr, #4 //这里要减去4,因为我们当前指令还没执行就发现中断了,所以要重新执行 stmdb sp!, {r0-r12, lr} //保护现场, /* 处理异常 */ bl do_irq /* 恢复现场 */ ldmia sp!, {r0-r12, pc}^ /* ^表示把spsr恢复到cpsr */ halt: b halt
irq.c
#include "stdio.h" #define GPNCON (*((volatile unsigned long *)0x7F008830)) #define GPNDAT (*((volatile unsigned long *)0x7F008834)) #define EINT0CON0 (*((volatile unsigned long *)0x7F008900)) #define EINT0MASK (*((volatile unsigned long *)0x7F008920)) #define EINT0PEND (*((volatile unsigned long *)0x7F008924)) #define PRIORITY (*((volatile unsigned long *)0x7F008280)) #define SERVICE (*((volatile unsigned long *)0x7F008284)) #define SERVICEPEND (*((volatile unsigned long *)0x7F008288)) #define VIC0IRQSTATUS (*((volatile unsigned long *)0x71200000)) #define VIC0FIQSTATUS (*((volatile unsigned long *)0x71200004)) #define VIC0RAWINTR (*((volatile unsigned long *)0x71200008)) #define VIC0INTSELECT (*((volatile unsigned long *)0x7120000c)) #define VIC0INTENABLE (*((volatile unsigned long *)0x71200010)) #define VIC0INTENCLEAR (*((volatile unsigned long *)0x71200014)) #define VIC0PROTECTION (*((volatile unsigned long *)0x71200020)) #define VIC0SWPRIORITYMASK (*((volatile unsigned long *)0x71200024)) #define VIC0PRIORITYDAISY (*((volatile unsigned long *)0x71200028)) #define VIC0ADDRESS (*((volatile unsigned long *)0x71200f00)) void irq_init(void) { /* 配置GPN0~5引脚为中断功能 */ GPNCON &= ~(0xfff); GPNCON |= 0xaaa; /* 设置中断触发方式为: 双边沿触发 */ EINT0CON0 &= ~(0xfff); EINT0CON0 |= 0x777; /* 禁止屏蔽中断 */ EINT0MASK &= ~(0x3f); /* 在中断控制器里使能这些中断 */ VIC0INTENABLE |= (0x3); /* bit0: eint0~3, bit1: eint4~11 */ } void do_irq(void) { int i = 0; /* 分辨是哪个中断 */ for (i = 0; i < 6; i ++) { if (EINT0PEND & (1<<i)) { if (GPNDAT & (1<<i)) { printf("K%d released\n\r", i+1); } else { printf("K%d pressed\n\r", i+1); } } } /* 清中断 */ EINT0PEND = 0x3f; VIC0ADDRESS = 0; }
相关文章推荐
- tiny6410裸机实验第2章--------------点亮LED灯(原理和代码分析)
- tiny6410裸机实验第10章--------------PWM定时器(原理及代码)
- tiny6410裸机实验第8章--------------中断(中断版UART)
- tiny6410裸机实验第8章--------------中断(中断向量寄存器)
- linux 系统调用中断劫持实现—原理和代码
- 黑马程序员-对eclipse代码补全功能原理的猜想及实验(对反射技术的一些使用)
- 谱聚类原理简述(含实验代码)
- 单片机定时器中断原理和C语言代码详解
- 代码片段-----键盘中断驱动实验
- tiny6410裸机实验第6章--------------NAND(初始化代码及NAND操作)
- 编译原理实验,实现一个代码解析程序
- DOS中断跟踪,在纯Dos下使用,分析Dos工作原理的最佳工具,原理详看原代码
- tiny6410裸机实验第4章--------------UART(原理分析)
- arm11 s3c6410 外部中断实验代码
- 我在研究Photoshop浮雕效果做实验时,无意中写了一段代码,经过几天的改进和原理论证,我觉得该浮雕效果从原理上是说得通的,应该有一定的应用价值,故发表在我的BLOG上,希望大家能提出改进意见:
- tiny6410裸机实验第9章--------------LCD(原理及源代码)
- tiny6410裸机实验第6章--------------NAND(初始化原理)
- linux中断底半部之 softirq 原理与代码分析
- tiny6410裸机实验第5章--------------DDR(代码重定位)
- tiny6410裸机实验第0章--------------开发环境的搭建(代码阅读和编写工具 source insight )