S5PV210中断系统 学习笔记

以按键为例:
按键按下,中断发生,经过异常向量表跳转到IRQ_handle函数,IRQ_handle 在汇编启动代码start.s中定义。

IRQ_handle函数:

IRQ_handle:
	// 设置IRQ模式下的栈
	ldr sp, =IRQ_STACK
	// 保存LR
	// 因为ARM有流水线,所以PC的值会比真正执行的代码+8,
	sub lr, lr, #4
	// 保存r0-r12和lr到irq模式下的栈上面
	stmfd sp!, {r0-r12, lr}
	// 调用irq_handler函数(这是真正处理中断程序的地方)
	bl irq_handler
	// 现场恢复
	ldmfd sp!, {r0-r12, pc}

IRQ_handle做了四件事:
1.设置IRQ模式下的栈
2.现场保护:保存下一个要执行的地址到LR以便返回,保存r0-r12到栈,保存CPSR到SPSR(自动)
3.转到irq_handler 执行ISR(中断服务程序)
4.现场恢复:LR中保存的地址给PC,栈中保存的r0–r12覆盖当r0–r12,SPSR保存的值恢复到CPSR(自动)

irq_handler函数:Soc支持很多中断,所以中断irq都会到这个函数,要在该函数里判断哪个中断发生了,然后调用ISR。

VICnADDR寄存器:发生中断后,硬件自动将该中断的IRQ地址放到这个寄存器。
VICVECTADDR寄存器:存放ISR地址,一共4*32个,每个中断源都有一个VICVECTADDR寄存器。

void irq_handler(void)
{
	unsigned long vicaddr[4] = {VIC0ADDR,VIC1ADDR,VIC2ADDR,VIC3ADDR};
    int i=0;
    void (*isr)(void) = NULL;
    
    for(i=0; i<4; i++)
    {
		// 发生一个中断时,4个VIC中有3个是全0,1个的其中一位不是0。通过判断VICnIRQSTATUS的值确定那个VIC发生了中断
        if(intc_getvicirqstatus(i) != 0) //
        {
            isr = (void (*)(void)) vicaddr[i];//发生中断时硬件自动将ISR首地址放入VICnADDR寄存器,此时isr函数指针指向了ISR
            break;
        }
    }
    (*isr)();		// 通过函数指针来调用函数
}

intc_getvicirqstatus函数:辅助型函数,返回VICnIRQSTATUS寄存器的值供irq_handler函数使用

//仅给出VIC0IRQSTATUS参考
#define        VIC0_BASE					    (0xF2000000)
#define		VIC0IRQSTATUS			( *((volatile unsigned long *)(VIC0_BASE + 0x00)) )
unsigned long intc_getvicirqstatus(unsigned long ucontroller)
{
    if(ucontroller == 0)
        return	VIC0IRQSTATUS;
    else if(ucontroller == 1)
        return 	VIC1IRQSTATUS;
    else if(ucontroller == 2)
        return 	VIC2IRQSTATUS;
    else if(ucontroller == 3)
        return 	VIC3IRQSTATUS;
    else
    {}
    return 0;
}

绑定函数:绑定自己写的ISR到VICnVECTADDR寄存器,绑定过之后我们就把isr地址交给硬件了,剩下的我们不用管了,硬件自己会处理,等发生相应中断的时候,我们直接到相应的VICnADDR中去取isr地址即可(上面 irq_handler函数已经实现)。

//                        中断源编号               自己写的IRQ函数
void intc_setvectaddr(unsigned long intnum, void (*handler)(void))
{
    //先确定该中断在哪个分区
    //VIC0
    if(intnum<32)
    {
     //然后把IRQ函数首地址 赋给 VICnVECTADDR寄存器 
        *( (volatile unsigned long *)(VIC0VECTADDR + 4*(intnum-0)) ) = (unsigned)handler;
    }
    //VIC1
    else if(intnum<64)
    {
        *( (volatile unsigned long *)(VIC1VECTADDR + 4*(intnum-32)) ) = (unsigned)handler;
    }
    //VIC2
    else if(intnum<96)
    {
        *( (volatile unsigned long *)(VIC2VECTADDR + 4*(intnum-64)) ) = (unsigned)handler;
    }
    //VIC3
    else
    {
        *( (volatile unsigned long *)(VIC3VECTADDR + 4*(intnum-96)) ) = (unsigned)handler;
    }
    return;
}

使用:

#define NUM_EINT2				(2)                      //硬件设定的中断源编号
#define KEY_DOWN		NUM_EINT2              //以按键为例,该按键对应的外部中断查原理图即可
intc_setvectaddr(KEY_DOWN, IRQ); //绑定IRQ到VICnVECTADDR寄存器

其他重要的 中断相关寄存器
VICnINTENCLEAR 禁止中断源
VIC0INTENABLE 使能中断源
VICnINTSELECT 选择中断类型,比如IRQ中断
VICnADDR 清理这个寄存器,以便硬件将ISR地址放进去(不清貌似也可)

使用按键中断还要初始化 外部中断 的相关寄存器,步骤如下:
1.查找原理图,确定按键的GPIO引脚。
2.GPxxCON 设置GPxxCON寄存器相应位为外部中断模式
3.EXT_INT_n_CON 设置触发方式(电平,边沿触发)
4.EXT_INT_n_PEND 清除中断挂起寄存器,(来中断时硬件会自动将相应位置1,执行完IRQ需要手动置0)
5.EXT_INT_n_MASK 使能相应的外部中断
6.写IRQ函数

以上为学习朱有鹏老师的课程所作笔记

你可能感兴趣的:(YT的学习笔记)