admin管理员组

文章数量:1122850

ARM

irq中断

按下按键k2或者k3就可以产生irq中断

回忆异常的处理流程:

5种异常模式  7种异常源

0地址有异常向量表:每一种异常源都会有一个固定的地址,在这个地址处,是一个跳转到对应的异常处理函数的跳转指令

当异常产生时,会发生哪些事情呢?

ARM core会自动:

  1. 将cpsr的值拷贝到spsr
  2. 将返回值拷贝到lr(将pc的值赋值给了lr,但是bl是将pc-4的值赋值给了lr)
  3. 设置处理器的状态为ARM态
  4. 设置处理器的工作模式为相应的异常模式
  5. 手动设置中断禁止位(看情况,irq和fiq)
  6. 设置pc为相应的异常向量

我们需要:

  1. 申请对应的栈空间
  2. 入栈保存现场
  3. 出栈恢复现场(包含了lr的值恢复到pc,将spsr的值拷贝到cpsr)

irq和fiq的区别?

1.irq是普通的低优先级中断,fiq是高优先级中断

 fiq可以打断irq,irq不能打断irq,只能按照顺序处理

2.fiq的响应速度快

  1. 多了5个私有寄存器
  2. 在中断向量表的最末端,可以直接写中断处理函数
  3. 优先级高

举例:k2产生irq按键中断

1.查看原理图

 

引脚:GPX1_1   外部中断:XEINT9(9号外部中断源)

2.查看芯片手册

 

 

中断要进入到cpu,最终被处理(调用中断处理函数),是要过五关斩六将的,是要把各个门打开,才能进去的

按下按键产生中断--》GPIO控制器要对中断使能-->GIC控制器的各个环节要对中断使能--》cpu0要对中断使能--》中断被处理

写代码需要干什么?

  1. 设置GPIO引脚功能
  2. GPIO控制器对中断使能
  3. GIC控制器对中断使能以及完成分发工作(选择cpu)
  4. cpu接口对中断使能
  5. 中断处理相关代码

 

当中断产生后,会先进入GPIO控制器,所以首先需要在GPIO控制器中对中断进行使能,接下来会进入到GIC,第一步需要GIC对中断进行使能,第二步,需要选择哪个接口来处理中断(分发),第三步,进行分发使能,同意这样选择,第四步,接口使能,第五步,设置优先级的门槛,优先级的取值范围是0-255,数字越大,优先级越小,所以一般就设置为255,允许所有优先级的中断通过

GIC可以产生三种类型的中断:

SGI:软件产生的中断

PPI:私有中断  只能由某一个cpu处理

SPI:共享中断  可以由cpu0-cpu3的任何一个处理   k2产生的中断

SGI、PPI、SPI一共有160个,每个中断都有自己的ID号,ID号是0-159

其中:SGI  16个  0-15

  PPI  16个  0-15

  SPI  128个  0-127

我们之后在使用的使用的时候,有些地方用的是SPI内部的0-127这个编号,有些地方又只能使用0-159这个编号

经过查找:k2(EINT9)的id号是57,SPI port no是25

 

一共有160个中断源,而一个寄存器只有32位,一个寄存器的位对应一个中断源,那也得5个寄存器才能表示完这个多中断源,经过查找,我们k2产生的中断对应的寄存器是ICDISER1,我们想让cpu0处理,所以寄存器的名字叫ICDISER1_CPU0

注意:

1.如果要使用绝对地址,链接地址和运行地址必须一样,所以我们需要改变链接地址为0x40008000,但是我们要使用的绝对地址是0,当中断产生时,cpu会去真正的0地址找中断向量表,而此时我们自己写的中断向量表在0x40008000这个地址,所以需要告诉cpu,将0x40008000这个地址作为0地址来使用

2、当中断产生时,GIC和GPIO控制器会自动设置这个中断对应的标志位,表示这个中断还没有被处理,让这个中断处于一个悬而未决的状态,所以当我们的中断被处理完之后,记得把中断标志位清除掉,否则cpu就会认为这个中断还没有被处理

在前几天点亮led灯、看门狗,及uart通信的文件基础下,添加irq.c文件、irq.h文件,及修改start.s汇编文件,map.lds链接脚本文件和makrfile工程文件

irq.c文件:

