ARM的中断(S3C2440)
中断要发生需要三部分同时工作:
- 中断源
- 中断控制器
- CPU使能中断
中断控制器的作用:
汇集各类中断信号并发给CPU。
中断处理过程:
1.中断控制器汇集各类中断信号并发给CPU。
2.CPU保存当前程序的运行环境(各个寄存器),调用中断服务程序ISR来处理中断
3.在ISR中通过读取中断控制器、外设相关寄存器来识别哪个中断,并处理。
4.清除中断:通过读写中断控制器和外设相关寄存器来实现。
5.最后恢复中断程序的运行环境(即2中保存的各个寄存器)。继续执行。
看一下中断控制器的内部结构框图,了解中断处理细节。
有些请求源是带sub寄存器的,有些则不带。
(1) 对于带sub寄存器的,中断源被触发之后,SUBSRCPND寄存器相应位被置1,如果没有被SUBMASK屏蔽的话,它在SRCPND的相应位也被置1,如果没有被MASK屏蔽的话或者FIQ的话,它将被进一步处理。
(2) 对于不带sub的,它在SRCPND的相应位被置1,如果没有被MASK屏蔽的话或者FIQ的话,它将被进一步处理。
(3) 对于一般中断,可能同时有几个中断被触发,未被INTMASK屏蔽的中断经比较后,选出优先级最高的中断,此中断再INTPND的相应位被置1,然后CPU进入中断模式进行处理。中断服务子程序通过读INTPND寄存器或者INTOFFSET寄存器来确定中断源。
清中断
- 指定中断源的中断服务程序中,必须通过清除 SRCPND 寄存器的相应位来正确的获得来自相同源的中断请求。如果从 ISR 中返回并且未清除相应位,则中断控制器的操作就好像其它中断请求已经从同一个源进入了。换句话说,如果 SRCPND 寄存器的指定位被设置为 1,其通常被认作一个有效中断请求正在等待服务。
清除相应位的时间依赖于用户的需要。如果希望收到来自相同冤源的其它有效请求,则应该首先清除相应位,并且接着使能中断。
可以通过写入一个数据到此寄存器来清除 SRCPND 寄存器的指定位。其只清除那些数据中被设置为 1 的相应位置的 SRCPND 位。那些数据中被设置为 0 的相应位置的位保持不变 - 就如 SRCPND 寄存器,必须在中断服务程序中清除了 SRCPND 寄存器后清除此寄存器。可以通过写入数据到此寄存器中来清除 INTPND 寄存器的指定位。只会清除数据中设置为 1 的相应 INTPND 寄存器位的位置。数据中设置为 0 的相应位的位置则保持不变。
- 可以通过写入数据到此寄存器来清除 SUBSRCPND 寄存器的指定位。只有数据中那些被设置为 1 的相应SUBSRCPND 寄存器的位的位置才能被清除。数据中那些被设置为 0 的相应位的位置则保持不变。
编程
对于特定的中断,不仅要设置中断控制器,还要进行其它设置。
外部中断:
- 设置中断源,让它能发出中断信号
- 设置中断控制器
- 设置CPU,CPSR的 I 位,是中断总开关
- 处理时要分辨中断源
- 处理完要清中断
汇编中的异常处理程序
.align 4
irq_eint:
ldr sp, =0x33d00000
/* 保护现场 */
stmdb sp!, {r0-r12,lr}
/* 处理异常 */
bl key_eint_isr
/* 恢复现场 */
ldmia sp!, {r0-r12,pc}^
相关C程序
/* 初始化中断控制器 */
void interrupt_init(void)
{
INTMSK &= ~(1<<0); /* Service available */
}
/* 初始化中断源,设置KEY为中断源 */
void key_eint_init(void)
{
GPFCON &= ~(3<<0);
GPFCON |= (2<<0);
EXTINT0 |= (7<<0); /* 设置双边沿触发 */
}
/* 按键外部中断处理函数 */
void key_eint_isr(void)
{
/* 中断处理 */
int val = GPFDAT;
if (val & (1<<4))
{
/* 当前状态是灭 */
GPFDAT &= ~(1<<4);
}
else
{
GPFDAT |= (1<<4);
}
/* 清中断 */
SRCPND |= (1<<0);
INTPND |= (1<<0);
}
定时器中断:
- 设置时钟
- 设置初值
- 加载初值,启动Timer
- 设置自动加载
- 中断相关
对于定时器部分,还要参考S3C2440的定时器功能。不再细述。
具体代码如下
#include "timer.h"
extern flag;
void interrupt_init(void)
{
INTMSK &= ~(1<<10); /* Service available */
}
/* 定时器输入时钟频率 = PCLK / {预分频值+1} / {分频值} */
/* 31250 = 50000000/(99+1)/16 */
void timer_init(void)
{
TCFG0 = 99; //定时器0和1的预分频值为99
TCFG1 &= ~(0xf<<0);
TCFG1 |= (3<<0); //定时器1的分频值为1/16
TCNTB0 = 31250; //定时周期设为0.5s
TCON |= (1<<1); //手动更新
TCON &= ~(1<<1);
TCON |= ((1<<0) | (1<<3)); //开启自动重载,开启定时器
}
void timer_isr(void)
{
if (flag)
{
GPFDAT |= (1<<4);
GPFDAT |= (1<<5);
GPFDAT |= (1<<6);
flag = 0;
}
else
{
GPFDAT &= ~(1<<4);
GPFDAT &= ~(1<<5);
GPFDAT &= ~(1<<6);
flag = 1;
}
SRCPND |= (1<<10);
INTPND |= (1<<10);
}