ARM 中断

1.按键中断实验分析

1.1 看原理图,找到key2对应的引脚是GPX1_1,我们需要配置这个引脚为中断功能 XEINT9/KP_COL1/ALV_DBG5/GPX1_1


1.2 分析芯片手册

过五关斩六将

1.2.1 外设层次上(五关第一关):

GPX1CON
GPX1CON[1]  [7:4] RW 0xF = WAKEUP_INT1[1] //设为中断模式

EXT_INT41CON
EXT_INT41_CON[1]  [6:4]: 0x2 = Triggers Falling edge //下降沿触发中断

EXT_INT41_MASK
EXT_INT41_MASK[1]  [1]: 0x0 = Enables Interrupt //使能中断,可以让中断发生

1.2.2 GIC(通用中断控制器)层次上

找到中断控制章节,查看简介如下:

翻译:
9 中断控制器

9.1 概述
通用中断控制器(GIC)是支持和管理系统中中断的集中资源。
GIC提供:
用于管理一个或多个处理器的中断源、中断行为和中断路由的寄存器。

ARM 架构安全扩展:
从硬件(外设)中断源启用、禁用和产生处理器中断。

生成软件中断:
中断屏蔽和优先级排序
GIC将系统级断言的中断,向每个连接的处理器发送相应的信号。
当GIC实现安全扩展时,它可以向连接的处理器实现两个中断请求。
架构将这些请求标识为IRQ和FIQ。

9.1.1 功能
GIC的特点是:1.
支持三种中断类型。
软件生成中断(SGI)。
私有外设中断(PPI)
共享外设中断(SPI)

可编程的中断,使你能够设置中断的:
中断的安全状态。
中断的优先级别。
中断的启用或禁用。
接收中断的处理器。

第二关 ICDISER1_CPU0

查看示例代码(一般由厂家提供)

示例代码:
ICDISER1_CPU0 = ICDISER1_CPU0 | (1<<25);

ICDISER1 //使能当前中断
Set-enable bits  [31:0]:  25  1-enable



第三关 ICDDCR

ICDDCR    //全局使能GIC,让所有中断都可以通过GIC到达CPU接口
Enable [0]: 1 = GIC monitors the peripheral interrupt signals and forwards pending interrupts to the CPU interfaces.

第四关 ICCPMR_CPU0

ICCPMR_CPU0 //设置优先级的门槛,当前发生的中断的优先级高于这个门槛才能通过
Priority [7:0]: 0xff //0xff表示优先级最低,所有的中断都可以通过

1.2.3 cpu内核层次上

第五关 ICCICR_CPU0

全局使能中断,所有中断都可以通过CPU接口到达处理器
ICCICR_CPU0 // Global enable for signaling of interrupts by the CPU Interface to the connected processors.
Enable [0]:1 = Enables signaling of interrupts

其他

ICDIPTR14 //将57号中断交给CPU0来处理
CPU targets, byte offset 1 [15:8]; 0x1

注:具体含义不做深究,在以后工作学习中经常会碰到类似的情况,拿来厂家给的示例代码,读不懂的就直接拿来用,若不能达到效果再仔细研读

示例代码
ICDIPTR14_CPU0 = 0x01010101;


1.3 中断恢复

进入中断以后,要提取中断号,并相应的做出反应;处理完中断后,要进行中断恢复:

1.3.1 ICCIAR_CPU0 提取中断号

ICCIAR_CPU0 //当前发生中断的中断号
ACKINTID [9:0] R The interrupt ID

1.3.2 EXT_INT41_PEND 外设层次上 GPIO管脚清中断

EXT_INT41_PEND
EXT_INT41_PEND[1] [1]:0x1 = Interrupt Occurs //这里特殊,写1清中断,在中断

1.3.2 ICDICPR1_CPU0 GIC(通用中断控制器)层次上 清当前中断

ICDICPR1_CPU0
Clear-pending bits [31:0]: 25 写1 清中断



1.3.3 ICCEOIR_CPU0 中断寄存器的终止

