CPU与硬件的交互方式(中断、轮询)

一、CPU与硬件的交互方式

轮询方式:CPU不断的访问硬件有没有进行操作,如果接收到了信息,CPU就处理。如果没有过一会在访问一遍 直到有信息为止
中断方式:硬件操作的时候会写一个中断,向CPU发送这个中断,CPU正常执行程序,如果收到该中断,就处理 中断
DMA方式:硬件产生数据时,直接放到存储器当中,不需要CPU的参与了

1.简述轮询和中断两种方式的本质区别是什么
轮询:需要CPU主动访问有没有数据
中断:是寄存器向CPU发送中断信号,来处理数据

1)轮询方式

轮询方式:实现KEY按键来控制LED2灯的点亮和熄灭

第一步:查看KEY按键的通电方式

在这里插入图片描述
得出结论K2按下连接地,弹起是通过VDD1V8_EXT来通电也就是1.8V高电平,通过UART_RING网络标号查看连接到那个引脚上

在这里插入图片描述
通过FS4412_CoreBoard_V2电路图查看连接在K2按键的引脚是GPX1_1引脚上面

第二步:

将GPX1_1寄存器设置成输入功能 因为是要接收KEY2按键的信号
判断GPX1_DAT寄存器接收到的是低电平还是高电平

第三步:
查看LED2灯引脚控制情况
在这里插入图片描述
通过CHG_COK网络标号查看那个引脚控制
在这里插入图片描述

将GPX2_7寄存器设置成输出功能
在这里插入图片描述

将GPX2_DAT寄存器第七位设置成高电平点亮,低电平熄灭

代码实现:

#include "exynos_4412.h"


void GPX_LED (char flag) {
	if(flag) {
		/* 点亮LED2 */
		GPX2.DAT = GPX2.DAT | (0x1 << 7);
	} else {
		/* 熄灭LED2 */
		GPX2.DAT = GPX2.DAT & (~(0x1 << 7));
	}
}

int main()
{
	char flag = 0x1;
	/* 将GPX1_1设置成输入功能 */
	GPX1.CON = GPX1.CON & (~(0xF << 4));

	/* 讲GPX2_7设置成输出功能 LED2 */
	GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);

	while (1) {
		
		/* 判断GPX_1引脚状态,即判断按键是否按下 Key2 */
		if (!(GPX1.DAT & (1 << 1))) {
			printf("Key2 Pressed\n");

			/* 点亮和熄灭LED2 */
			flag = !flag;
			GPX_LED(flag);

			/* 判断按键是否松开 */
			while (!(GPX1.DAT & (1 << 1)));
		}
		else {
		}
	}
	return 0;
}

2)中断方式

1. 中断控制器的作用

  • 多个中断同时产生时可对这些中断挂起排队,然后按照优先级依次发送给CPU处理
  • 可以为每一个中断分配一个优先级
  • 一个中断正在处理时若又产生其它中断,可将新的中断挂起,待CPU空闲时再发送
  • 可以为每一个中断选择一个CPU处理
  • 可以为每一个中断选择一个中断类型(FIQ或IRQ)
  • CPU接收到中断信号后并不能区分是哪个外设产生的,此时CPU可查询中断控制器来获取当前的中断信号是由哪个硬件产生的,然后再进行对应的处理
  • 可以打开或禁止每一个中断
    … …
    在这里插入图片描述

中断实验:

中断方式:GPIO寄存器检测KEY按键引脚是否产生中断

CPU检测到K2为下降沿信号会向CPU发送一个中断信号

如果将GPX1_1寄存器这一组设置成中断功能,那么中断一些详细的设置就在EXT_INT ‘41’ CON数字是41的寄存器中设置
在这里插入图片描述
EXT_INT41_CON[1]:产生中断的方式,什么情况下向CPU发送中断信号
0x0 = Low level高电平方式
0x1 = High level 低电平方式
0x2 = Triggers Falling edge 上升沿方式
0x3 = Triggers Rising edge 下降沿方式
0x4 = Triggers Both edge 上升、下降其中一种
0x5 to 0x7 = Reserved

