ARM裸机 - 中断处理编程实战

本文详细介绍了嵌入式系统中断处理的关键概念,包括VIC寄存器的功能,如中断使能、禁止、模式设置和优先级管理。还展示了S5PV210处理器中断处理的编程实践,包括初始化中断控制器、设置中断向量地址、中断使能与禁止的实现。此外,解析了中断处理的流程,包括中断发生后的现场保护、中断服务例程的调用和中断恢复。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.控制中断的几个重要寄存器

VICnINTENABLE寄存器:负责相应的中断的使能。

VICnINTENCLEAR寄存器:负责相应的中断的禁止

这里的设计一共有2种:有些CPU是中断使能和禁止是一个寄存器位,写1就使能写0就进制(或者反过来写1就进制写0就使能)。

VICnINTSELECT寄存器:设置各个中断的模式为irq还是fiq,一般都设置成irq。irq是普通中断,fiq是快速中断。

VICnFIQSTATUS寄存器:中断状态寄存器,是只读的。当发生了中断时,硬件会自动将该寄存器的对应位置为1,表示中断发生了。

VICnVECTPRIORITY0~VICnVECTPRIORITY31:中断优先级设置寄存器,设置多个中断同时发生时先处理谁后处理谁的问题。一般来说高优先级的中断可以打断低优先级的中断,从而嵌套处理中断。

2.S5PV210中断处理的编程实践

-> 完善int.h头文件:(声明初始化的函数,定义寄存器,定义中断编号)

#ifndef __INT_H__
#define __INT_H__


void intc_init(void);
void intc_enable(unsigned long intnum);
void intc_disable(unsigned long intnum);
void intc_setvectaddr(unsigned long intnum, void (*handler)(void));
void intc_clearvectaddr(void);
unsigned long intc_getvicirqstatus(unsigned long ucontroller);
void irq_handler(void);
void IRQ_handle(void);
void system_init_exception(void);



 Interrupt
#define VIC0_BASE					(0xF2000000)
#define VIC1_BASE					(0xF2100000)
#define VIC2_BASE					(0xF2200000)
#define VIC3_BASE					(0xF2300000)

// VIC0
#define		VIC0IRQSTATUS			( *((volatile unsigned long *)(VIC0_BASE + 0x00)) )
#define		VIC0FIQSTATUS			( *((volatile unsigned long *)(VIC0_BASE + 0x04)) )
#define		VIC0INTSELECT			( *((volatile unsigned long *)(VIC0_BASE + 0x0c)) )
#define		VIC0INTENABLE			( *((volatile unsigned long *)(VIC0_BASE + 0x10)) )
#define		VIC0INTENCLEAR			( *((volatile unsigned long *)(VIC0_BASE + 0x14)) )
#define 	VIC0VECTADDR			(VIC0_BASE + 0x100)
#define 	VIC0ADDR				( *((volatile unsigned long *)(VIC0_BASE + 0xf00)) )

// VIC1
#define		VIC1IRQSTATUS			( *((volatile unsigned long *)(VIC1_BASE + 0x00)) )
#define		VIC1FIQSTATUS			( *((volatile unsigned long *)(VIC1_BASE + 0x04)) )
#define		VIC1INTSELECT			( *((volatile unsigned long *)(VIC1_BASE + 0x0c)) )
#define		VIC1INTENABLE			( *((volatile unsigned long *)(VIC1_BASE + 0x10)) )
#define		VIC1INTENCLEAR			( *((volatile unsigned long *)(VIC1_BASE + 0x14)) )
#define 	VIC1VECTADDR			(VIC1_BASE + 0x100)
#define 	VIC1ADDR				( *((volatile unsigned long *)(VIC1_BASE + 0xf00)) )