ICCEOIR_CPU0 //当中断处理程序结束,将中断号写回这个寄存器,代表中断处理完成
EOIINTID [9:0] W The ACKINTID value from the corresponding ICCIAR access.

2.代码示例:

2.1 main.c代码

//uart
#define GPA1CON 	0x11400020
#define ULCON2 		0x13820000
#define UCON2 		0x13820004
#define UBRDIV2 	0x13820028
#define UFRACVAL2  	0x1382002c
#define UTXH2 		0x13820020
#define UTRSTAT2 	0x13820010

#define rGPA1CON 	(*(volatile unsigned int*)GPA1CON)
#define rULCON2 	(*(volatile unsigned int*)ULCON2)
#define rUCON2 		(*(volatile unsigned int*)UCON2)
#define rUBRDIV2 	(*(volatile unsigned int*)UBRDIV2)
#define rUFBRACVAL2 (*(volatile unsigned int*)UFRACVAL2)
#define rUTXH2 		(*(volatile unsigned int*)UTXH2)
#define rUTRSTAT2 	(*(volatile unsigned int*)UTRSTAT2)


//interrupt
#define GPX1CON 		0x11000c20
#define EXT_INT41CON 	0x11000E04
#define EXT_INT41_MASK 	0x11000F04
#define ICDISER1_CPU0 	0x10490104
#define ICDDCR 			0x10490000
#define ICCPMR_CPU0 	0x10480004
#define ICCICR_CPU0 	0x10480000
#define ICCIAR_CPU0 	0x1048000C
#define EXT_INT41_PEND 	0X11000F44
#define ICDICPR1_CPU0 	0x10490284
#define ICCEOIR_CPU0 	0x10480010
#define ICDIPTR14_CPU0 	0x10490838

#define rGPX1CON 		(*(volatile unsigned int*)GPX1CON)
#define rEXT_INT41CON 	(*(volatile unsigned int*)EXT_INT41CON)
#define rEXT_INT41_MASK (*(volatile unsigned int*)EXT_INT41_MASK)
#define rICDISER1_CPU0 	(*(volatile unsigned int*)ICDISER1_CPU0)
#define rICDDCR 		(*(volatile unsigned int*)ICDDCR)
#define rICCPMR_CPU0 	(*(volatile unsigned int*)ICCPMR_CPU0)
#define rICCICR_CPU0 	(*(volatile unsigned int*)ICCICR_CPU0)
#define rICCIAR_CPU0 	(*(volatile unsigned int*)ICCIAR_CPU0)
#define rEXT_INT41_PEND (*(volatile unsigned int*)EXT_INT41_PEND)
#define rICDICPR1_CPU0 	(*(volatile unsigned int*)ICDICPR1_CPU0)
#define rICCEOIR_CPU0 	(*(volatile unsigned int*)ICCEOIR_CPU0)
#define rICDIPTR14_CPU0 (*(volatile unsigned int*)ICDIPTR14_CPU0)

void uart_putc(char c);
void do_irq(void);

