下降沿触发 过滤器 抖动 中断编号越低优先级越高
需求:按键K2按下 打印一个'S'
看原理图:
1)按键按下(管脚输入电平 高 到 低)
2)UART_RING GPX1_1(XEINT9)
XEINT9/KP_COL1/ALV_DBG5/GPX1_1(蓝) UART_RING(红)
外部中断:XEINT9
UART_RING --》GPX1_1-->XEINT9(GPIO设置成中断管脚)
|-----》外部中断控制器
看芯片手册:GPX1_1
1)GPIO特点之一:控制172个外部中断,所以外部中断控制器也是GPIO
【GPIO】
GPX1CON[1] [7:4] 0xF = EXT_INT41[1]
【GPIO 外部中断控制器】
观看过滤器(4个):
EXT_INT41CON
EXT_INT41_FLTCON0
EXT_INT41_MASK
EXT_INT41_PEND1)EXT_INT41_CON[1] [6:4] 0x2 = Triggers Falling edge下降沿触发
2)FLTSEL16[1] [15] 0x0:关闭过滤器、0x1:启动过滤器
FLTSEL16[1] [14] 0x1:延迟过滤器、0x1:数字过滤器(时钟计数)
延时过滤器:一定时间内只处理一次中断抖动,忽略其他中断(设备自己做)
数字过滤器:设置手动过滤,在 FLTWIDTH16[1] 里面设置
FLTWIDTH16[1] [13:8] 当 FLTSEL16[1] =0x1时有效,时长=时钟*0xnn
这三者是启动过滤器,延时消除抖动,为了简单便设置默认
3)EXT_INT41_MASK[1] [1] 0x0:启动中断、0x1:屏蔽中断
4)EXT_INT41_PEND[1] [1] 0x0:没有发生、0x1:中断发生
注意:这里中断处理退出后需要将其置为1(写1清理设备),防止将其频繁处理中断没将'S'写进去,汇编中cpsr的问题
1、由于之前汇编将中断全部屏蔽,所以需要在初始化和main函数中(即切换到用户模式时)将中断启动
2、设置中断处理函数,并给中断分配栈空间
3、纠正处理中断指令返回后的地址
4、设置中断控制器 【中断设备】----【中断控制器】------【CPU】
SGI(软中断) PPI(私有中断) SPI(共享中断)
9.2.2 GIC中断(160个) SPI Port No:这类里面的中断编号 ID:全部中断的中断编号
25 57 EINT[9] 外部中断9
中断优先级0-255 最高:0 最低:255
Distributor(分发器) ->Highest poiority interrupt(根据优先级)->Interface(指定CPU核,可指定一个或者多个)
知道中断流程后,筛选的CPU的寄存器:
ICCICR_CPU0 CPU0的控制寄存器 CPU interface control register 0x10480000
ICCPMR_CPU0 CPU0的中断优先掩码控制器 Interrupt priority mask register 0x10480004
ICCBPR_CPU0 CPU0的二进制点寄存器 Binary point register 0x10480008
ICCIAR_CPU0 CPU0的中断接收确认寄存器 Interrupt acknowledge register 0x1048000C
ICCEOIR_CPU0 CPU0的中断完成确认寄存器 End of interrupt register 0x10480010
ICCABPR_CPU0 CP0的别名二进制寄存器 Aliased binary point register 0x1048001C
知道中断流程后,筛选的分发器的寄存器:
ICDDCR 分发器控制寄存器 Distributor control register 0x10490000
ICDICTR 分发器类型控制器 Interrupt controller type register 0x10490004
ICDIIDR 分发器观测寄存器 Distributor implementer identification register 08
ICDISER0_CPU0 中断使能寄存器 Interrupt set-enable register (SGI,PPI) 0x10490100
ICDICER0_CPU0 中断清理寄存器 Interrupt clear-enable register (SGI,PPI) 0x10490180进一步看CPU寄存器功能筛选寄存器:
ICCICR_CPUn 0x1 使能中断
ICCIAR_CPUn 中断接收确认 ICCEOIR_CPUn 中断完成确认
ICCPMR_CPUn 0xff 掩码,当中断优先级高于该字段表示接口信号的值时,接口发出信号中断到处理器,0-255,255为最低
ICCBPR_CPU0 拆分字段,不需要,筛出 ICCABPR_CPU0同理
进一步看分发器寄存器筛选寄存器:
ICDDCR 0x1 全局启用,用于监控外围中断信号,并将挂起的中断转发到CPU接口
ICDICTR ICDIIDR 只读的辅助寄存器,筛出
ICDISER_CPU 读取SPI、PPI的每个bit位 1=启用相应中断,启用后可使能
注意:这里设置了五个寄存器,每个中断只占寄存器中的1位,总共160位,1为启用,0为禁用,根据中断编号来确认 25 57 EINT[9],所有这里偏移地址为 0x0104 0x1<<25
【ICDICER_CPU 读取SPI、PPI的每个bit位 1=读取相应中断,启用后禁用 0x0184 0x1<<25】
ICDICPR_CPU 清除挂起位 防止被重复挂起,然后被CPU调用 1:启动 0x284 0x1<<25
ICDIPR14_CPU0 设置中断的优先级 0x0438 [15:8] 0
ICDIPTR14_CPU0 确认中断发送的目标CPU 0bxx1 CPU interface 0 0x0838 [15:8] 0x1
ICDICFR_CPU 设置触发方式,但是是只读寄存器,手册有误,筛去
Makefile:
all: arm-linux-gcc start.S -o start.o -c -g arm-linux-gcc uart.c -o uart.o -c -g arm-linux-gcc main.c -o main.o -c -g arm-linux-ld main.o uart.o start.o -o start.elf -Ttest.lds arm-linux-objcopy start.elf start.bin -O binary clean: rm *.o *.elf *.bin
start.S:
.globl _start _start: b reset 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 reset: /* 设置cpu模式为SVC模式 */ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0xd3 @11010011 msr cpsr,r0 /* 设置异常向量表起始地址 */ ldr r0, =0x41000000 mcr p15, 0, r0, c12, c0, 0 @Set VBAR /*用户需要设置的初始化*/ ldr sp, stacktop /*设置svc sp*/ sub r6, sp , #64 /*计算user需要指向的栈顶地址*/ /*切换到irq 模式*/ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0x12 msr cpsr,r0 /*给中断分配栈空间*/ mov sp, r6 /*设置irq sp*/ sub r6, sp , #64 /*切换到用户模式*/ mrs r0, cpsr bic r0, r0, #0xff /*清零,将中断开启*/ orr r0, r0, #0x10 msr cpsr,r0 mov sp, r6 /*设置user sp*/ /*跳转到应用程序*/ bl main irq_handler: /*纠正处理中断指令返回后的地址*/ sub lr,lr,#4 stmfd sp!,{r0-r12,lr} bl doirq ldmfd sp!,{r0-r12,pc}^ stack: .space 64*8 stacktop: .word stack+64*8
test.lds:
ENTRY(_start) SECTIONS{ . = 0x41000000; .text : { start.o(.text) *(.text) } .data : { *(.data) } .bss : { *(.bss) } }
uart.h:
#ifndef __MY_UART__ #define __MY_UART__ void uart_init(); void uart_send(char c); void uart_str(char* s); #endif
uart.c:
#define GPA1CON *(unsigned int volatile*)0x11400020 #define ULCON2 *(unsigned int volatile*)0x13820000 #define UCON2 *(unsigned int volatile*)0x13820004 #define UTRSTAT2 *(unsigned int volatile*)0x13820010 #define UTXH2 *(unsigned int volatile*)0x13820020 #define URXH2 *(unsigned int volatile*)0x13820024 #define UBRDIV2 *(unsigned int volatile*)0x13820028 #define UFRACVAL2 *(unsigned int volatile*)0x1382002c void uart_init() { GPA1CON = GPA1CON & ~(0xf<<4) | (0x2<<4); GPA1CON = GPA1CON & ~(0xf<<0) | (0x2<<0); ULCON2 = 0x3; UCON2 = UCON2 & ~0x3f | (0x1<<2)|(0x1<<0); UBRDIV2 = 53; UFRACVAL2 = 4; } void uart_send(char c) { while( !(UTRSTAT2&(0x1<<2)) ); UTXH2 = c; } void uart_str(char* s) { while(*s != '\0') { uart_send(*s); s++; } } char uart_recv() { while( !(UTRSTAT2&0x1) ); return (URXH2 & 0xff); }
main.c:
#include "uart.h" //GPIO将GPX1_1管脚转换成中断管脚(GPIO控制器) #define GPX1CON *(unsigned int volatile*)0x11000C20 //设置中断触发方式寄存器 #define EXT_INT41CON *(unsigned int volatile*)0x11000E04 //启动中断寄存器 #define EXT_INT41_MASK *(unsigned int volatile*)0x11000F04 //处理中断后清理中断标签寄存器 #define EXT_INT41_PEND *(unsigned int volatile*)0x11000F44 //使能中断寄存器 CPU核 #define ICCICR_CPUn *(unsigned int volatile*)0x10480000 //中断掩码寄存器,设置中断信号优先级门槛 CPU核 #define ICCPMR_CPUn *(unsigned int volatile*)0x10480004 //中断接收确认寄存器 CPU核 #define ICCIAR_CPUn *(unsigned int volatile*)0x1048000c //中断完成确认寄存器 CPU核 #define ICCEOIR_CPUn *(unsigned int volatile*)0x10480010 //分发器启用寄存器 分发器 #define ICDDCR *(unsigned int volatile*)0x10490000 //读取相应中断寄存器,后续可进行使能操作 分发器 #define ICDISER1_CPU0 *(unsigned int volatile*)0x10490104 //设置中断优先级寄存器 分发器 #define ICDIPR14_CPU0 *(unsigned int volatile*)0x10490438 //确认中断发送目标CPU寄存器 分发器 #define ICDIPTR14_CPU0 *(unsigned int volatile*)0x10490838 //清除挂起位寄存器 分发器 #define ICDICPR1_CPU0 *(unsigned int volatile*)0x10490284 void delay() { int t = 0xfffff*3; while(t--); } void doirq() { //确认接收到中断 int irq = ICCIAR_CPUn; //进行中断操作,这里是按键发送S uart_send('S'); //启动中断发生标签清理 EXT_INT41_PEND = EXT_INT41_PEND | (0x1<<1); //清除挂起位寄存器 ICDICPR1_CPU0 |= 0x1<<25; //确认完成中断 ICCEOIR_CPUn = irq; } void gic_init() { //使能中断 ICCICR_CPUn=0x1; //设置中断掩码, ICCPMR_CPUn = 0xff; //启动分发器,监控外部中断信号,并将挂起的中断转发到CPU接口 ICDDCR= 0x1; //启动查找对应中断的寄存器 ICDISER1_CPU0 |= 0x1<<25; //设置被查找到的中断的优先级为0 ICDIPR14_CPU0 = ICDIPR14_CPU0 & ~(0xff<<8); //确认中断发送的目标的CPU ICDIPTR14_CPU0 = ICDIPTR14_CPU0 & ~(0xff<<8) | (0x1<<8); } void key_init() { //将GPX1_1管脚转换成中断管脚 GPX1CON = GPX1CON | (0xf<<4); //设置下降沿触发方式(先清后置) EXT_INT41CON = EXT_INT41CON & ~(0x7<<4)|(0x2<<4); //启动中断 EXT_INT41_MASK = EXT_INT41_MASK & ~(0x1<<1); } void main() { //分发器初始化 gic_init(); uart_init(); //设置中断触发方式 key_init(); while(1); }
结果:
## Starting application at 0x41000000 ... SSSSSSSSSSSSSSSSSSSSSSSSSSS
1、需求: 按键按下 打印一个'S'
2、原理图:
1)按键按下 (管脚输入电平 高 到 低 )
2)UART_RING--》GPX1_1 --->XEINT9 (GPIO设置成中断管脚)
|-------》外部中断控制器
3、芯片手册:
【GPIO】
GPX1CON [7:4] 0xF = EXT_INT41[1]
【外部中断控制器 GPIO】
EXT_INT41_CON [6:4] 0x2 = Triggers Falling edge
EXT_INT41_MASK [1] 0x0 = Enables Interrupt
EXT_INT41_PEND [1] 0x1
4、写代码【GIC】
25 57 – EINT[9]
【Distributor】
Enabling the forwarding of interrupts to the CPU interfaces globally. (全局通道 distributor --> face) ICDDCR= 0x1;
Enabling or disabling each interrupt. (中断使能) ICDISER1_CPU0 |= 0x1<<25;
Setting the priority level of each interrupt. (中断优先级 :0) ICDIPR14_CPU0 [15:8] 0
Setting the target processor list of each interrupt. (中断发往的cpu : cpu0 ) ICDIPTR14_CPU0 [15:8] 00000001
Setting each peripheral interrupt to be level-sensitive or edge-triggered.设置中断触发方式
(请pending状态) ICDICPR_CPU0 |= 0x1<<25;
【cpu interface】
Enabling the signaling of interrupt requests by the CPU interface.(使能异常) ICCICR_CPUn=0x1;
Acknowledging an interrupt. (应答收到了中断) ICCIAR_CPUn
Indicating completion of the processing of an interrupt. (应答中断处理完成) ICCEOIR_CPUn
Setting an interrupt priority mask for the processor. (优先级屏蔽码) ICCPMR_CPUn = 0xff;