上升沿:就是有一个信号由高电平变成低电平时触发

在这里插入图片描述
EXT_INT41_MASK寄存器:是关闭和打开引脚中断功能的

代码实现:

int main()
{
	/* 将GPX1_1设置成中断功能 */
	GPX1.CON = GPX1.CON | (0xF << 4);

	/* 设置GPX1_1中断触发方式 下降沿触发方式*/
	EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);

	/* 使用GPX1_1中断功能开关 0-开  1-关 */
	EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 1));

	/*
	设置了这些中断功能,GPIO寄存器检测到KEY按键的中断信号,
	是不能直接往CPU中发送这个中断信号的,需要通过中断控制器发往CPU
	 */
	return 0;
}

有160个外部设备可以向CPU发送中断信号
本次实验需要设置四个处理器

在这里插入图片描述
XEINT9:外部中断9的意思
在这里插入图片描述

找到控制Key2按键引脚的中断控制号
57号就是GPX1_1引脚产生的中断号,后面设置57号相关的寄存器就行了

在这里插入图片描述
ICDDCR寄存器:中断控制器的总开关,0-关闭,1-开启

在这里插入图片描述

ICDISER_CPU寄存器:一个寄存器占八个字节,32位,所以由5个寄存器管理者160个中断,我们要把管理着GPX1_1引脚的中断的第57号中断打开,其他位不动

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

INTID 57号是在0x0838地址中的8-15位上面,所以要设置8-15位交给CPU0管理
ICDIPTR_CPU寄存器:里面有40个寄存器可以将160个中断分配到那个CPU处理,将57号中断发送到那个CPU去处理该中断

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

ICCICR_CPUn寄存器:CPU和中断之间的接口开关和闭合,这样中断信号才能顺利的到达CPU

代码:

#include "exynos_4412.h"

int main()
{

	/* 外设层次 - 让外部的硬件控制器能产生一个中断信号并发送给中断控制器  */
	/* 将GPX1_1设置成中断功能 */
	GPX1.CON = GPX1.CON | (0xF << 4);

	/* 设置GPX1_1中断触发方式 下降沿触发方式*/
	EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);

	/* 使用GPX1_1中断功能开关 0-开  1-关 */
	EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 1));


	/* 中断控制器层次 - 让中断控制器接受外设发来的中断信号并对其进行管理然后转发给一个合适的CPU来处理该中断 */
	/* 全局使能中断控制器使其能够接受外部设备产生的中断信号并转发给CPU接口  打开终端控制器总开关 ICDDCR */
	ICDDCR = ICDDCR | 1;

	/* 在中断控制器中使能57号中断,使中断控制器在接收到57号中断后能将其进一步转发到CPU接口    将57号中断打开 ICDISER_CPU */
	ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 25);

	/* 选择CPU0来处理57号中断 */
	ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 8)) | (0x01 << 8);
	
	/* 将中断控制器和CPU0之间的接口使能,使得中断控制器转发的信号能到达CPU0 */
	CPU0.ICCICR = CPU0.ICCICR | 1;

	return 0;
}

GPIO寄存器触发中断的框架

1.0 版本

启动代码
start.S

.text
.global _start
_start:
	/*
	 * Vector table
	 */ 
	b reset
	b .
	b .
	b .
	b .
	b .
	/*
	 * 从异常向量表再跳转到IRQ的异常处理程序
	 */
	b irq_handler
	b .

reset:
	/*
	 * Set vector address in CP15 VBAR register
	 */ 
	ldr	r0, =_start
	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR

	/*
	 * Set the cpu to SVC32 mode, Disable FIQ/IRQ
	 */  
	mrs r0, cpsr
	bic r0, r0, #0x1f
	orr	r0, r0, #0xd3
	msr	cpsr ,r0

	/*
	 * Defines access permissions for each coprocessor
	 */  
    mov	r0, #0xfffffff
    mcr	p15, 0, r0, c1, c0, 2  	

	/*
	 * Invalidate L1 I/D                                                                                                                   
	 */
	mov	r0, #0					@Set up for MCR
	mcr	p15, 0, r0, c8, c7, 0	@Invalidate TLBs
	mcr	p15, 0, r0, c7, c5, 0	@Invalidate icache
	
	/*
	 * Set the FPEXC EN bit to enable the FPU
	 */ 
	mov r3, #0x40000000
	fmxr FPEXC, r3
	
	/*
	 * Disable MMU stuff and caches
	 */
	mrc	p15, 0, r0, c1, c0, 0
	bic	r0, r0, #0x00002000		@Clear bits 13 (--V-)
	bic	r0, r0, #0x00000007		@Clear bits 2:0 (-CAM)
	orr	r0, r0, #0x00001000		@Set bit 12 (---I) Icache
	orr	r0, r0, #0x00000002		@Set bit 1 (--A-) Align
	orr	r0, r0, #0x00000800		@Set bit 11 (Z---) BTB
	mcr	p15, 0, r0, c1, c0, 0

	/*
	 * Initialize stacks                                                                                                                  
	 */
init_stack:     
	/*svc mode stack*/
	msr cpsr, #0xd3
	ldr sp, _stack_svc_end

	/*undef mode stack*/
	msr cpsr, #0xdb
	ldr sp, _stack_und_end

	/*abort mode stack*/	
	msr cpsr,#0xd7
	ldr sp,_stack_abt_end

	/*irq mode stack*/	
	msr cpsr,#0xd2
	ldr sp, _stack_irq_end
	
	/*fiq mode stack*/
	msr cpsr,#0xd1
	ldr sp, _stack_fiq_end
	
	/*user mode stack, enable FIQ/IRQ*/
	msr cpsr,#0x10
	ldr sp, _stack_usr_end

	/*Call main*/
	b main

	/*
	 * IRQ的异常处理程序
	 */
irq_handler:
	/*
	 * 因为产生IRQ异常后ARM自动保存到LR中的返回地址是被IRQ打断的指令
     * 的下一条再下一条指令的地址,所以我们需要人为的去修正一下
	 */
	sub lr, lr, #4
	/*
	 * 因为IRQ模式下使用的R0-R12寄存器和USER模式下使用的是同一组
	 * 所以在处理异常之前需要先将之前寄存器中的值压栈保护
	 */
	stmfd sp!, {r0-r12,lr}
	/*
	 * 跳转到do_irq处理异常
	 */
	bl do_irq
	/*
	 * 异常返回
	 * 1.将R0-R12寄存器中的值出栈,使其恢复到被异常打断之前的值
	 * 2.将SPSR寄存器中的值恢复到CPSR,使CPU的状态恢复到被异常打断之前
	 * 3.将栈中保存的LR寄存器的值出栈给PC,使程序跳转回被异常打断的点继续执行
	 */
	ldmfd sp!,{r0-r12,pc}^

_stack_svc_end:      
	.word stack_svc + 512
_stack_und_end:      
	.word stack_und + 512
_stack_abt_end:      
	.word stack_abt + 512
_stack_irq_end:      
    .word stack_irq + 512
_stack_fiq_end:
    .word stack_fiq + 512
_stack_usr_end:      
    .word stack_usr + 512

.data
stack_svc:      
	.space 512
stack_und:
	.space 512
stack_abt:      
	.space 512
stack_irq:      
	.space 512
stack_fiq:      
	.space 512
stack_usr:      
	.space 512

程序执行代码
interface.c
处理异常程序就可以在do_irq()函数中写了



#include "exynos_4412.h"

void Delay(unsigned int Time)
{
	while(Time--);
}