int main(int argc, const char *argv[])
{
	//KEY3
	/* 设置GPA1控制器为UART模式 */
	rGPA1CON &= ~(0xff<<0);   	//把寄存器的bit0~7全部清零
	rGPA1CON |= 0X22<<0; 			//Rx,Tx
	/* 设置串口协议 */
	rULCON2 = 0x03; 			//0校验位 ,8数据位,1停止位
	rUCON2 = 0x05; 				//轮询模式
	/*
	 * 设置波特率:
	 *UART时钟信号源的值为:
	 *100Mhz= 100 000khz = 100 000 000hz
	 *本实验波特率值位115200,DIV_VAL = 100000000/(115200*16) -1 = 54.25 -1 = 53.25
	 *UBRDIVn = 53
	 *UFRACVALn/16 = 0.25 ----> UFRACVALn = 4
	 */
	rUBRDIV2 = 53; 				
	rUFBRACVAL2 = 4;

	//INT
	/* 外设层次上,配置管脚工作模式 */
	rGPX1CON &= ~(0xf<<4); 	//设置为中断模式
	rGPX1CON |= (0xF<<4);

	rEXT_INT41CON &= ~(0X7<<4);
	rEXT_INT41CON |= (0X02<<4); 	//设置为下降沿触发

	rEXT_INT41_MASK &= ~(1<<1); 	//外设引脚使能中断,可以让中断发生
	/* GIC(通用中断控制器)层次上 */
	rICDISER1_CPU0 = rICDISER1_CPU0 | (1<<25); 	//使能当前中断
	rICDDCR |= (1<<0); 			//全局使能GIC 让所有中断可以通过GIC到达CPU
	rICCPMR_CPU0 |= (0xFF<<0); 	//设置中断优先级掩码级别为255
	/* CPU内核层次上 */
	rICCICR_CPU0 |= (1<<0); 		//CPU0的全局使能中断,所有中断都可以通过CPU接口到达内核
	
	rICDIPTR14_CPU0 = 0x01010101; //照抄,意义不明

	/* 发送状态判断 */	
	while(1)
	{
		uart_putc('c');
		delay1s();
	}

	return 0;
}


void uart_putc(char c)
{	
	while(!(rUTRSTAT2&0X02));
	rUTXH2 = c;
	return;
}

void do_irq(void) //参数中的void表示该函数不允许接受参数,理论上不写也可以,为了方便编译器通过,处于严谨考虑,还是要写上
{	
	int irq_num;
	irq_num = rICCIAR_CPU0&0x3FF; 	//获取中断号
	switch(irq_num) 
	{
	case 57:
		uart_putc('k');
		uart_putc('2');
		rEXT_INT41_PEND |= 1<<1; 	//清GPX1_1中断位
		rICDICPR1_CPU0 |= 1<<25; 	//清GIC GPX1_1中断位
		break;
	default:
		uart_putc('x');
		break;
	}
	
	rICCEOIR_CPU0 &= (~0x3FF);
	rICCEOIR_CPU0 |= irq_num; 	//结束中断 将处理完成的中断ID号写入该寄存器 表示相应的中断处理完成
}

2.2 start.S代码

    .global  delay1s  @.C文件要调用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_handle 
_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          /* 初始化阶段要禁止IRQ,FIQ中断,I位=1,r位=1,mode位=10010 集合到一起就是d2 */
		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

irq_handle:
	sub lr,lr,#4
	stmfd sp!,{r0-r12,lr}
	bl do_irq
irq_handle_end:
	ldmfd sp!,{r0-r12,pc}^
	


delay1s:                            /* 延时1s函数 */
     ldr      r4,=0x1ffffff   
delay1s_loop:
     sub    r4,r4,#1
     cmp    r4,#0         
     bne    delay1s_loop
     mov   pc,lr	


	.align	4                   /* 4字节对齐 */

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


stacktop:    .word 		stack+4*512

.data

stack:	 
  .space  4*512
.end

2.3 Makefile代码

all:
	arm-none-linux-gnueabi-gcc -fno-builtin -nostdinc -c -o start.o start.S
	arm-none-linux-gnueabi-gcc -fno-builtin -nostdinc -c -o main.o main.c
	arm-none-linux-gnueabi-ld start.o main.o -Tmap.lds -o uart.elf
	arm-none-linux-gnueabi-objcopy -O binary  uart.elf uart.bin
	arm-none-linux-gnueabi-objdump -D uart.elf > uart.dis
clean:
	rm -rf *.bak start.o main.o uart.elf uart.bin uart.dis

2.4 map.lds代码

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
	. = 0x40008000;  /*ָ 程序加载地址 */
	. = ALIGN(4);
	.text      :
	{
		start.o(.text)
		*(.text)
	}
	. = ALIGN(4);
    .data : 
	{ *(.data) }
    . = ALIGN(4);
    .bss :
     { *(.bss) }
}

3. 效果展示: