嵌入式linux-ARM体系结构及接口技术,ARM中断机制,外部中断的配置,协处理器指令修改异常向量表起始地址,清中断标志

1,ARM中断机制

1.1,中断硬件机制

在这里插入图片描述

1.2,中断过程

在这里插入图片描述

1.3,中断源

Exynos4412中断控制器包含160个中断控制源,分三类分别是:
用于CPU之间通信的SGI
(Software Generated Interrupt)
专用于特定CPU核的PPI
(Private Peripheral Interrupt)
被多个CPU核共享的SPI
(Shared Peripheral Interrupt)

1.4,中断控制器

中断源分发给不同的CPU.
每个中断都有一个唯一对应的ID号,当中断发生时,该ID号会写入一个特定的寄存器。
中断处理程序可以读取该寄存器来决定该调用哪个具体的中断处理函数。
在这里插入图片描述

2,外部中断的配置

2.1,查看原理图

在这里插入图片描述
在这里插入图片描述

可以看到按键k2,挂载在GPX1_1引脚上,对应XEINT9

2.2,外部,配置芯片管脚工作模式

2.2.1,配置I/O口为中断模式

在这里插入图片描述

在这里插入图片描述

GPX1CON = (GPX1CON & ~(0x0f << 4) | (0x0f << 4));		// 设置管脚为中断模式   

2.2.2,配置中断为下降沿触发

在这里插入图片描述
在这里插入图片描述

EXT_INT41_CON = (EXT_INT41_CON & ~(0X07 << 4)) | (0X02 << 4);		//设置中断触发方式为下降沿

2.2.3,使能外部中断

在这里插入图片描述
在这里插入图片描述

EXT_INT41_MASK = (EXT_INT41_MASK & ~(0X01 << 1)) | (0X00 << 1);		//管脚中断使能(一关)

2.3,内部,设置功能模块(中断控制模块 GIC)

2.3.1,查GIC中断表,找出对应的中断端口号和ID号

在这里插入图片描述
在这里插入图片描述
XEINT9对应的中断端口号是25,ID号是57

2.3.2,设置GIC中断使能

在这里插入图片描述
在这里插入图片描述

ICDISER1_CPU0 = ICDISER1_CPU0 | (0x01 << 25); 	//使能对应中断(二关) 

2.3.3,分发配置

在这里插入图片描述
在这里插入图片描述

根据样例选用默认设置

//#define  ICDIPTR17_CPU0 (*(volatile unsigned int *)0x10490844) 	//分发配置,根据样例选用默认设置
#define  ICDIPTR14_CPU0 (*(volatile unsigned int *)0x10490838) 	//分发配置,根据样例选用默认设置
//ICDIPTR17_CPU0 = 0x01010101;
ICDIPTR14_CPU0 = 0x01010101;

ICDIPTR17_CPU0 = 0x01010101; //分发器中断使能 (三关),根据样例选用默认设置

2.3.4,分发总使能

在这里插入图片描述

ICDDCR = ICDDCR | 0X01;		//分发器中断使能 (三关)

2.3.5,CPU外部中断借口使能

在这里插入图片描述

ICCICR_CPU0 = ICCICR_CPU0 | 0x01;		//CPU接口中断使能(四关)

2.3.6,设置CPU的中断优先级门限值

在这里插入图片描述

ICCPMR_CPU0 = 0xff;		//设置CPU的中断优先级门限值,当前为最低,所有的中断都可以处理     

3,中断处理

3.1,协处理器指令修改异常向量表起始地址

  1. CPU规定的异常向量表的地址是从0x00开始的,其中中断异常的地址是0x18
  2. 但是想开发版中下载程序的时候我们会制定一个起始地址,这样异常向量表的起始地址就不是0x00了
  3. 所以,需要重新制定模拟的异常向量表的起始地址
MRC Pn,op1,Rd,CRn,CRm,op2  //mrc  p15,0,r0,c1,c0,0 读入cp15的c1寄存器的内容到r0中
MCR Pn,op1,Rd,CRn,CRm,op2  //mcr  p15,0,r0,c1,c0,0 写r0内容到cp15的c1寄存器中

通过协处理器设置异常向量表的起始地址为0x40008000

@ set Vector Base Address 为0x40008000	
ldr      r0,=0x40008000
mcr    p15,0,r0,c12,c0,0