//IRQ异常处理
void do_irq(void)
{
	printf("Key2 Pressed\n");	
}

int main()
{
	/*外设层次 - 让外部的硬件控制器产生一个中断信号发送给中断控制器*/
	/*将GPX1_1设置成中断功能*/
	GPX1.CON = GPX1.CON | (0xF << 4);
	/*设置GPX1_1的中断触发方式为下降沿触发*/
	EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);
	/*使能GPX1_1的中断功能*/
	EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 1));

	/*中断控制器层次 - 让中断控制器接收外设产生的中断信号并对其进行管理然后再转发给CPU处理*/
	/*全局使能中断控制器使其能接收外设产生的中断信号并转发到CPU接口*/
	ICDDCR = ICDDCR | 1;
	/*在中断控制器中使能57号中断,使中断控制器接收到57号中断后能将其转发到CPU接口*/
	ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 25);
	/*选择由CPU0来处理57号中断*/
	ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 8)) | (0X01 << 8);
	/*使能中断控制器和CPU0之间的接口,使中断控制器转发的中断信号能够到达CPU0*/
	CPU0.ICCICR = CPU0.ICCICR | 1;

	GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);

    while(1)
	{   
		/*点亮LED2*/
		GPX2.DAT = GPX2.DAT | (1 << 7); 
		/*延时*/
		Delay(1000000);
		/*熄灭LED2*/
		GPX2.DAT = GPX2.DAT & (~(1 << 7));
		/*延时*/
		Delay(1000000);
	}   

	return 0;
}

1.1 版本

interface.c
执行该程序CPU将一直执行中断信号,是因为该中断信号一直是挂起状态
EXT_INT41_PEND: 中断挂起寄存器
当CPIO产生一个中断时,有可能该中断信号得不到及时的处理,所以可以通过中断挂起寄存器将该中断挂起,如果CPU不能及时的处理该中断信号,CPU就可以一直将该中断信号挂着,直到CPU能处理时,然后再把该中断信号发给CPU处理。
当发生中断信号时该寄存器对应的产生中断信号引脚的寄存器自动置1,然后再向CPU发送中断信号,处理完异常程序 返回main函数时 该位还是1,这样就导致CPU将会一直触发该中断信号,所以再返回main函数时要将该位置0。(该寄存器有些特殊,写1就是0,写0就是1)


//IRQ异常处理
void do_irq(void)
{
		printf("Key2 Pressed\n");
		/*清除GPIO控制器中GPX1_1的中断挂起标志位*/
		EXT_INT41_PEND = (1 << 1);
}

1.2 版本

interface.c
一共有160个外部中断信号,CPU接收到的中断信号都是相同的中断信号,不知道是怎么区分中断信号的来源,但是中断控制器直到该中断信号的来源是哪个外部设备产生的,所以要区分一下该中断信号是谁产生的。
ICCIAR_CPU: 存储中断控制号的
所有的外部中断信号产生时先发送给 中断控制器 ,中断控制器中该寄存器bit[9:0]存储中断控制号,然后CPU执行中断程序时先读一下该寄存器,是几号中断就执行几号中断。


//IRQ异常处理
void do_irq(void)
{
	unsigned int IrqNum = 0;
	/*从中断控制器中获取当前中断的中断号*/
	IrqNum = CPU0.ICCIAR & 0x3FF;
	
	/*根据中断号处理不同的中断*/
	switch(IrqNum)
	{
		case 0:
			//0号中断的处理程序
			break;
		case 1:
			//1号中断的处理程序
			break;
			/*
			 * ... ...
			 */
		case 57:
			printf("Key2 Pressed\n");
			/*清除GPIO控制器中GPX1_1的中断挂起标志位*/
			EXT_INT41_PEND = (1 << 1);
			break;
			/*
			 * ... ...
			 */
		case 159:
			//159号中断的处理程序
			break;
		default:
			break;
	}

}

