1.管脚介绍
2.相关知识点介绍
1)中断源相关仲裁器 :
2)异常向量表:
3)PSR格式(通过对I、F位置'0',可以开启IRQ和FIQ )
arm920t包含了一个CPSR(当前程序状态寄存器) , 5个SPSRs(保存程序状态寄存器 : 用于异常中断处理程序保存CPSR的值) ,
其中的I , f 位分别是用来使能和禁止IRQ和FIQ的(置'0'使能 ) , M[4:0]是用来选择处理器运行模式的
注:
在配置好相关中断控制寄存器之后要通过置'0' PSR中的I位或者F位来使能IRQ或者FIQ , 如果中断服务子程序是用c语言写的话还要在
启动中断使能之前通过设置M[4:0]来进入IRQ模式下的sp.
4) 进入一个异常时CPU做的事
①把下一条指令的的地址放入相应的LR寄存器里 . 如果是从AMR状态下进入异常的,那就是PC的下一条(PC+4 or PC+8,
主要依据于异常模式,具体可以参考下表)指令被拷贝到LR ; 如果是从THUMB模式下进入异常的,那当前PC偏移值会被写入LR.
②拷贝CPSR到相应模式下的SPSR
③迫使的CPSR模式位的值变为相应异常模式的值
④迫使PC从相关异常向量中获取下一条要执行的指令
通常也设置中断禁止标志来阻止另外的难于管理的嵌套中断
5)退出一个异常时CPU所做的活动
①取出lr里的值,减去适当的偏移量,赋给PC
②把SPSR的内容拷贝到CPSR
③如果在进入异常处理时有设置中断禁止标志,则清除中断禁止标志
3.相关寄存器介绍:
①SRCPND
注:
这个寄存器是用于指明哪个中断源产生中断信号了,1为相应中断源产生中断请求,具体能不能被服务还得看其它寄存器的设置.
在进入中断服务子程序后要对其写'1'清零.
②INTMOD
注:
采用默认模式IRQ就好了
③INTMSK
注:
默认所有中断都被屏蔽的 , 我们不能开启这个中断屏蔽位 .
配置如下:
INTMSK &=~( 1 << 5 )
④PRIORITY
注:
关于优先级设置 ,ARB_SEL6设置为00 , 这样仲裁器6的REQ1就拥有了第二高的优先级了. 同理,ARB_SEL1也设置为00 ,
然后不使能优先级自动轮流翻转.
配置如下:
PRIOITY &= ~( ( 0x3<< 19 ) | ( 0x3 << 9 ) | ( 1 << 1) | ( 1 << 6 ) )
⑤INTPND
注:
这个寄存器是用于指明哪一个中断请求可以被响应.
在进入中断服务子程序后对其写'1'清零.
⑥INTOFFSET
注:
通过读取这个寄存器的值可以得知中断源.
⑦EXTINT1/2
注:
FLTENn用于使能或者禁止滤波 : 采用滤波使能 '1'
EINTn用于设置设置信号触发模式 : 选择下降沿触发'01x'
EXTINT1 |= (0x05<<1) | (0x05<<13) | (0x05 << 21) |(0x05 << 25) | (0x05 << 29)
EXTINT2 |= (0x05 << 13)
⑧EINTMASK
这个寄存器用于屏蔽外部中断源 , 置'1'为屏蔽相应中断.
配置结果如下:
EINTMASK &=~( 0x3F << 8 );
⑨EINTPEND
注:
这个寄存器是用于指明哪一个外部中断源产生中断请求.
在进入中断服务子程序后对其写'1'清零.
4.程序设计流程图
1)主流程图:
2)中断服务子程序流程图
3)中断服务处理子函数
5.程序设计
1)Makefile
interrupt.bin : head.o interrupt_main.o2)interrupt.lds
SECTIONS {}
3)head.S
@与中断相关
.equ INTMSK , 0x4A000008
.equ PRIOITY , 0x4A00000C
.equ EINTMASK, 0x560000A4
.equ EXTINT1 , 0x5600008C
.equ EXTINT2 , 0x56000090
@与看门狗相关
.equ WTCON , 0x53000000
@与管脚配置相关
.equ GPGCON , 0x56000060
.equ GPGUP , 0x56000068
@与灯光配置相关的
.equ GPBCON , 0x56000010
.equ GPBDAT , 0x56000014
.Text
.global _start
_start:
/***********设置中断向量表*************/
b ResetInit @复位异常入口
HandlerUndef:
b HandlerUndef @未定义异常入口
HandlerSWI:
b HandlerSWI @软中断异常入口
HandlerPabort:
b HandlerPabort @取指中止异常入口
HandlerDabort:
b HandlerDabort @数据中止异常入口
HandlerNotUsed:
b HandlerNotUsed @保留
b HandlerIRQ @中断异常入口
HandlerFIQ:
b HandlerFIQ @快中断异常入口
/************END设置中断向量表***********/
ResetInit:
/**************关闭看门狗****************/
ldr r0 , =WTCON
mov r1 , #0x0
str r1 , [r0]
/************END关闭看门狗***************/
/***********设置系统模式下的sp***********/
@复位默认进入系统模式
ldr sp , =4096
/********END设置系统模式下的sp***********/
/********配置相关管脚为外部中断功能******/
ldr r0 , =GPGCON
ldr r1 , [r0]
ldr r2 , =(3<<22)|(0x3f<<10)|(0x03<<6)|(0x03)
bic r1 , r1 , r2
ldr r2 , =(2<<22)|(42<<10)|(2<<6)|(2<<0)
orr r1 , r1 , r2
str r1 , [r0]
ldr r0 , =GPGUP
mov r1 , #0
str r1 , [r0]
/*******END配置相关管脚为外部中断功能****/
/************初始化LED灯管脚*************/
@把LED1-4管脚置为输出
ldr r0 , =GPBCON
ldr r1 , [r0] @把GPBCON里的内容加载到r1里
ldr r2 , =(0xFF<<10)
bic r1 , r1 ,r2 @操作数取反码或上r1,用于清零工作
ldr r2 , =(0x55<<10)
orr r1 , r1 , r2
str r1 , [r0]
@灯全灭
ldr r0 , =GPBDAT
ldr r1 , [r0]
ldr r2 , =(0x0F<<5)
orr r1 , r1 , r2
str r1 , [r0]
/***********END初始化LED灯管脚************/
/********调用配置中断控制器子程序********/
bl IntConfigure
/*******END调用配置中断控制器子程序******/
/*进入中断模式设置中断模式下的sp退出到系统模式
*使能IRQ中断*/
@进入中断模式,禁止中断,其中cpsr后的_c表示cpsr[7:0]
msr cpsr_c , 0xd2
@设置sp
ldr sp , =3072
@退出到系统模式,使能IRQ中断
msr cpsr_c , 0x5f
/*END进入中断模式设置中断模式下的sp退出到系统模式*/
/************死循环等待中断**************/
halt_loop:
b halt_loop
/************END死循环等待中断***********/
/*************IRQ中断服务子程序**********/
HandlerIRQ:
@因为在产生中断异常时,lr存的是当前指令的下一条指令,所以要减四
sub lr , lr , #4
@把相关寄存器压入中断模式下的栈
@db表示sp每次传送内容前减1
stmdb sp! , {r0-r12 , lr}
@禁止IRQ中断,其中cpsr后的_c表示cpsr[7:0]
mrs r1 , cpsr_all
orr r1 , r1 , #(1<<7)
msr cpsr_all , r1
@调用中断服务处理函数
ldr lr , =IRQ_Return
ldr pc , =main
IRQ_Return:
@把栈里面的内容推出到相应寄存器里,并把lr推到pc寄存器实现跳转
@ia表示每次传送后加1 , 当寄存器列表中包含了pc寄存器,选用^为后缀,就会把spsr拷贝到cpsr
ldmia sp! , {r0-r12 , pc}^
/********END IRQ中断服务子程序***********/
/***********配置中断控制器子程序*********/
IntConfigure:
@关闭EINT8_23的中断屏蔽位
ldr r0 , =INTMSK
bic r1 , r1 , #(1<<5)
str r1 , [r0]
@ARB_SEL6 = 00 , ARB_SEL1 = 00
ldr r0 , =PRIOITY
ldr r2 , =(0x03<<19)|(0x03<<9)|(0x01<<6)|(0x01<<1)
bic r1 , r1 , r2
str r1 , [r0]
@关闭EINT19,EINT15,EINT14,EINT13,EINT11,EINT8屏蔽
ldr r0 , =EINTMASK
ldr r2 , =(0x3F<<8)
bic r1 , r1 , r2
str r1 , [r0]
@使能管脚滤波,下降沿触发
ldr r0 , =EXTINT1
ldr r1 , =(0x05<<1)|(0x05<<13)|(0x05<<21)|(0x05<<25)|(0x05<<29)
str r1 , [r0]
ldr r0 , =EXTINT2
ldr r1 , =(0x05<<13)
str r1 , [r0]
bx lr
/****************END IntConfigure***********/
4)interrupt_main.c
#define GPBDAT (*(volatile unsigned long *)0x56000014)
#define INTOFFSET (*(volatile unsigned long *)0x4A000014)
#define EINTPEND (*(volatile unsigned long *)0x560000A8)
#define SRCPND (*(volatile unsigned long *)0x4A000000)
#define INTPND (*(volatile unsigned long *)0x4A000010)
/**
*如果不开启-O2以上优化,gcc编译器不提供inline优化,所以写入的inline只在编译时加入了-O2优化选项才会有效,这时for里面的循环参数可以不加volatile
*如果开启了-O2以上优化,gcc提供的inline优化有效,如果for循环里做编译器认为没意义的事,循环参数加上volatile声明。
*/
static inline void delay(volatile unsigned long dly)
{
for(; dly > 0; dly--);
}
int main()
{
unsigned long i = 0;
delay(20000);
if(INTOFFSET == 5){
i = EINTPEND & ((0x01<<19)|(0x07<<13)|(0x01<<11)|(0x01<<8));
switch(i)
{
case (0x01<<8):
GPBDAT = GPBDAT^(1<<5); //翻转LED1
break;
case (0x01<<11):
GPBDAT = GPBDAT^(1<<6); //翻转LED2
break;
case (0x01<<13):
GPBDAT = GPBDAT^(1<<7); //翻转LED3
break;
case (0x01<<14):
GPBDAT = GPBDAT^(1<<8); //翻转LED4
break;
case (0x01<<15):
GPBDAT &= ~(0x0f<<5); //灯全亮
break;
case (0x01<<19):
GPBDAT |= (0x0f<<5); //灯全灭
break;
default:
break;
}
}
EINTPEND = EINTPEND; //这个要先清除,不然处理器还是会以为产生外部中断
SRCPND = SRCPND;
INTPND = INTPND;
return 0;
}