在这里插入图片描述

3.2,异常向量表

_start: b       start_code
ldr     pc, _undefined_instruction
ldr     pc, _software_interrupt
ldr     pc, _prefetch_abort
ldr     pc, _data_abort
ldr     pc, _not_used
ldr     pc, _irq
ldr     pc, _fiq
_undefined_instruction: .word _undefined_instruction
_software_interrupt:    .word _software_interrupt
_prefetch_abort:        .word _prefetch_abort
_data_abort:            .word _data_abort
_not_used:              .word _not_used
_irq:                   .word irq_handler 
_fiq:                   .word _fiq
irq_handler:
	sub  lr,lr,#4 
	stmfd sp!,{r0-r12,lr}  @保护现场
	bl	do_irq
irq_handler_end:
	ldmfd sp!,{r0-r12,pc}^ @恢复现场

软中断SWI中,lr既是返回地址,中断IRQ中lr-4才是返回地址

3.3,中断处理函数

3.3.1,获取中断ID号

在这里插入图片描述

int irq_num;
irq_num = ICCIAR_CPU0 & 0X3ff; 	//获取中断ID号

3.3.2,清GPIO的中断标志

在这里插入图片描述

EXT_INT41_PEND = EXT_INT41_PEND | (0X01 << 1); 		//清GPX1_1中断标志

3.3.3,清GCI中断标志

在这里插入图片描述
在这里插入图片描述

ICDICPR1_CPU0 = ICDICPR1_CPU0 | (0x01 << 25);  		//清GCI中GPX1_1的中断标志

3.3.4,结束中断

在这里插入图片描述

ICCEOIR_CPU0 = (ICCEOIR_CPU0 & ~0x3ff) | irq_num;   	//结束对应的中断

3.3.5,中断处理函数

void do_irq()
{
/* 获取中断ID号,并根据不同的中断号做相应的处理 */
	int irq_num;
	irq_num = ICCIAR_CPU0 & 0X3ff; 	//获取中断ID号
	switch(irq_num)
	{
		case 57:
			uart2_putc('k');
			uart2_putc('2');
			EXT_INT41_PEND = EXT_INT41_PEND | (0X01 << 1); 		//清GPX1_1中断标志
			ICDICPR1_CPU0 = ICDICPR1_CPU0 | (0x01 << 25);  		//清GCI中GPX1_1的中断标志
			break;
		default:
			uart2_putc('d');
	}
	 /*结束中断  将处理完成的中断ID号写入该寄存器,则表示相应的中断处理完成*/
	ICCEOIR_CPU0 = (ICCEOIR_CPU0 & ~0x3ff) | irq_num;   	//结束对应的中断
}

4,例

4.1,start.S

    .global  delay1s 
    .text     
    .global _start
_start:
		b		reset                        @0x00
		ldr		pc,_undefined_instruction  @0x04
		ldr		pc,_software_interrupt     
		ldr		pc,_prefetch_abort
		ldr		pc,_data_abort
		ldr		pc,_not_used
		ldr		pc,_irq
		ldr		pc,_fiq

_undefined_instruction: .word  _undefined_instruction
_software_interrupt:	.word  _software_interrupt
_prefetch_abort:		.word  _prefetch_abort
_data_abort:			.word  _data_abort
_not_used:				.word  _not_used
_irq:					.word  irq_handler
_fiq:					.word  _fiq

reset: 
	ldr	r0,=0x40008000      @设置异常向量表的启始地址为 0x40008000
	mcr	p15,0,r0,c12,c0,0		@ Vector Base Address Register

init_stack:
	ldr		r0,stacktop         /*get stack top pointer*/

	/********svc mode stack********/
		mov		sp,r0
		sub		r0,#128*4          /*512 byte  for irq mode of stack*/
	/****irq mode stack**/
		msr		cpsr,#0xd2
		mov		sp,r0
		sub		r0,#128*4          /*512 byte  for irq mode of stack*/
	/***fiq mode stack***/
		msr 	cpsr,#0xd1
		mov		sp,r0
		sub		r0,#0
	/***abort mode stack***/
		msr		cpsr,#0xd7
		mov		sp,r0
		sub		r0,#0
	/***undefine mode stack***/
		msr		cpsr,#0xdb
		mov		sp,r0
		sub		r0,#0
   /*** sys mode and usr mode stack ***/
		msr		cpsr,#0x10
		mov		sp,r0             /*1024 byte  for user mode of stack*/

		b		main    /* 跳转到c程序的main函数  */

