ARM的中断(S3C2440)

中断要发生需要三部分同时工作:

  1. 中断源
  2. 中断控制器
  3. CPU使能中断

中断控制器的作用
汇集各类中断信号并发给CPU。

中断处理过程
1.中断控制器汇集各类中断信号并发给CPU。
2.CPU保存当前程序的运行环境(各个寄存器),调用中断服务程序ISR来处理中断
3.在ISR中通过读取中断控制器、外设相关寄存器来识别哪个中断,并处理。
4.清除中断:通过读写中断控制器和外设相关寄存器来实现。
5.最后恢复中断程序的运行环境(即2中保存的各个寄存器)。继续执行。

看一下中断控制器的内部结构框图,了解中断处理细节。
ARM的中断(S3C2440)
有些请求源是带sub寄存器的,有些则不带。
(1) 对于带sub寄存器的,中断源被触发之后,SUBSRCPND寄存器相应位被置1,如果没有被SUBMASK屏蔽的话,它在SRCPND的相应位也被置1,如果没有被MASK屏蔽的话或者FIQ的话,它将被进一步处理。

(2) 对于不带sub的,它在SRCPND的相应位被置1,如果没有被MASK屏蔽的话或者FIQ的话,它将被进一步处理。

(3) 对于一般中断,可能同时有几个中断被触发,未被INTMASK屏蔽的中断经比较后,选出优先级最高的中断,此中断再INTPND的相应位被置1,然后CPU进入中断模式进行处理。中断服务子程序通过读INTPND寄存器或者INTOFFSET寄存器来确定中断源。

清中断

  1. 指定中断源的中断服务程序中,必须通过清除 SRCPND 寄存器的相应位来正确的获得来自相同源的中断请求。如果从 ISR 中返回并且未清除相应位,则中断控制器的操作就好像其它中断请求已经从同一个源进入了。换句话说,如果 SRCPND 寄存器的指定位被设置为 1,其通常被认作一个有效中断请求正在等待服务。
    清除相应位的时间依赖于用户的需要。如果希望收到来自相同冤源的其它有效请求,则应该首先清除相应位,并且接着使能中断。
    可以通过写入一个数据到此寄存器来清除 SRCPND 寄存器的指定位。其只清除那些数据中被设置为 1 的相应位置的 SRCPND 位。那些数据中被设置为 0 的相应位置的位保持不变
  2. 就如 SRCPND 寄存器,必须在中断服务程序中清除了 SRCPND 寄存器后清除此寄存器。可以通过写入数据到此寄存器中来清除 INTPND 寄存器的指定位。只会清除数据中设置为 1 的相应 INTPND 寄存器位的位置。数据中设置为 0 的相应位的位置则保持不变。
  3. 可以通过写入数据到此寄存器来清除 SUBSRCPND 寄存器的指定位。只有数据中那些被设置为 1 的相应SUBSRCPND 寄存器的位的位置才能被清除。数据中那些被设置为 0 的相应位的位置则保持不变。

编程

对于特定的中断,不仅要设置中断控制器,还要进行其它设置。

外部中断:

  1. 设置中断源,让它能发出中断信号
  2. 设置中断控制器
  3. 设置CPU,CPSR的 I 位,是中断总开关
  4. 处理时要分辨中断源
  5. 处理完要清中断

汇编中的异常处理程序

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

定时器中断:

  1. 设置时钟
  2. 设置初值
  3. 加载初值,启动Timer
  4. 设置自动加载
  5. 中断相关
    对于定时器部分,还要参考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);	
}