S5PV210 外部中断 -朱有鹏老师学习笔记

S5PV210的中断实现机制

SOC中断机制的实现靠的是异常向量表。ARM处理器有7种工作模式,除了用户模式和系统模式外的五种模式都为异常模式,其中就包括中断模式(irq)和快速中断模式(firq),也就是说中断时异常的一种。异常向量表就是一个地址表,表项是异常的地址。当异常发生时,PC跳转到对应异常的地址执行相应的处理。中断作为一种异常,当中断发生时,PC跳转到中断模式对应的地址处,执行相应的处理。
中断处理可以分为两个阶段。异常向量表跳转为第一阶段,第二阶段为进入中断处理程序之后的部分。
第一个阶段的实现依赖于CPU提供的异常向量表机制。异常发生后响应异常,保存/恢复现场,跳转的相应的异常服务程序。
中断源众多,包括外部中断,定时器中断,ADC中断,DMA中断等等。在第二阶段识别时哪一个中断触发的中断,然后执行相应的中断服务程序,这部分工作主要是中断控制器完成的。

中断源

以外部中断为例,配置GPIO为中断模式
void KeyInitInterrupt(void)
{
    //对应外部gpio模式设置
    GPH0CON = (0xF << 8);     //GPH0_2 设置为外部中断模式

    //外部中断出发模式   双
    EXT_INT_0_CON &= ~(0xF << 8);
    EXT_INT_0_CON |= (2<<8);        //下降沿触发

    //允许外部中断
    EXT_INT_0_MASK &= ~(1 << 2);   //允许外部中断 2

    //清外部中断挂起位  清除写1, 这有点反数据手册~
    EXT_INT_0_PEND |= (1 << 2);
}

为中断响应做的一些准备

绑定异常向量表并初始化中断控制器

之前说到,异常发生时,CPU基于异常向量表进行跳转至相应的异常位置处执行。裸机程序在IRAM执行,IRAM为异常向量表划分了一片空间,可以在IROM_APPLICATION里查看,起始地址为0xD0037400。

在这里插入图片描述

绑定异常向量表就是向异常向量表内写对应处理的函数名。函数名的本质就是函数执行首地址。这样异常发生时就回去执行对应的函数。

#define exception_vector_table_base		0xD0037400
#define exception_reset					(exception_vector_table_base + 0x00)
#define exception_undef					(exception_vector_table_base + 0x04)
#define exception_sotf_int				(exception_vector_table_base + 0x08)
#define exception_prefetch				(exception_vector_table_base + 0x0C)
#define exception_data					(exception_vector_table_base + 0x10)
#define exception_irq					(exception_vector_table_base + 0x18)
#define exception_fiq					(exception_vector_table_base + 0x1C)

void system_init_exception(void)
{
    //绑定异常向量表
    r_exception_reset = (unsigned int)reset_exception;		
    r_exception_undef = (unsigned int)udef_exception;		
    r_exception_sotf_int = (unsigned int)soft_int;   //软中断	
    r_exception_prefetch = (unsigned int)prefetch_exception;
    r_exception_data = (unsigned int)data_exception;
    r_exception_irq = (unsigned int)IRQ_handle;
    r_exception_fiq = (unsigned int)FIRQ_handle;

    //初始化中断控制器的基本寄存器
    intc_init();
}

绑定异常向量表后,初始化中断控制器。

void intc_init(void)
{
    //失能中断
    VIC0INTENCLEAR = 0xFFFFFFFF;
    VIC1INTENCLEAR = 0xFFFFFFFF;
    VIC2INTENCLEAR= 0xFFFFFFFF;
    VIC3INTENCLEAR= 0xFFFFFFFF;
    // 选择中断类型为IRQ
    VIC0INTSELECT = 0x00;
    VIC1INTSELECT = 0x00;
    VIC2INTSELECT = 0x00;
    VIC2INTSELECT = 0x00;

    //清VICxADDR
    VIC0ADDR = 0;
    VIC1ADDR = 0;
    VIC2ADDR = 0;
    VIC3ADDR = 0;
}

中断控制器的配置还剩下最重要的一步,就是如何识别哪个中断发生了并跳转到相应的中断服务程序。210给出的策略如下:
210支持的中断个数为128个(包括复用)。由四个中断控制器分别控制 VIC0~VIC3。210为每个中断控制器提供了32个中断服务程序地址寄存器VICnVECTADDRm(n(0-3), m(0-31));用户预先把对应的中断服务程序地址(即中断服务函数的函数名)写到对应的地址寄存器。当中断发生时,硬件会自动选择发生了哪个中断,并把对应的中断服务程序地址返回到VICnADDR寄存器。用户只需要读这个寄存器的内容然后执行就可以了。配置如下:

