以KEY2控制LED3亮灭为例:
一、轮询方式
【0】检测按键k2,按键k2按下一次,灯LED2闪一次。
【1】查看原理图,连接引脚和控制逻辑
(1)按键k2 连接在GPX1_1引脚
(2)控制逻辑
k2 按下 ---- K2闭合 ---- GPX1_1 低电压
k2 常态 ---- K2打开 ---- GPX1_1 高电压
【2】查看相应的芯片手册
【2-1】循环检测GPX1_1引脚输入的电平,为低电压时,按键按下
(1)配置GPX1_1引脚功能为输入,设置内部上拉下拉禁止。
GPX1.CON = GPX1.CON &(~(0xf<<4)) ;
GPX1.PUD = GPX1.PUD & ~(0x3 << 2);
(2)循环检测:
这种轮询方式始终占着CPU,不利于操作。
二、中断方式
将K2按下时,GPX1_1引脚获得的电平,作为异常事件。使能异常处理,k2每按下一次,响应一次异常处理。SPI 传递流程如下示:
注:
Exynos4412中断控制器包括160个中断控制源,这些中断源来自 软中断(SGI),私有外部中断(PPI),公共外部中断(SPI)。
Exynos4412采用GIC中断控制器,主要是因为Contex-A9 是多核处理器,GIC(Generic Interrupt Controller)通用中断控制器用来选择使用哪个CPU接口,具体主要有两个功能:
1) 分配器: 设置一个开关,是否接收外部中断源;为该中断源选择CPU接口;
2) CPU接口: 设置一个开发,是否接受该中断源请求;
具体实现如下:
1、外设一级 ---设置 GPIO控制器
1-- 将GPX1_1引脚的上拉和下拉禁止
GPX1PUD[3:2]= 0b00;
2 -- 将GPX1_1引脚功能设置为中断功能 WAKEUP_INT1[1] --- EXT_INT41[1]
GPX1CON[7:4] = 0xf
3 -- EXT_INT41CON 配置触发电平
当前配置成下降沿触发:
EXT_INT41CON[6:4] = 0x2
4 -- EXT_INT41_FLTCON0 配置中断引脚滤波
默认就是打开的,不需要配置
5 -- EXT_INT41_MASK 中断使能寄存器
使能INT41[1]
EXT_INT41_MASK[1] = 0b0
6 -- EXT_INT41_PEND 中断状态寄存器
当GPX1_1引脚接收到中断信号,中断发生,中断状态寄存器EXT_INT41_PEND 相应位会自动置1
注意:中断处理完成的时候,需要清除相应状态位。置1清0.
EXT_INT41_PEND[1] =0b1
2、中断控制器
1-- 找到外设中断名称和GIC中断控制器对应的名称
查看芯片手册(本例:Exynos_4412 -- 9.2表)
WAKEUP_INT1[1] --- EXT_INT41[1] --- INT[9] --- SPI[25]/ID[57]
其 对应INT[9],中断ID为57 ,这是非常重要的,在后面的寄存器设置中起很大作用;
下面是外设与中断控制器处理具体流程:
2 -- GIC使能
ICDDCR =1;
使能分配器。
3 -- 使能相应中断到分配器
ICDISER.ICDISER1 |= (0x1 << 25); //57/32 =1...25 取整数(那个寄存器) 和余数(哪位)
ICDISER 用于使能相应中断到分配器,一个bit控制一个中断源,一个ICDISER可以控制32个中断源,这里INT[9] 对应的中断ID为57,所以在ICDSER1中进行设置,57/32 =1余25,所以这里在ICDISER1第25位置一。
4 -- 选择CPU接口
设置SPI[25]/ID[57]由那个cpu处理,当前设置为cpu0的irq中断
ICDIPTR.ICDIPTR14 |= 0x01<<8; //SPI25 interrupts are sent to processor 0 //57/4 = 14..1 14号寄存器的[15:8]
ICDIPTR 寄存器每8个bit 控制一个中断源
5 -- 全局使能cpu0中断处理
CPU0.ICCICR |= 0x1;
使能中断到CPU。
6 -- 优先级屏蔽寄存器,设置cpu0能处理所有的中断。
CPU0.ICCPMR = 0xFF;
3、ARM内核(cpu0)
前面两步设置好,就可以等待中断的发生了,当中断发生时,ARM内核的处理过程如下:
1-- 四大步三小步 --- 硬件
(1)拷贝 CPSR 到 SPSR_<mode>
(2)设置适当的 CPSR 位:
(2-1)--改变处理器状态进入 ARM 态
(2-2)--改变处理器模式进入相应的异常模式
(2-3)--设置中断禁止位禁止相应中断 (如果需要)
(3)保存返回地址到 LR_<mode>
(4)设置 PC 为相应的异常向量
2 -- 中断服务程序 --- start.S 汇编
3--中断处理程序 --- do_irq函数 c语言(函数原型void name(void))
(1) 读取正在处理的中断ID寄存器(ICCIAR)
irq_num = (CPU0.ICCIAR & 0x1FF);
(2)根据irq_num,分支处理中断
(3)清除中断状态位
(3-1)i.外设级,EXT_INT41_PEND |= 0x1 << 1;
(3-2)ii.GIC级,ICDICPR.ICDICPR1 |= 0x1 << 25;
(3-3)iii.CPU0级 CPU0.ICCEOIR = (CPU0.ICCEOIR & ~(0x1FF)) | irq_num;
下面是C 程序:
一、轮询方式
【0】检测按键k2,按键k2按下一次,灯LED2闪一次。
【1】查看原理图,连接引脚和控制逻辑
(1)按键k2 连接在GPX1_1引脚
(2)控制逻辑
k2 按下 ---- K2闭合 ---- GPX1_1 低电压
k2 常态 ---- K2打开 ---- GPX1_1 高电压
【2】查看相应的芯片手册
【2-1】循环检测GPX1_1引脚输入的电平,为低电压时,按键按下
(1)配置GPX1_1引脚功能为输入,设置内部上拉下拉禁止。
GPX1.CON = GPX1.CON &(~(0xf<<4)) ;
GPX1.PUD = GPX1.PUD & ~(0x3 << 2);
(2)循环检测:
while(1) { if(!(GPX1.DAT & (0x1<<1))) // 返回为真,按键按下 { msdelay(10); if(!(GPX1.DAT & (0x1<<1))) //二次检测,去抖 { GPX2.DAT |= 0x1 << 7; //Turn on LED2 mydelay_ms(500); GPX2.DAT &= ~(0x1<<7); //Turn off LED2 mydelay_ms(500); while(!(GPX1.DAT & (0x1<<1))); } } }
这种轮询方式始终占着CPU,不利于操作。
二、中断方式
将K2按下时,GPX1_1引脚获得的电平,作为异常事件。使能异常处理,k2每按下一次,响应一次异常处理。SPI 传递流程如下示:
注:
Exynos4412中断控制器包括160个中断控制源,这些中断源来自 软中断(SGI),私有外部中断(PPI),公共外部中断(SPI)。
Exynos4412采用GIC中断控制器,主要是因为Contex-A9 是多核处理器,GIC(Generic Interrupt Controller)通用中断控制器用来选择使用哪个CPU接口,具体主要有两个功能:
1) 分配器: 设置一个开关,是否接收外部中断源;为该中断源选择CPU接口;
2) CPU接口: 设置一个开发,是否接受该中断源请求;
具体实现如下:
1、外设一级 ---设置 GPIO控制器
1-- 将GPX1_1引脚的上拉和下拉禁止
GPX1PUD[3:2]= 0b00;
2 -- 将GPX1_1引脚功能设置为中断功能 WAKEUP_INT1[1] --- EXT_INT41[1]
GPX1CON[7:4] = 0xf
3 -- EXT_INT41CON 配置触发电平
当前配置成下降沿触发:
EXT_INT41CON[6:4] = 0x2
4 -- EXT_INT41_FLTCON0 配置中断引脚滤波
默认就是打开的,不需要配置
5 -- EXT_INT41_MASK 中断使能寄存器
使能INT41[1]
EXT_INT41_MASK[1] = 0b0
6 -- EXT_INT41_PEND 中断状态寄存器
当GPX1_1引脚接收到中断信号,中断发生,中断状态寄存器EXT_INT41_PEND 相应位会自动置1
注意:中断处理完成的时候,需要清除相应状态位。置1清0.
EXT_INT41_PEND[1] =0b1
2、中断控制器
1-- 找到外设中断名称和GIC中断控制器对应的名称
查看芯片手册(本例:Exynos_4412 -- 9.2表)
WAKEUP_INT1[1] --- EXT_INT41[1] --- INT[9] --- SPI[25]/ID[57]
其 对应INT[9],中断ID为57 ,这是非常重要的,在后面的寄存器设置中起很大作用;
下面是外设与中断控制器处理具体流程:
2 -- GIC使能
ICDDCR =1;
使能分配器。
3 -- 使能相应中断到分配器
ICDISER.ICDISER1 |= (0x1 << 25); //57/32 =1...25 取整数(那个寄存器) 和余数(哪位)
ICDISER 用于使能相应中断到分配器,一个bit控制一个中断源,一个ICDISER可以控制32个中断源,这里INT[9] 对应的中断ID为57,所以在ICDSER1中进行设置,57/32 =1余25,所以这里在ICDISER1第25位置一。
4 -- 选择CPU接口
设置SPI[25]/ID[57]由那个cpu处理,当前设置为cpu0的irq中断
ICDIPTR.ICDIPTR14 |= 0x01<<8; //SPI25 interrupts are sent to processor 0 //57/4 = 14..1 14号寄存器的[15:8]
ICDIPTR 寄存器每8个bit 控制一个中断源
5 -- 全局使能cpu0中断处理
CPU0.ICCICR |= 0x1;
使能中断到CPU。
6 -- 优先级屏蔽寄存器,设置cpu0能处理所有的中断。
CPU0.ICCPMR = 0xFF;
3、ARM内核(cpu0)
前面两步设置好,就可以等待中断的发生了,当中断发生时,ARM内核的处理过程如下:
1-- 四大步三小步 --- 硬件
(1)拷贝 CPSR 到 SPSR_<mode>
(2)设置适当的 CPSR 位:
(2-1)--改变处理器状态进入 ARM 态
(2-2)--改变处理器模式进入相应的异常模式
(2-3)--设置中断禁止位禁止相应中断 (如果需要)
(3)保存返回地址到 LR_<mode>
(4)设置 PC 为相应的异常向量
2 -- 中断服务程序 --- start.S 汇编
.text .global _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: ldr r0,=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 .align 4 /**** swi_interrupt handler ****/ /**** irq_handler ****/ irq_handler: sub lr,lr,#4 stmfd sp!,{r0-r12,lr} .weak do_irq bl do_irq ldmfd sp!,{r0-r12,pc}^ stacktop: .word stack+4*512 .data stack: .space 4*512
3--中断处理程序 --- do_irq函数 c语言(函数原型void name(void))
(1) 读取正在处理的中断ID寄存器(ICCIAR)
irq_num = (CPU0.ICCIAR & 0x1FF);
(2)根据irq_num,分支处理中断
switch(irq_num) { . case 57: break; .... }
(3)清除中断状态位
(3-1)i.外设级,EXT_INT41_PEND |= 0x1 << 1;
(3-2)ii.GIC级,ICDICPR.ICDICPR1 |= 0x1 << 25;
(3-3)iii.CPU0级 CPU0.ICCEOIR = (CPU0.ICCEOIR & ~(0x1FF)) | irq_num;
下面是C 程序:
#include "exynos_4412.h" #include "led.h" void delay_ms(unsigned int num) { int i,j; for(i=num; i>0;i--) for(j=1000;j>0;j--) ; } void do_irq(void) { static int a = 1; int irq_num; irq_num = CPU0.ICCIAR&0x3ff; //获取中断号 switch(irq_num) { case 57: printf("in the irq_handler\n"); if(a) led_on(1); else led_off(1); a = !a; EXT_INT41_PEND = EXT_INT41_PEND |((0x1 << 1)); //清GPIO中断标志位 ICDICPR.ICDICPR1 = ICDICPR.ICDICPR1 | (0x1 << 25); //清GIC中断标志位 break; } CPU0.ICCEOIR = CPU0.ICCEOIR&(~(0x3ff))|irq_num; //清cpu中断标志位 } /* * 裸机代码,不同于LINUX 应用层, 一定加循环控制 */ int main (void) { GPX1.CON =GPX1.CON & (~(0xf << 4)) |(0xf << 4); //配置引脚功能为外部中断 GPX1.PUD = GPX1.PUD & (~(0x3 << 2)); //关闭上下拉电阻 EXT_INT41_CON = EXT_INT41_CON &(~(0xf << 4))|(0x2 << 4); //外部中断触发方式 EXT_INT41_MASK = EXT_INT41_MASK & (~(0x1 << 1)); //使能中断 ICDDCR = 1; //使能分配器 ICDISER.ICDISER1 = ICDISER.ICDISER1 | (0x1 << 25); //使能相应中断到分配器 ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xff << 8))|(0x1 << 8); //选择CPU接口 CPU0.ICCPMR = 255; //中断屏蔽优先级 CPU0.ICCICR = 1; //使能中断到CPU led_init(); while(1) { } return 0; }