1.3 版本

interface.c
第二次按键按下时,GPIO寄存器又会向中断控制器发送信号,中断控制器将不会再向CPU发送信号! 因为CPU还没有处理完上一次中断异常,是因为CPU执行完处理异常程序返回main的时候没有告诉中断控制器,该中断信号处理完毕。
所以当CPU处理完中断返回main函数时要告诉中断控制器。
ICCEOIR_CPU: 处理完的中断号
在该寄存器bit[9:0]写入CPU处理完的中断号

#include "exynos_4412.h"

void Delay(unsigned int Time)
{
	while(Time--);
}

//IRQ异常处理
void do_irq(void)
{
	unsigned int IrqNum = 0;
	/*从中断控制器中获取当前中断的中断号*/
	IrqNum = CPU0.ICCIAR & 0x3FF;
	
	/*根据中断号处理不同的中断*/
	switch(IrqNum)
	{
		case 0:
			//0号中断的处理程序
			break;
		case 1:
			//1号中断的处理程序
			break;
			/*
			 * ... ...
			 */
		case 57:
			printf("Key2 Pressed\n");
			/*清除GPIO控制器中GPX1_1的中断挂起标志位*/
			EXT_INT41_PEND = (1 << 1);
			/*将当前中断的中断号写回到中断控制器中,以这种方式来告知中断控制器当前的中断已经处理完成,可以发送其它中断*/
			CPU0.ICCEOIR = CPU0.ICCEOIR & (~(0x3FF)) | (57);
			break;
			/*
			 * ... ...
			 */
		case 159:
			//159号中断的处理程序
			break;
		default:
			break;
	}

}

int main()
{
	/*外设层次 - 让外部的硬件控制器产生一个中断信号发送给中断控制器*/
	/*将GPX1_1设置成中断功能*/
	GPX1.CON = GPX1.CON | (0xF << 4);
	/*设置GPX1_1的中断触发方式为下降沿触发*/
	EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);
	/*使能GPX1_1的中断功能*/
	EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 1));

	/*中断控制器层次 - 让中断控制器接收外设产生的中断信号并对其进行管理然后再转发给CPU处理*/
	/*全局使能中断控制器使其能接收外设产生的中断信号并转发到CPU接口*/
	ICDDCR = ICDDCR | 1;
	/*在中断控制器中使能57号中断,使中断控制器接收到57号中断后能将其转发到CPU接口*/
	ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 25);
	/*选择由CPU0来处理57号中断*/
	ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 8)) | (0X01 << 8);
	/*使能中断控制器和CPU0之间的接口,使中断控制器转发的中断信号能够到达CPU0*/
	CPU0.ICCICR = CPU0.ICCICR | 1;

	GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);

    while(1)
	{   
		/*点亮LED2*/
		GPX2.DAT = GPX2.DAT | (1 << 7); 
		/*延时*/
		Delay(1000000);
		/*熄灭LED2*/
		GPX2.DAT = GPX2.DAT & (~(1 << 7));
		/*延时*/
		Delay(1000000);
	}   

	return 0;
}

二、中断实验小作业

实验结果:使用中断的方式检测Key3按键的状态,实现按一次按键,LED2点亮,再次按下,LED2熄灭

第一步:外设层次 -
让外部的硬件控制器产生一个中断信号发送给中断控制器
在这里插入图片描述

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

int main()
{
	/*外设层次 - 让外部的硬件控制器产生一个中断信号发送给中断控制器*/
	/*将GPX1_2设置成中断功能*/
	GPX1.CON = GPX1.CON | (0xF << 8);
	/*设置GPX1_2的中断触发方式为下降沿触发*/
	EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 8)) | (0x2 << 8);
	/*使能GPX1_2的中断功能*/
	EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 2));
}

第二步:中断控制器层次 -
让中断控制器接收外设产生的中断信号并对其进行管理然后再转发给CPU处理

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

