【OK6410裸机程序】按键中断

1. 中断也是一种异常,可以出发FIQ或IRQ异常。

OK6410提供64个中断源,其中INT_EINT0~4是由外部信号触发的中断,其它都是由芯片内部信号触发的中断。

【OK6410裸机程序】按键中断_第1张图片

2. 外部触发中断,OK6410提供127个外部中断源,共划分为10组,其中第0组最受重视,在VIC中分配的5个外部中断源中的分配如下:

Group0的IO口占用的VIC中的4个中断源,而Group1--Group9共占用VIC中的一个中断源。

INT_EINT0--Group0[0--3]

INT_EINT1--Group0[4--11]

INT_EINT2--Group0[12--19]

INT_EINT3--Group0[20--27]

INT_EINT4--Group1~Group9

【OK6410裸机程序】按键中断_第2张图片

3. OK6410开发板中按键引脚分配

【OK6410裸机程序】按键中断_第3张图片

4. 中断配置过程

4.1  GPIO配置,先配置为中断功能,再配置中断产生何种信号才会被捕获。主要是五种,低电平,高电平,上升沿,下降沿或者两者均可, 第0组用EINT0CON0/EINT0CON1两个寄存器来设定。

	/* 配置GPIO引脚为中断引脚 */
	/* GPN0~5 设为中断引脚 */
	GPNCON &= ~(0xfff);
	GPNCON |= 0xaaa;

	/* 设置中断触发方式为: 双边沿触发 */
	EINT0CON0 &= ~(0xfff);
	EINT0CON0 |= 0x777;
4.2 使能中断

中断开关分为3级,EINTxMask是临时性关闭中断,VICxINTENABLE是VIC控制器中的中断开关,CPSR中的I/F位是中断总开关。

5. 中断处理函数

IRQ中断处理发生后硬件完成以下工作,注意PC的跳转,当VE值不同时跳转到的地址不同,由此产生两种不同的中断处理方式,向量中断和非向量中断。

        mrc p15,0,r0,c1,c0,0
        orr r0,r0,#(1<<24)   @ SET VE=1
        mcr p15,0,r0,c1,c0,0

【OK6410裸机程序】按键中断_第4张图片

5.1 非向量中断(VE=0)

PC跳转到固定的地址去执行中断服务函数(ISR)。再判断具体是哪个中断源产生的中断,然后再调用具体的处理函数。

注意在S3C6410用32个地址连续的寄存器组成两个寄存器数组,用来保存VIC中对应的中断源的ISR入口函数地址。首地址分别是0x71200100和0x71300100可以象指针数组一样来操作它们,数组的下标就是中断号。

【OK6410裸机程序】按键中断_第5张图片

处理某个中断函数时,对应的VICxVECTADDR会自动拷贝到VICxADDRESS寄存器中,所以读取VICxVECTADDR就可以得到ISR的入口地址。

写任意值到VICxVECTADDR中可以清除当前中断。

【OK6410裸机程序】按键中断_第6张图片


5.2 向量中断(VE=1)

当VE=1时,IRQ发生后,PC直接跳转至对应的VICxVECTADDR指定的ISR入口地址执行。

5.3 区别

非向量中断由于跳转到固定地址0x00000018,所以保存现场/恢复现场的工作都在0x00000018地址的代码中执行。

fiq:
	/* 1. 保存现场 */
	ldr sp, =0x53000000
	sub lr, lr, #4
	stmdb sp!, {r0-r7, lr}  /* lr就是swi的下一条指令地址 */

	/* 2. 处理异常 */
	bl do_fiq
	/* 3. 恢复现场 */
	ldmia sp!, {r0-r7, pc}^  /* ^表示把spsr恢复到cpsr */

void do_irq(void)
{
	int i = 0;

	void (*the_isr)(void);

	if (VIC0IRQSTATUS)
	{
		the_isr = VIC0ADDRESS;
			
		/* 2.1 分辨是哪个中断 */
		/* 2.2 调用它的处理函数 */	
		/* 2.3 清中断 */	

		the_isr();
	
		EINT0PEND   = 0x3f;  /* 清中断 */
		VIC0ADDRESS = 0;
	}
}

向量中断直接跳转到对应ISR中,所以保存现场/恢复现场的工作在每个VICxVECTADDR指定的函数中都要实现。只需设置好VICxVECTADDR,

IRQ发生后,自动跳转执行函数eint0_3_irq或者eint4_11_irq。

void irq_init(void)
{
	/* 配置GPIO引脚为中断引脚 */
	/* GPN0~5 设为中断引脚 */
	GPNCON &= ~(0xfff);
	GPNCON |= 0xaaa;

	/* 设置中断触发方式为: 双边沿触发 */
	EINT0CON0 &= ~(0xfff);
	EINT0CON0 |= 0x777;

	/* 使能中断 */
	EINT0MASK &= ~(0x3f);


	//VIC0INTSELECT |=0x20; /*key1~4 FIQ, key5~6 IRQ*/
	//VIC0INTENABLE |= (0x20);
	/* 在中断控制器里使能这些中断 */
	VIC0INTENABLE |= (0x03); /* bit0: eint0~3, bit1: eint4~11 */ 

	VIC0VECTADDR0 = eint0_3_irq; /* 用户按下key时,CPU就会自动的将VIC0VECTADDR0的值赋给VIC0ADDRESS并跳转到这个地址去执 */  
	VIC0VECTADDR1 = eint4_11_irq;
	
}

void eint0_3_irq(void)
{
	int i;
    __asm__( 
    
    "sub lr, lr, #4\n"  
    "stmfd sp!, {r0-r12, lr}\n"       
    : 
    : 
  );	
	printf("auto eint0_3_irq\n\r");  /* K1~K4 */
	for (i = 0; i < 4; i ++)
	{
		if (EINT0PEND & (1<


参考: 6410中断控制详解


你可能感兴趣的:(ARM-Linux)