您的位置:首页 > 编程语言

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个按键吧。。

                           


                             


                             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;
}


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux 嵌入式
相关文章推荐