/**
 * @brief 注册中断服务程序到VICnVECTADDDR
 * @param intnum: 中断号
 *        hnadler: isr 地址
*/
void intc_setVcctAddr(unsigned long intnum, void (*handler)(void))
{
    //VIC 0
    if(intnum<32)
    {
        *((volatile unsigned int *)(VIC0VECTADDR+4*(intnum-0))) = (unsigned long)handler;
    }
    //VIC 1
    else if(intnum<64)
    {
        *((volatile unsigned int *)(VIC1VECTADDR+4*(intnum-32))) = (unsigned long)handler;
    }
    //VIC 2
    else if(intnum<96)
    {
        *((volatile unsigned int *)(VIC2VECTADDR+4*(intnum-64))) = (unsigned long)handler;
    }
    //VIC 3
    else if(intnum<128)
    {
        *((volatile unsigned int *)(VIC3VECTADDR+4*(intnum-96))) = (unsigned long)handler;
    }
    else
    {
        // 中断号error
    }
    
    return;
}

使能中断

/**
 * @brief 使能中断
 * @param intnum - 中断号
 */
void intc_enable(unsigned long intnum)
{
    unsigned long temp;

    if (intnum < 32)
    {
        temp = VIC0INTENABLE;
        temp |= (1 << intnum);
        VIC0INTENABLE = temp;
    }
    else if (intnum < 64)
    {
        temp = VIC0INTENABLE;
        temp |= (1 << (intnum-32));
        VIC1INTENABLE = temp;
    }
    else if (intnum < 96)
    {
        temp = VIC0INTENABLE;
        temp |= (1 << (intnum-64));
        VIC2INTENABLE = temp;
    }
    else if (intnum < NUM_ALL)
    {
        temp = VIC0INTENABLE;
        temp |= (1 << (intnum-96));
        VIC3INTENABLE = temp;
    }
    // num_all :使能所有中断  这为啥要关闭所有中断?
    else
    {
        VIC0INTENCLEAR = 0xFFFFFFFF;
        VIC1INTENCLEAR = 0xFFFFFFFF;
        VIC2INTENCLEAR = 0xFFFFFFFF;
        VIC3INTENCLEAR = 0xFFFFFFFF;
    }

    return;
}

到这中断控制寄存器的配置基本配置完了。到这已经完成的功能包括:
1.中断发生时,CPU会跳转到中断处理。这是由我们绑定的异常向量表决定的。
2.中断发生时,硬件会自动识别哪个中断发生了,并自动返回相应的中断服务程序入口地址,这是通过中断控制器的配置实现的。
现在中断发生前的准备工作已基本完成,下面开始中断发生后,如何处理。

中断发生后的处理

中断发生后,CPU会从SVC模式跳转到IRQ模式,所以首先要设置IRQ模式的栈;中断发生后会根据异常向量表跳转到IRQ_handle,在IRQ_handle里要保存现场,跳转到中断服务程序irq_handler,然后恢现场。
#define WTCON		0xE2700000

#define SVC_STACK	0xd0037d80
#define IRQ_STACK	0xd0037f80

.global _start					
.global IRQ_handle
_start:
	// 第1步:关看门狗(向WTCON的bit5写入0即可)
	ldr r0, =WTCON
	ldr r1, =0x0
	str r1, [r0]
	
	// 第2步:初始化时钟
	bl clock_init
	
	// 第3步:设置SVC栈
	ldr sp, =SVC_STACK
	
	// 第4步:开/关icache
	mrc p15,0,r0,c1,c0,0;			// 读出cp15的c1到r0中
	//bic r0, r0, #(1<<12)			// bit12 置0  关icache
	orr r0, r0, #(1<<12)			// bit12 置1  开icache
	mcr p15,0,r0,c1,c0,0;

clean_bss:
	ldr r0, = __bss_start
	ldr r1, = _end
	mov r2, #0
	cmp r0, r1
	bne clean_loop

clean_loop:
	str r2, [r0], #4
	cmp r0, r1
	bne clean_loop

	bl main
	
// 汇编最后的这个死循环不能丢
	b .

IRQ_handle:
	// 设置IRQ模式下的栈
	ldr sp, =IRQ_STACK
	// 保存LR
	// 因为ARM有流水线,所以PC的值会比真正执行的代码+8,
	sub lr, lr, #4
	// 保存r0-r12和lr到irq模式下的栈上面
	stmfd sp!, {r0-r12, lr}
	// 在此调用真正的isr来处理中断
	bl irq_handler
	// 处理完成开始恢复现场,其实就是做中断返回,关键是将r0-r12,pc,cpsr一起回复
	ldmfd sp!, {r0-r12, pc}^

在中断服务程序中,去读取VICnADDR的值,其值就是中断源对应服务程序的首地址,然后执行

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++)
    {
        if(intc_getVicIrqQuestion(i) != 0)
        {
            isr = (void (*)(void)) vicaddr[i];
            break;
        }
    }
    (*isr)(); 
}
//中断处理函数
void isrEint2(void)
{
    printf("isr is left");

    EXT_INT_0_PEND |= (1 << 2);  //中断发生后,清挂起
    intc_clearvectaddr();
}

void main(void)
{
    //clock_init();
    uart0_init();

    KeyInitInterrupt();

    //初始化中断控制器
    system_init_exception();

    printf("----------------int--------------------");
    //绑定isr到中断控制器硬件
    intc_setVcctAddr(KEY_EINT2, isrEint2);    


    //使能中断
    intc_enable(KEY_EINT2);

    while(1)
    {
        printf("a ");
        delay();
    }
}

现象如下
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值