// VIC2
#define		VIC2IRQSTATUS			( *((volatile unsigned long *)(VIC2_BASE + 0x00)) )
#define		VIC2FIQSTATUS			( *((volatile unsigned long *)(VIC2_BASE + 0x04)) )
#define		VIC2INTSELECT			( *((volatile unsigned long *)(VIC2_BASE + 0x0c)) )
#define		VIC2INTENABLE			( *((volatile unsigned long *)(VIC2_BASE + 0x10)) )
#define		VIC2INTENCLEAR			( *((volatile unsigned long *)(VIC2_BASE + 0x14)) )
#define 		VIC2VECTADDR			(VIC2_BASE + 0x100)
#define 		VIC2ADDR				( *((volatile unsigned long *)(VIC2_BASE + 0xf00)) )

// VIC3
#define		VIC3IRQSTATUS			( *((volatile unsigned long *)(VIC3_BASE + 0x00)) )
#define	 	VIC3FIQSTATUS			( *((volatile unsigned long *)(VIC3_BASE + 0x04)) )
#define	 	VIC3INTSELECT			( *((volatile unsigned long *)(VIC3_BASE + 0x0c)) )
#define	 	VIC3INTENABLE			( *((volatile unsigned long *)(VIC3_BASE + 0x10)) )
#define	 	VIC3INTENCLEAR			( *((volatile unsigned long *)(VIC3_BASE + 0x14)) )
#define 		VIC3VECTADDR			(VIC3_BASE + 0x100)
#define 		VIC3ADDR				( *((volatile unsigned long *)(VIC3_BASE + 0xf00)) )


#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)

#define r_exception_reset		(*(volatile unsigned int *)exception_reset)
#define r_exception_undef		(*(volatile unsigned int *)exception_undef)
#define r_exception_sotf_int	(*(volatile unsigned int *)exception_sotf_int)
#define r_exception_prefetch	(*(volatile unsigned int *)exception_prefetch)
#define r_exception_data		(*(volatile unsigned int *)exception_data)
#define r_exception_irq			(*(volatile unsigned int *)exception_irq)
#define r_exception_fiq			(*(volatile unsigned int *)exception_fiq)



// 中断源编号
#define INT_LIMIT				(96)

//INT NUM - VIC0
#define NUM_EINT0				(0)
#define NUM_EINT1				(1)
#define NUM_EINT2				(2)
#define NUM_EINT3				(3)
#define NUM_EINT4				(4)
#define NUM_EINT5				(5)
#define NUM_EINT6				(6)
#define NUM_EINT7				(7)
#define NUM_EINT8				(8)
#define NUM_EINT9				(9)
#define NUM_EINT10				(10)
#define NUM_EINT11				(11)
#define NUM_EINT12				(12)
#define NUM_EINT13				(13)
#define NUM_EINT14				(14)
#define NUM_EINT15				(15)
#define NUM_EINT16_31			(16)
#define NUM_Reserved17			(17) 
#define NUM_MDMA				(18)
#define NUM_PDMA0				(19)
#define NUM_PDMA1				(20)
#define NUM_TIMER0				(21)
#define NUM_TIMER1				(22)
#define NUM_TIMER2				(23)
#define NUM_TIMER3				(24)
#define NUM_TIMER4				(25)
#define NUM_SYSTIMER			(26)
#define NUM_WDT					(27)
#define NUM_RTC_ALARM			(28)
#define NUM_RTC_TICK			(29)
#define NUM_GPIOINT				(30)
#define NUM_FIMC3				(31)