#include "irq.h"
#include "uart.h"
#include "led.h"void irq_init()
{/*********GPIO*****************///设置GPX1_1引脚的功能为外部中断GPX1CON = GPX1CON | 0XF << 4;//设置中断触发方式为下降沿触发EXT_INT41_CON = EXT_INT41_CON & ~(0xf << 4) | 0x2 << 4;//在GPIO控制器中使能中断EXT_INT41_MASK &= ~(0x1 << 1);/*********GIC******************///使能GICICDISER1_CPU0 |= 0X1 << 25;//分发ICDIPTR14_CPU0 = ICDIPTR14_CPU0 & ~(0XFF << 8) | 0X1 << 8;//分发使能ICDDCR = 1;//接口使能ICCICR_CPU0 = 1;//设置优先级门槛ICCPMR_CPU0 = 255;
}void do_irq()
{//得到当前中断的id号int intId = ICCIAR_CPU0 & 0x3ff;//不同的中断进行不同的处理switch(intId){case 57:uart_putc('k');uart_putc('2');//清GPIO中的中断标志位EXT_INT41_PEND |= 0X1 << 1;//清GIC中的中断标志位ICDICPR1_CPU0 |= 0x1 << 25;break;default:uart_putc('e');}//告诉CPU0,中断处理完了ICCEOIR_CPU0 = ICCEOIR_CPU0 & ~0X3FF | intId;
}

irq.h文件:

#ifndef _IRQ_H_
#define _IRQ_H_//#define GPX1CON *((volatile unsigned int *)0x11000C20)//在led灯的文件代码中该寄存器已被使用
#define EXT_INT41_CON *((volatile unsigned int *)0x11000E04)
#define EXT_INT41_MASK *((volatile unsigned int *)0x11000f04)
#define ICDISER1_CPU0 *((volatile unsigned int *)0x10490104)
#define ICDIPTR14_CPU0 *((volatile unsigned int *)0x10490838)
#define ICDDCR *((volatile unsigned int *)0x10490000)
#define ICCICR_CPU0 *((volatile unsigned int *)0x10480000)
#define ICCPMR_CPU0 *((volatile unsigned int *)0x10480004)
#define ICCIAR_CPU0 *((volatile unsigned int *)0x1048000C)
#define EXT_INT41_PEND *((volatile unsigned int *)0x11000F44)
#define ICDICPR1_CPU0 *((volatile unsigned int *)0x10490284)
#define ICCEOIR_CPU0 *((volatile unsigned int *)0x10480010)void irq_init();
void do_irq();#endif

start.s汇编文件:

.global _start_start:
.text@异常向量表
b reset                @reset
nop                    @undef
nop
nop                    @prefetch abort
nop                    @data abort
nop                    @reserved
b irq_handler          @irq
nop                    @fiqirq_handler:@入栈保存现场sub lr,#4stmfd sp!, {r0-r12, lr}bl do_irq
irq_handler_end:@出栈恢复现场ldmfd sp!, {r0-r12, pc}^reset:@告诉cpu,把0x40008000当成0地址ldr r0, =0x40008000mcr p15,0,r0,c12,c0,0@切换到irq模式mrs r0, cpsrbic r0, #0x1forr r0, #0x12@使能irq中断bic r0, #0x80msr cpsr, r0@给irq模式的sp赋值ldr sp,=stack_irq_top@切换到user模式mrs r0, cpsrbic r0, #0x1forr r0, #0x10msr cpsr, r0@给user模式的sp赋值ldr sp,=stack_user_topb main.data
stack_user:.space 1024
stack_user_top:stack_irq:.space 100
stack_irq_top:
.end

map.lds链接脚本文件:

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{. = 0x40008000;    @要使用绝对地址,链接地址和运行地址必须一样. = ALIGN(4);.text :{*(.text)}. = ALIGN(4);.data : { *(.data) }. = ALIGN(4);.bss :{ *(.bss) }
}

makefile工程文件:

CROSS = arm-none-linux-gnueabi-
CC=$(CROSS)gcc
LD=$(CROSS)ld
OBJCOPY=$(CROSS)objcopyall:$(CC) -g -c -o main.o main.c $(CC) -g -c -o irq.o irq.c $(CC) -g -c -o led.o led.c $(CC) -g -c -o start.o start.s  	 	$(CC) -g -c -o wdt.o wdt.c  $(CC) -g -c -o pwm.o pwm.c $(CC) -g -c -o uart.o uart.c  	$(LD) start.o led.o uart.o irq.o main.o wdt.o pwm.o -Tmap.lds -o led.elf$(OBJCOPY)  -O binary -S led.elf led.bin$(CROSS)objdump -D led.elf > led.disclean:rm -f *.o *.elf *.bin *.dis

本文标签: ARM