irq_handler:
	sub lr,lr,#4 	@软中断SWI中,lr既是返回地址,中断IRQ中lr-4才是返回地址
	stmfd sp!,{r0-r12,lr} 	@入栈保护现场
	bl do_irq 	@调用C函数
irq_handler_end:
	ldmfd sp!,{r0-r12,pc}^ 	@出栈恢复现场

delay1s:
     ldr      r4,=0x1ffffff   
delay1s_loop:
     sub    r4,r4,#1
     cmp   r4,#0         
     bne    delay1s_loop
     mov   pc,lr	

	.align	4

	/****  swi_interrupt handler  ****/

stacktop:    .word 		stack+4*512

.data

stack:	
  .space  4*512
.end

4.2,main.c

#define GPA1CON 		(*(volatile unsigned int *)0x11400020) 	//volatile 确保本条指令不会因编译器的优化而省略
#define ULCON2 			(*(volatile unsigned int *)0x13820000) 	//串口2线控:数据位,停止位,奇偶校验位
#define UCON2 			(*(volatile unsigned int *)0x13820004) 	//串口2读取控制:串口读的方式(如:轮询/中断等),写的方式
#define UBRDIV2 		(*(volatile unsigned int *)0x13820028) 	//串口2波特率设置
#define UFRACVAL2 		(*(volatile unsigned int *)0x1382002c) 	//串口2波特率设置
#define UTXH2 			(*(volatile unsigned int *)0x13820020) 	//串口2发送缓存器
#define URXH2 			(*(volatile unsigned int *)0x13820024) 	//串口2接受缓存器
#define UTRSTAT2 		(*(volatile unsigned int *)0x13820010) 	//串口2状态寄存器

#define GPA0CON 		(*(volatile unsigned int *)0x11400000) 	//volatile 确保本条指令不会因编译器的优化而省略
#define ULCON0 			(*(volatile unsigned int *)0x13800000) 	//串口0线控:数据位,停止位,奇偶校验位
#define UCON0 			(*(volatile unsigned int *)0x13800004) 	//串口0读取控制:串口读的方式(如:轮询/中断等),写的方式
#define UBRDIV0 		(*(volatile unsigned int *)0x13800028) 	//串口0波特率设置
#define UFRACVAL0 		(*(volatile unsigned int *)0x1380002c) 	//串口0波特率设置
#define UTXH0 			(*(volatile unsigned int *)0x13800020) 	//串口0发送缓存器
#define URXH0 			(*(volatile unsigned int *)0x13800024) 	//串口0接受缓存器
#define UTRSTAT0 		(*(volatile unsigned int *)0x13800010) 	//串口0状态寄存器

#define GPX1CON 		(*(volatile unsigned int *)0x11000C20) 	//
#define EXT_INT41_CON 	(*(volatile unsigned int *)0x11000E04) 	//外部中断1触发方式
#define  EXT_INT41_MASK	(*(volatile unsigned int *)0x11000f04) 	//使能外部中断
#define  ICDISER1_CPU0	(*(volatile unsigned int *)0x10490104) 	//GIC中断使能
//#define  ICDIPTR17_CPU0 (*(volatile unsigned int *)0x10490844) 	//分发配置,根据样例选用默认设置
#define  ICDIPTR14_CPU0 (*(volatile unsigned int *)0x10490838) 	//分发配置,根据样例选用默认设置
#define  ICDDCR 		(*(volatile unsigned int *)0x10490000) 	//全局外部中断使能
#define  ICCICR_CPU0 	(*(volatile unsigned int *)0x10480000) 	//CPU外部中断接口使能
#define  ICCPMR_CPU0 	(*(volatile unsigned int *)0x10480004) 	//优先级设定
#define  ICCIAR_CPU0 	(*(volatile unsigned int *)0x1048000c) 	//获取中断ID号
#define  EXT_INT41_PEND	(*(volatile unsigned int *)0x11000f44) 	//清GPIO的中断标志
#define  ICDICPR1_CPU0	(*(volatile unsigned int *)0x10490284) 	//清GIC[31:0]的中断标志
#define  ICCEOIR_CPU0	(*(volatile unsigned int *)0x10480010) 	//结束中断