//INT NUM - VIC1
#define NUM_CORTEX0				(32+0)
#define NUM_CORTEX1				(32+1)
#define NUM_CORTEX2				(32+2)
#define NUM_CORTEX3				(32+3)
#define NUM_CORTEX4				(32+4)
#define NUM_IEM_APC				(32+5)
#define NUM_IEM_IEC				(32+6)
#define NUM_Reserved39			(32+7)
#define NUM_NFC					(32+8)
#define NUM_CFC					(32+9)
#define NUM_UART0				(32+10)
#define NUM_UART1				(32+11)
#define NUM_UART2				(32+12)
#define NUM_UART3				(32+13)
#define NUM_I2C					(32+14)
#define NUM_SPI0				(32+15)
#define NUM_SPI1				(32+16)
#define NUM_SPI2				(32+17)
#define NUM_AUDIO				(32+18)
#define NUM_I2C_PMIC			(32+19)
#define NUM_I2C_HDMI			(32+20)
#define NUM_HSIRX				(32+21)
#define NUM_HSITX				(32+22)
#define NUM_UHOST				(32+23)
#define NUM_OTG					(32+24)
#define NUM_MSM					(32+25)
#define NUM_HSMMC0				(32+26)
#define NUM_HSMMC1				(32+27)
#define NUM_HSMMC2				(32+28)
#define NUM_MIPI_CSI			(32+29)
#define NUM_MIPI_DSI			(32+30)
#define NUM_ONENAND_AUDI		(32+31)

//INT NUM - VIC2
#define NUM_LCD0				(64+0)
#define NUM_LCD1				(64+1)
#define NUM_LCD2				(64+2)
#define NUM_LCD3				(64+3)
#define NUM_ROTATOR				(64+4)
#define NUM_FIMC_A				(64+5)
#define NUM_FIMC_B				(64+6)
#define NUM_FIMC_C				(64+7)
#define NUM_JPEG				(64+8)
#define NUM_2D					(64+9)
#define NUM_3D					(64+10)
#define NUM_MIXER				(64+11)
#define NUM_HDMI				(64+12)
#define NUM_HDMI_I2C			(64+13)
#define NUM_MFC					(64+14)
#define NUM_TVENC				(64+15)
#define NUM_I2S0				(64+16)
#define NUM_I2S1				(64+17)
#define NUM_I2S2				(64+18)
#define NUM_AC97				(64+19)
#define NUM_PCM0				(64+20)
#define NUM_PCM1				(64+21)
#define NUM_SPDIF				(64+22)
#define NUM_ADC 				(64+23)
#define NUM_PENDN				(64+24)
#define NUM_KEYPAD				(64+25)
#define NUM_Reserved90			(64+26) 
#define NUM_HASH				(64+27) 
#define NUM_FEEDCTRL			(64+28) 
#define NUM_PCM2				(64+29)
#define NUM_SDM_IRQ				(64+30)
#define NUM_SMD_FIQ				(64+31)

//INT NUM - VIC3
#define NUM_IPC					(96+0)
#define NUM_HOSTIF				(96+1)
#define NUM_HSMMC3				(96+2)
#define NUM_CEC					(96+3)
#define NUM_TSI					(96+4)
#define NUM_MDNIE0				(96+5)
#define NUM_MDNIE1				(96+6)
#define NUM_MDNIE2				(96+7)
#define NUM_MDNIE3				(96+8)
#define NUM_ADC1				(96+9)
#define NUM_PENDN1				(96+10)
#define NUM_ALL					(200)



#endif

-> 中断控制器初始化,绑定异常向量表到异常处理程序

void reset_exception(void)
{
	printf("reset_exception.\n");
}

void undef_exception(void)
{
	printf("undef_exception.\n");
}

void sotf_int_exception(void)
{
	printf("sotf_int_exception.\n");
}

void prefetch_exception(void)
{
	printf("prefetch_exception.\n");
}

void data_exception(void)
{
	printf("data_exception.\n");
}

// 主要功能:绑定第一阶段异常向量表;禁止所有中断;选择所有中断类型为IRQ;
// 清除VICnADDR为0
void system_init_exception(void)
{
	// 第一阶段处理,绑定异常向量表
	r_exception_reset = (unsigned int)reset_exception;
	r_exception_undef = (unsigned int)undef_exception;
	r_exception_sotf_int = (unsigned int)sotf_int_exception;
	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)IRQ_handle;
	
	// 初始化中断控制器的基本寄存器
	intc_init();
}

-> intc_init()的函数展开(禁止所有中断源;选择所有中断类型为IRQ;清理VICnADDR寄存器为0)

// 清除需要处理的中断的中断处理函数的地址
void intc_clearvectaddr(void)
{
    // VICxADDR:当前正在处理的中断的中断处理函数的地址
    VIC0ADDR = 0;
    VIC1ADDR = 0;
    VIC2ADDR = 0;
    VIC3ADDR = 0;
}

// 初始化中断控制器
void intc_init(void)
{
    // 禁止所有中断
	// 为什么在中断初始化之初要禁止所有中断?
	// 因为中断一旦打开,因为外部或者硬件自己的原因产生中断后一定就会寻找isr
	// 而我们可能认为自己用不到这个中断就没有提供isr,这时它自动拿到的就是乱码
	// 则程序很可能跑飞,所以不用的中断一定要关掉。
	// 一般的做法是先全部关掉,然后再逐一打开自己感兴趣的中断。一旦打开就必须
	// 给这个中断提供相应的isr并绑定好。
    VIC0INTENCLEAR = 0xffffffff;
    VIC1INTENCLEAR = 0xffffffff;
    VIC2INTENCLEAR = 0xffffffff;
    VIC3INTENCLEAR = 0xffffffff;

    // 选择中断类型为IRQ
    VIC0INTSELECT = 0x0;
    VIC1INTSELECT = 0x0;
    VIC2INTSELECT = 0x0;
    VIC3INTSELECT = 0x0;

    // 清VICxADDR
    intc_clearvectaddr();
}

-> 中断的使能与禁止

思路是先根据中断号判断这个中断属于VIC几,然后在用中断源减去这个VIC的偏移量,得到这个中断号在本VIC中的偏移量,然后1<<x位,写入相应的VIC的INTENABLE/INTENCLEAR寄存器即可。

// 使能中断
// 通过传参的intnum来使能某个具体的中断源,中断号在int.h中定义,是物理中断号
void intc_enable(unsigned long intnum)
{
    unsigned long temp;
	// 确定intnum在哪个寄存器的哪一位
	// <32就是0~31,必然在VIC0
    if(intnum<32)
    {
        temp = VIC0INTENABLE;
        temp |= (1<<intnum);		// 如果是第一种设计则必须位操作,第二种设计可以
									// 直接写。
        VIC0INTENABLE = temp;
    }
    else if(intnum<64)
    {
        temp = VIC1INTENABLE;
        temp |= (1<<(intnum-32));
        VIC1INTENABLE = temp;
    }
    else if(intnum<96)
    {
        temp = VIC2INTENABLE;
        temp |= (1<<(intnum-64));
        VIC2INTENABLE = temp;
    }
    else if(intnum<NUM_ALL)
    {
        temp = VIC3INTENABLE;
        temp |= (1<<(intnum-96));
        VIC3INTENABLE = temp;
    }
    // NUM_ALL : enable all interrupt
    else
    {
        VIC0INTENABLE = 0xFFFFFFFF;
        VIC1INTENABLE = 0xFFFFFFFF;
        VIC2INTENABLE = 0xFFFFFFFF;
        VIC3INTENABLE = 0xFFFFFFFF;
    }

}

