一、S3C2440的中断系统
1.1 S3C2440的7种模式
用户模式(usr):ARM处理器正常的程序执行状态
快速中断模式(fiq):用于高速数据传输或通道处理
中断模式(irq):用于通用的中断处理
管理模式(svc):操作系统使用的保护模式
数据访问终止模式(abt):当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护
系统模式(sys):运行具有特权的操作系统任务
未定义指令中止模式(und):当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真
除用户模式外,其他6种工作模式都属于特权模式,大多数程序运行于用户模式,进入特权模式是为了处理中断、异常,或者访问被保护的系统资源。
1.2 CPSR(current program state register,当前程序状态寄存器)
ldr sp, =4096 @ 设置栈指针,以下都是C函数,调用前需要设好栈
bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启
msr cpsr_c, #0xd2 @ 进入中断模式 0xd2=1101 0010
ldr sp, =3072 @ 设置中断模式栈指针
msr cpsr_c, #0xd3 @ 进入管理模式 0xd3=1101 0011
ldr sp, =4096 @ 设置管理模式栈指针,
@ 其实复位之后,CPU就处于管理模式,
@ 前面的“ldr sp, =4096”完成同样的功能,此句可省略
1.3 S3C2440的中断过程
首先,当有中断请求时,SUBSRCPND或者SRCPND中的相应位会被置1
然后,根据SUBMASK和MASK寄存器的设置值进行相应的屏蔽,如果一个中断请求发生但被屏蔽了,中断信号传递停止
最后,根据MODE寄存器判断该中断源的模式,如果是快中断模式(系统只有一个中断源为快中断其他的都为普通中断)直接执行,如果是普通中断则跟其他普通中断角逐出一个优先级最高的送到INTPND寄存器
二、S3C2440中断寄存器
s3c2440的中断系统一共需要设置5个寄存器,中断源寄存器SRCPND、SUBSRCPND,中断模式寄存器INTMOD,中断屏蔽寄存器INTMASK、INTSUBMASK,中断优先级寄存器PRIORITY,中断待决寄存器INTPND
2.1 中断源寄存器
s3c2440支持60个中断源,部分是子中断源(比如串口接收中断、串口发送中断、串口错误中断都属于串口中断)
SRCPND中断源寄存器地址为0X4A000000,它一共32位,每一位都代表一个中断源,相应的中断置1就表示有中断请求,置0就表示无中断请求(中断的置1系统自动完成,清0需要用户手动)。
清0的方法就是向SRCPND或者SUBSRCPND的相应位写1即可,清0的位置由用户决定。如果在中断处理程序结束处清0,那么在执行中断程序过程中即便该中断源又有中断请求,程序也无法响应。如果中断处理程序的开始处清0,那么在执行中断处理程序的过程中该中断源又有中断请求程序也能响应。
SRCPND寄存器中每位代表的中断源如下:
SUBSRCPND子中断源寄存器地址为0X4A000018,每位代表的中断源如下:
2.2 中断模式寄存器
INTMOD中断模式寄存器的地址为0X4A000004,一共32位,每位代表的中断源和SRCPND中一致。如果置1则相应位的中断源就为快中断,否则就为普通中断。(32个中断源中只有一个能设为快中断)
2.3 中断屏蔽寄存器
INTMSK中断屏蔽寄存器的地址为0X4A000008,一共32位,每位代表的中断源和SRCPND中一致。如果置1则相应的中断源将不被响应,否则可以响应相应的中断源。(中断屏蔽不影响:有中断请求SRCPND相应位置1)
2.4 中断优先级寄存器
s3c2440优先级仲裁模块示意图如下:
s3c2440优先级逻辑由7个仲裁器构成。每个仲裁器中REQ0优先级最高,REQ5优先级最低。余下的REQ1、REQ2、REQ3、REQ4优先级则由ARB_MODE(1位)和ARB_SEL(2位)来决定。
仲裁器的ARB_SEL位和各输入信号优先级对应关系表
当某个仲裁器的ARB_MODE被设置为0时,它的ARB_SEL位是不会自动变化的,此时该仲裁器的输入引脚的优先级固定不变。(当然可以通过软件修改ARB_SEL的值来改变它们的优先级)当ARB_MODE的值设置为1时,ARB_SEL会随着已被服务的IRQx(x为1~4)值而自动改变。
中断优先级仲裁器的ARB_SEL变化规则
s3c2440的优先级寄存器PRIORITY 的位置为0x4A00000C,为32位,7组仲裁器,每组使用3位,共使用21位,其寄存器每位的分布如下:
2.5 中断待决寄存器
中断待决寄存器INTPND地址为0X4A000010,一共32位,每位代表的中断源和SRCPND中一致。如果相应位置1则中断请求被受理。同一时间只允许一个中断源被受理。(INTPND置1系统自动,清0用户手动,在SRCPND清0后才清除)
INTOFFSET中断偏移寄存器地址为 0X4A000014,一共32位,表示INTPND中的置1位的位序号(0~31),它在INTPND和SRCPND清0后自动清0。
三.中断实例程序
3.1 外部中断实例
@初始化的代码
@ File:head.S
@ 功能:初始化,设置中断模式、管理模式的栈,设置好中断处理函数
@******************************************************************************
.extern main
.text
.global _start
_start:
@******************************************************************************
@ 异常向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用
@******************************************************************************
b Reset
@ 0x04: 未定义指令中止模式的向量地址
HandleUndef:
b HandleUndef
@ 0x08: 管理模式的向量地址,通过SWI指令进入此模式
HandleSWI:
b HandleSWI
@ 0x0c: 指令预取终止导致的异常的向量地址
HandlePrefetchAbort:
b HandlePrefetchAbort
@ 0x10: 数据访问终止导致的异常的向量地址
HandleDataAbort:
b HandleDataAbort
@ 0x14: 保留
HandleNotUsed:
b HandleNotUsed
@ 0x18: 中断模式的向量地址
b HandleIRQ
@ 0x1c: 快中断模式的向量地址
HandleFIQ:
b HandleFIQ
Reset:
ldr sp, =4096 @ 设置栈指针,以下都是C函数,调用前需要设好栈
bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启
msr cpsr_c, #0xd2 @ 进入中断模式
ldr sp, =3072 @ 设置中断模式栈指针
msr cpsr_c, #0xd3 @ 进入管理模式
ldr sp, =4096 @ 设置管理模式栈指针,
@ 其实复位之后,CPU就处于管理模式,
@ 前面的“ldr sp, =4096”完成同样的功能,此句可省略
bl init_led @ 初始化LED的GPIO管脚
bl init_irq @ 调用中断初始化函数,在init.c中
msr cpsr_c, #0x53 @ 设置I-bit=0,开IRQ中断
ldr lr, =halt_loop @ 设置返回地址
ldr pc, =main @ 调用main函数
halt_loop:
b halt_loop
HandleIRQ:
sub lr, lr, #4 @ 计算返回地址
stmdb sp!, { r0-r12,lr } @ 保存使用到的寄存器
@ 注意,此时的sp是中断模式的sp
@ 初始值是上面设置的3072
ldr lr, =int_return @ 设置调用ISR即EINT_Handle函数后的返回地址
ldr pc, =EINT_Handle @ 调用中断服务函数,在interrupt.c中
int_return:
ldmia sp!, { r0-r12,pc }^ @ 中断返回, ^表示将spsr的值复制到cpsr,此处将返回到主函数执行
/*中断相关寄存器的初始化*/
#include "s3c24xx.h"
/*
* LED1,LED2,LED4对应GPB5、GPB6、GPB7、GPB8
*/
#define GPB5_out (1<<(5*2))
#define GPB6_out (1<<(6*2))
#define GPB7_out (1<<(7*2))
#define GPB8_out (1<<(8*2))
#define GPB5_msk (3<<(5*2))
#define GPB6_msk (3<<(6*2))
#define GPB7_msk (3<<(7*2))
#define GPB8_msk (3<<(8*2))
/*
* K1,K2,K3,K4对应GPF1、GPF4、GPF2、GPF0
*/
#define GPF0_int (0x2<<(0*2))
#define GPF1_int (0x2<<(1*2))
#define GPF2_int (0x2<<(2*2))
#define GPF4_int (0x2<<(4*2))
#define GPF0_msk (3<<(0*2))
#define GPF1_msk (3<<(1*2))
#define GPF2_msk (3<<(2*2))
#define GPF4_msk (3<<(4*2))
/*
* 关闭WATCHDOG,否则CPU会不断重启
*/
void disable_watch_dog(void)
{
WTCON = 0; // 关闭WATCHDOG很简单,往这个寄存器写0即可
}
void init_led(void)
{
// LED1,LED2,LED3,LED4对应的4根引脚设为输出
GPBCON &= ~(GPB5_msk | GPB6_msk | GPB7_msk | GPB8_msk);
GPBCON |= GPB5_out | GPB6_out | GPB7_out | GPB8_out;
}
/*
* 初始化GPIO引脚为外部中断
* GPIO引脚用作外部中断时,默认为低电平触发、IRQ方式(不用设置INTMOD)
*/
void init_irq( )
{
// K1,K2,K3,K4对应的4根引脚设为中断功能
GPFCON &= ~(GPF0_msk | GPF1_msk | GPF2_msk | GPF4_msk);
GPFCON |= GPF0_int | GPF1_int | GPF2_int | GPF4_int;
// 对于EINT4,需要在EINTMASK寄存器中使能它
EINTMASK &= ~(1<<4);
/*
* 设定优先级:
* ARB_SEL0 = 00b, ARB_MODE0 = 0: REQ1 > REQ2 > REQ3,即EINT0 > EINT1 > EINT2
* 仲裁器1、6无需设置
* 最终:
* EINT0 > EINT1> EINT2 > EINT4 即K4 > K1 > K3 > K2
*/
PRIORITY = (PRIORITY & ((~0x01) | ~(0x3<<7)));
// EINT0、EINT1、EINT2、EINT4_7使能
INTMSK &= (~(1<<0)) & (~(1<<1)) & (~(1<<2)) & (~(1<<4));
}
/*中断处理函数*/
#include "s3c24xx.h"
void EINT_Handle()
{
unsigned long oft = INTOFFSET;
unsigned long val;
/*
* K1,K2,K3,K4对应GPF1、GPF4、GPF2、GPF0
* 即 EINT1, ETIN4, EINT2, EINT0
* oft为 1, 4, 2, 0 (对应INTMSK寄存器)
*/
switch( oft )
{
// K1被按下
case 1:
{
GPBDAT |= (0xF<<5); // 所有LED熄灭
GPBDAT &= ~(1<<5); // LED1点亮
break;
}
// K2被按下
case 4:
{
GPBDAT |= (0xF<<5); // 所有LED熄灭
GPBDAT &= ~(1<<6); // LED2点亮
break;
}
// K3被按下
case 2:
{
GPBDAT |= (0xF<<5); // 所有LED熄灭
GPBDAT &= ~(1<<7); // LED3点亮
break;
}
// K4被按下
case 0:
{
GPBDAT |= (0xF<<5); // 所有LED熄灭
GPBDAT &= ~(1<<8); // LED4点亮
break;
}
default:
break;
}
//清中断
if( oft == 4 )
EINTPEND = (1<<4); //清EINT4中断源,相当于SRCPND
SRCPND = 1<
/*主函数*/
int main()
{
while(1);
return 0;
}