在这里插入图片描述

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


	/*中断控制器层次 - 让中断控制器接收外设产生的中断信号并对其进行管理然后再转发给CPU处理*/
	/*全局使能中断控制器使其能接收外设产生的中断信号并转发到CPU接口*/
	ICDDCR = ICDDCR | 1;
	/*在中断控制器中使能58号中断,使中断控制器接收到58号中断后能将其转发到CPU接口*/
	ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 26);
	/*选择由CPU0来处理58号中断*/
	ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 16)) | (0X01 << 16);
	/*使能中断控制器和CPU0之间的接口,使中断控制器转发的中断信号能够到达CPU0*/
	CPU0.ICCICR = CPU0.ICCICR | 1;

第三步:启动代码
设置IRQ的异常处理程序
start.S

.text
.global _start
_start:
	/*
	 * Vector table
	 */ 
	b reset
	b .
	b .
	b .
	b .
	b .
	/*
	 * 从异常向量表再跳转到IRQ的异常处理程序
	 */
	b irq_handler
	b .

reset:
	/*
	 * Set vector address in CP15 VBAR register
	 */ 
	ldr	r0, =_start
	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR

	/*
	 * Set the cpu to SVC32 mode, Disable FIQ/IRQ
	 */  
	mrs r0, cpsr
	bic r0, r0, #0x1f
	orr	r0, r0, #0xd3
	msr	cpsr ,r0

	/*
	 * Defines access permissions for each coprocessor
	 */  
    mov	r0, #0xfffffff
    mcr	p15, 0, r0, c1, c0, 2  	

	/*
	 * Invalidate L1 I/D                                                                                                                   
	 */
	mov	r0, #0					@Set up for MCR
	mcr	p15, 0, r0, c8, c7, 0	@Invalidate TLBs
	mcr	p15, 0, r0, c7, c5, 0	@Invalidate icache
	
	/*
	 * Set the FPEXC EN bit to enable the FPU
	 */ 
	mov r3, #0x40000000
	fmxr FPEXC, r3
	
	/*
	 * Disable MMU stuff and caches
	 */
	mrc	p15, 0, r0, c1, c0, 0
	bic	r0, r0, #0x00002000		@Clear bits 13 (--V-)
	bic	r0, r0, #0x00000007		@Clear bits 2:0 (-CAM)
	orr	r0, r0, #0x00001000		@Set bit 12 (---I) Icache
	orr	r0, r0, #0x00000002		@Set bit 1 (--A-) Align
	orr	r0, r0, #0x00000800		@Set bit 11 (Z---) BTB
	mcr	p15, 0, r0, c1, c0, 0

	/*
	 * Initialize stacks                                                                                                                  
	 */
init_stack:     
	/*svc mode stack*/
	msr cpsr, #0xd3
	ldr sp, _stack_svc_end

	/*undef mode stack*/
	msr cpsr, #0xdb
	ldr sp, _stack_und_end

	/*abort mode stack*/	
	msr cpsr,#0xd7
	ldr sp,_stack_abt_end

	/*irq mode stack*/	
	msr cpsr,#0xd2
	ldr sp, _stack_irq_end
	
	/*fiq mode stack*/
	msr cpsr,#0xd1
	ldr sp, _stack_fiq_end
	
	/*user mode stack, enable FIQ/IRQ*/
	msr cpsr,#0x10
	ldr sp, _stack_usr_end

	/*Call main*/
	b main

	/*
	 * IRQ的异常处理程序
	 */