// 禁止中断
// 通过传参的intnum来禁止某个具体的中断源,中断号在int.h中定义,是物理中断号
void intc_disable(unsigned long intnum)
{
    unsigned long temp;

    if(intnum<32)
    {
        temp = VIC0INTENCLEAR;
        temp |= (1<<intnum);
        VIC0INTENCLEAR = temp;
    }
    else if(intnum<64)
    {
        temp = VIC1INTENCLEAR;
        temp |= (1<<(intnum-32));
        VIC1INTENCLEAR = temp;
    }
    else if(intnum<96)
    {
        temp = VIC2INTENCLEAR;
        temp |= (1<<(intnum-64));
        VIC2INTENCLEAR = temp;
    }
    else if(intnum<NUM_ALL)
    {
        temp = VIC3INTENCLEAR;
        temp |= (1<<(intnum-96));
        VIC3INTENCLEAR = temp;
    }
    // NUM_ALL : disable all interrupt
    else
    {
        VIC0INTENCLEAR = 0xFFFFFFFF;
        VIC1INTENCLEAR = 0xFFFFFFFF;
        VIC2INTENCLEAR = 0xFFFFFFFF;
        VIC3INTENCLEAR = 0xFFFFFFFF;
    }

    return;
}

-> 绑定自己实现的isr到VICnVECTADDR

搞清楚2个寄存器的区别:VICnVECTADDR和VICnADDR.

VICVECTADDR寄存器一共有4×32个,每个中断源都有一个VECTADDR寄存器,我们应该将自己为这个中断源写的isr地址丢到这个中断源对应的VECTADDR寄存器中即可。

// 绑定我们写的isr到VICnVECTADDR寄存器
// 绑定过之后我们就把isr地址交给硬件了,剩下的我们不用管了,硬件自己会处理
// 等发生相应中断的时候,我们直接到相应的VICnADDR中去取isr地址即可。
// 参数:intnum是int.h定义的物理中断号,handler是函数指针,就是我们写的isr

// VIC0VECTADDR定义为VIC0VECTADDR0寄存器的地址,就相当于是VIC0VECTADDR0~31这个
// 数组(这个数组就是一个函数指针数组)的首地址,然后具体计算每一个中断的时候
// 只需要首地址+偏移量即可。
void intc_setvectaddr(unsigned long intnum, void (*handler)(void))
{
    //VIC0
    if(intnum<32)
    {
        *( (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;
}

-> 真正的中断处理程序如何获取isr

当发生中断时,硬件会自动把相应中断源的isr地址从VICnVECTADDR寄存器中推入VICnADDR寄存器中,所以我们第二阶段的第二阶段isr_handler中,只需要到相应的VICnADDR中去拿出isr地址,调用执行即可。

// 通过读取VICnIRQSTATUS寄存器,判断其中哪个有一位为1,来得知哪个VIC发生中断了
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;
}


// 真正的中断处理程序。意思就是说这里只考虑中断处理,不考虑保护/恢复现场
void irq_handler(void)
{
	printf("irq_handler.\n");
	// SoC支持很多个(在低端CPU例如2440中有30多个,在210中有100多个)中断
	// 这么多中断irq在第一个阶段走的是一条路,都会进入到irq_handler来
	// 我们在irq_handler中要去区分究竟是哪个中断发生了,然后再去调用该中断
	// 对应的isr。
	
	
	// 虽然硬件已经自动帮我们把isr放入了VICnADDR中,但是因为有4个,所以我们必须
	// 先去软件的检查出来到底哪个VIC中断了,也就是说isr到底在哪个VICADDR寄存器中
	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
        if(intc_getvicirqstatus(i) != 0)
        {
            isr = (void (*)(void)) vicaddr[i];
            break;
        }
    }
    (*isr)();		// 通过函数指针来调用函数
}

整个中断的流程梳理:
整个中断的工作分为2部分:
第一部分是我们为中断响应而做的预备工作:
-> 初始化中断控制器
-> 绑定写好的isr到中断控制器
-> 相应中断的所有条件使能
第二部分是当硬件产生中断后如何自动执行isr:
-> 经过异常向量表跳转入IRQ/FIQ的入口
-> 做中断现场保护(在start.S中),然后跳入isr_handler
-> 在isr_handler中先去搞清楚是哪个VIC中断了,然后直接去这个VIC的ADDR
寄存器中取isr来执行即可。
-> isr执行完,中断现场恢复,直接返回继续做常规任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式_笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值