#define  GPX2CON		(*(volatile unsigned int *)0x11000C40) 	//
#define  GPF3CON		(*(volatile unsigned int *)0x11000C20) 	//
#define  GPX1DATA		(*(volatile unsigned int *)0x11000C24) 	//
#define  GPX2DATA		(*(volatile unsigned int *)0x11000C44) 	//
#define  GPF3DATA		(*(volatile unsigned int *)0x114001E4) 	//

#define GPX2_7_U (GPX2DATA |= (0X01 << 7)) 		//LED2
#define GPX2_7_D (GPX2DATA &= ~(0X01 << 7))

#define GPX1_0_U (GPX1DATA |= (0X01 << 0)) 		//LED3
#define GPX1_0_D (GPX1DATA &= ~(0X01 << 0))

#define GPF3_4_U (GPF3DATA |= (0X01 << 4)) 		//LED4
#define GPF3_4_D (GPF3DATA &= ~(0X01 << 4))

#define GPF3_5_U (GPF3DATA |= (0X01 << 5)) 		//LED5
#define GPF3_5_D (GPF3DATA &= ~(0X01 << 5))

void uart_init(void)
{	
	//4.设置波特率为115200
	//For example, if the Baud rate is 115200 bps and SCLK_UART is 100 MHz,UBRDIVn and UFRACVALn are:
	//DIV_VAL = (SCLK_UART/(bps * 16)) - 1
	//DIV_VAL = (100000000/(115200 * 16)) – 1
	//= 54.253 – 1
	//= 53.253
	//UBRDIVn = 53 (integer part of DIV_VAL)
	//UFRACVALn/16 = 0.253
	//Therefore, UFRACVALn = 4

	//1.配置GPA1CON寄存器中的UART2    	GPA1_0  UART_2_RXD
	// 									GPA1_1  UART_2_TXD
	GPA1CON &= 0xffffff00;
	GPA1CON |= 0x00000022;

	//2.配置ULCON2寄存器  	串口2 8位数据位,1位停止位,无奇偶校验
	ULCON2 &= 0Xffffffc0;
	ULCON2 |= 0x00000003;

	//3.配置UCON2寄存器 	通过轮询(polling)的模式读取串口数据     通过轮询(polling)的模式往串口写入数据数据
	UCON2 &= 0xfffffff0;
	UCON2 |= 0x00000005;

	UBRDIV2 = 53;
	UFRACVAL2 = 4;

	
	//1.配置GPA0CON寄存器中的UART0    	GPA0_0  UART_0_RXD
	// 									GPA0_1  UART_0_TXD
	GPA0CON &= 0xffffff00;
	GPA0CON |= 0x00000022;

	//2.配置ULCON0寄存器  	串口0 8位数据位,1位停止位,无奇偶校验
	ULCON0 &= 0Xffffffc0;
	ULCON0 |= 0x00000003;

	//3.配置UCON0寄存器 	通过轮询(polling)的模式读取串口数据     通过轮询(polling)的模式往串口写入数据数据
	UCON0 &= 0xfffffff0;
	UCON0 |= 0x00000005;

	//4.设置波特率为115200
	UBRDIV0 = 53;
	UFRACVAL0 = 4;
}

void interrupt_init()
{
	//*  key2    GPX1_1    EINT9   SPIPORT:25 	INTID:57                                        *
	//*******外   配置芯片管脚工作模式**************************************
	//1.配置GPX1CON寄存器中的WAKEUP_INT1_1         	GPX1_1
	GPX1CON = (GPX1CON & ~(0x0f << 4)) | (0x0f << 4);
	//2.通过EXT_INT41_CON1 配置外部中断1为下降沿触发
	EXT_INT41_CON = (EXT_INT41_CON & ~(0X07 << 4)) | (0X02 << 4);
	//3.使能外部中断
	EXT_INT41_MASK = (EXT_INT41_MASK & ~(0X01 << 1)) | (0X00 << 1);
	
	
	//*******内   功能块设置**************************************
	//4.设置GIC中断使能  ICDISER1_CPU0  GPX1_1的中断,即XEINT9 中断端口号为25
	ICDISER1_CPU0 = ICDISER1_CPU0 | (0x01 << 25);
	//5.根据样例选用默认设置
  	//ICDIPTR17_CPU0 = 0x01010101;
  	ICDIPTR14_CPU0 = 0x01010101;
	//6.分发总使能
	ICDDCR = ICDDCR | 0X01;
	//7.CPU外部中断借口使能
	ICCICR_CPU0 = ICCICR_CPU0 | 0x01;
	//8.设置CPU0的优先级门槛为最低
	ICCPMR_CPU0 = 0xff;
	
	
	
	//*  key3    GPX1_2    EINT10   SPIPORT:26 	INTID:58  
	//*******外   配置芯片管脚工作模式**************************************
	//1.配置GPX1CON寄存器中的WAKEUP_INT1_2         	GPX1_2
	GPX1CON = (GPX1CON & ~(0x0f << 8)) | (0x0f << 8);
	//2.通过EXT_INT41_CON2 配置外部中断1为下降沿触发
	EXT_INT41_CON = (EXT_INT41_CON & ~(0X07 << 8)) | (0X02 << 8);
	//3.使能外部中断
	EXT_INT41_MASK = (EXT_INT41_MASK & ~(0X01 << 2)) | (0X00 << 2);
	
	
	//*******内   功能块设置**************************************
	//4.设置GIC中断使能  ICDISER1_CPU0  GPX1_2的中断,即XEINT10 中断端口号为26
	ICDISER1_CPU0 = ICDISER1_CPU0 | (0x01 << 26);
	//5.根据样例选用默认设置
  	//ICDIPTR17_CPU0 = 0x01010101;
  	ICDIPTR14_CPU0 = 0x01010101;
	//6.分发总使能
	ICDDCR = ICDDCR | 0X01;
	//7.CPU外部中断借口使能
	ICCICR_CPU0 = ICCICR_CPU0 | 0x01;
	//8.设置CPU0的优先级门槛为最低
	ICCPMR_CPU0 = 0xff;
                                      
}
void led_init()
{
	GPX2CON = (GPX2CON & ~(0X0F << 28)) | (0X01 << 28); 	//设置LED2灯的配置寄存器 GPX2CON7为输出状态
	GPX1CON = (GPX1CON & ~(0X0F << 0)) | (0X01 << 0); 		//设置LED3灯的配置寄存器 GPX1CON0为输出状态
	GPF3CON = (GPF3CON & ~(0X0F << 16)) | (0X01 << 16); 	//设置LED4灯的配置寄存器 GPF3CON4为输出状态
	GPF3CON = (GPF3CON & ~(0X0F << 20)) | (0X01 << 20); 	//设置LED5灯的配置寄存器 GPF3CON5为输出状态
}
void uart0_putc(char c)
{
	while(1)
	{
		if(UTRSTAT0 && 0X02) break;
	}

	UTXH0 = c;
}
char uart0_getc(char *c)
{
	while(1)
	{
		if(UTRSTAT0 && 0X01) break;
	}
	*c = URXH0;
	uart0_putc(*c);

	return *c;
}

void uart2_putc(char c)
{
	while(1)
	{
		if(UTRSTAT2 && 0X02) break;
	}

	UTXH2 = c;
}
void do_irq()
{
	int irq_num;
	irq_num = ICCIAR_CPU0 & 0X3ff; 	//获取中断ID号
	switch(irq_num)
	{
		case 57:
			//LED2闪烁
			GPX2_7_U;
			delay1s();
			GPX2_7_D;
			EXT_INT41_PEND = EXT_INT41_PEND | (0X01 << 1); 		//清GPX1_1中断标志
			ICDICPR1_CPU0 = ICDICPR1_CPU0 | (0x01 << 25);  		//清GCI中GPX1_1的中断标志
			break;
		case 58:
			uart2_putc('3');
			EXT_INT41_PEND = EXT_INT41_PEND | (0X01 << 2); 		//清GPX1_2中断标志
			ICDICPR1_CPU0 = ICDICPR1_CPU0 | (0x01 << 26);  		//清GCI中GPX1_2的中断标志
			break;

		default:
			uart2_putc('d');
	}
	ICCEOIR_CPU0 = (ICCEOIR_CPU0 & ~0x3ff) | irq_num;   	//结束对应的中断
	
}
int main(void) 
{
	uart_init();
	led_init();
	interrupt_init();
	char c;

	while(1)
	{
		uart0_getc(&c);
		uart2_putc('b');
		delay1s();
		delay1s();
	}
	
	return 0;
}
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值