irq_handler:
	/*
	 * 因为产生IRQ异常后ARM自动保存到LR中的返回地址是被IRQ打断的指令
     * 的下一条再下一条指令的地址,所以我们需要人为的去修正一下
	 */
	sub lr, lr, #4
	/*
	 * 因为IRQ模式下使用的R0-R12寄存器和USER模式下使用的是同一组
	 * 所以在处理异常之前需要先将之前寄存器中的值压栈保护
	 */
	stmfd sp!, {r0-r12,lr}
	/*
	 * 跳转到do_irq处理异常
	 */
	bl do_irq
	/*
	 * 异常返回
	 * 1.将R0-R12寄存器中的值出栈,使其恢复到被异常打断之前的值
	 * 2.将SPSR寄存器中的值恢复到CPSR,使CPU的状态恢复到被异常打断之前
	 * 3.将栈中保存的LR寄存器的值出栈给PC,使程序跳转回被异常打断的点继续执行
	 */
	ldmfd sp!,{r0-r12,pc}^

_stack_svc_end:      
	.word stack_svc + 512
_stack_und_end:      
	.word stack_und + 512
_stack_abt_end:      
	.word stack_abt + 512
_stack_irq_end:      
    .word stack_irq + 512
_stack_fiq_end:
    .word stack_fiq + 512
_stack_usr_end:      
    .word stack_usr + 512

.data
stack_svc:      
	.space 512
stack_und:
	.space 512
stack_abt:      
	.space 512
stack_irq:      
	.space 512
stack_fiq:      
	.space 512
stack_usr:      
	.space 512

第四步:LED3灯
在这里插入图片描述

在这里插入图片描述

写.c文件中的执行处理异常相关程序
interface.c

#include "exynos_4412.h"

//IRQ异常处理
void do_irq(void)
{
	unsigned int IrqNum = 0;
	/*从中断控制器中获取当前中断的中断号*/
	IrqNum = CPU0.ICCIAR & 0x3FF;
	
	/*根据中断号处理不同的中断*/
	switch(IrqNum)
	{
		case 0:
			//0号中断的处理程序
			break;
		case 1:
			//1号中断的处理程序
			break;
			/*
			 * ... ...
			 */
		case 58:
			printf("Key3 Pressed\n");
			/* 熄灭LED3  */
			GPX1.DAT = GPX1.DAT & (~ 1);
			/*清除GPIO控制器中GPX1_2的中断挂起标志位*/
			EXT_INT41_PEND = (1 << 2);
			/*将当前中断的中断号写回到中断控制器中,以这种方式来告知中断控制器当前的中断已经处理完成,可以发送其它中断*/
			CPU0.ICCEOIR = CPU0.ICCEOIR & (~(0x3FF)) | (58);
			break;
			/*
			 * ... ...
			 */
		case 159:
			//159号中断的处理程序
			break;
		default:
			break;
	}

}

int main()
{
	/*外设层次 - 让外部的硬件控制器产生一个中断信号发送给中断控制器*/
	/*将GPX1_2设置成中断功能*/
	GPX1.CON = GPX1.CON | (0xF << 8);
	/*设置GPX1_2的中断触发方式为下降沿触发*/
	EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 8)) | (0x2 << 8);
	/*使能GPX1_2的中断功能*/
	EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 2));

	/*中断控制器层次 - 让中断控制器接收外设产生的中断信号并对其进行管理然后再转发给CPU处理*/
	/*全局使能中断控制器使其能接收外设产生的中断信号并转发到CPU接口*/
	ICDDCR = ICDDCR | 1;
	/*在中断控制器中使能58号中断,使中断控制器接收到58号中断后能将其转发到CPU接口*/
	ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 26);
	/*选择由CPU0来处理58号中断*/
	ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 16)) | (0X01 << 16);
	/*使能中断控制器和CPU0之间的接口,使中断控制器转发的中断信号能够到达CPU0*/
	CPU0.ICCICR = CPU0.ICCICR | 1;


	GPX1.CON = GPX1.CON & (~(0xF)) | (0x1);
    while(1)
	{   
		/* 点亮LED3 */
		GPX1.DAT = GPX1.DAT | 1;
	} 

	return ;
}

完整代码:C工程模板,提取码:hit2
把上面的interface.c和start.S文件替换

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值