cortex-A7的内核
【按键中断实验原理】
实验目的:通过按键触发事件并产生中断然后控制对应的led灯亮或灭。
查询硬件原理图可知key1对应着PF9引脚,key2对应着PF7引脚,key3对应着PF8引脚。
并且根据原理图可知当按键按下后对应的引脚应该从高电平转变为低电平
从对应的引脚来看本次实验有需要使用到GPIO模块,由于本次实验结合了中断,所以还需要添加中断所使用的模块。
【中断的基本原理】
按键按下后会被GPIO模块捕获并传递到exti模块产生一个事件,随后由这个事件转换成中断传递到GIC模块对中断进行处理,对中断进行相关设置后交由CPU1或者CPU0处理并执行对应的代码。具体工作框图如下:
从框图中可以看出,本次实验需要使用到GPIO模块,EXTI模块、GIC模块、RCC使能模块。但是EXTI和GIC模块都不需要RCC使能,本次实验RCC只需要使能GPIO模块即可
【GPIO模块】
GPIO模块承担的角色是获取按键被按下的信息并产生一个事件交由EXTI模块处理。所以对GPIO模块只需要使能和设置输入模式即可。
RCC使能GPIOF组模块只需要将RCC_MP_AHB4ENSETR的第5位设置为1。
设置为输入只需要将GPIO_MODER寄存器的对应位设置为00即可
【EXTI模块】
exti模块的主要任务有:
1、设置是由哪个引脚产生事件
2、设置事件的检测模式(上升沿检测/下降沿检测)
3、取消对xxx事件的屏蔽
(4)、在每次执行完中断事件后都要清空一次事件继续标志
这三个模块分别有三个寄存器完成对应的操作
选择引脚:EXTI_EXTICRx寄存器
该寄存器用于选择引脚,但是在绑定引脚和产生的事件之前需要弄清楚引脚产生事件的机制,产生的事件一共有16种,正好对应了每组的16个引脚编号。也就是说Pxy产生EXTIy,如本次实验PF7、8、9对应EXTI7、8、9,产生的事件编号只与管脚编号有关和组别无关。该寄存器由8位控制一个管脚产生事件,一个寄存器只能管理4个管脚,所以一共是4个寄存器共同管理管脚。x对应的值是1-4.我们要设置的是7、8、9,所以就需要对EXTI_EXTICR2的第24-31位设置,EXTI_EXTICR3的第0-7位设置,EXTI_EXTICR2的第8-15位设置.查询手册后发现只需要将对应位设置成0x5即可。
选择中断模式:EXTI_FTSR1寄存器
该寄存器是设置下降沿触发的寄存器,这个寄存器每一位设置一个事件的触发方式,所以一个寄存器就可以管理16个事件,只需要把对应位设置位1即可将该事件设置为下降沿触发
取消事件屏蔽:EXTI_IMR1寄存器
该寄存器用于取消对事件的屏蔽,默认是屏蔽所有的事件,只有取消屏蔽才能使事件产生中断进入GIC模块,该寄存器也是1位设置一个事件,所以只需要把对应位设置位1
清空就绪标志:EXTI_FPR1寄存器
该寄存器用来清楚事件就绪的标志,每次处理中断结束后都要清空一次,否则会重复执行处理代码。也是只需要将对应位设置为1即可清空标志。
【GIC模块】
这个模块由两个层组成,GICD层和GICC层,他们做了不同的事情。在这个模块,事件就被转变成中断来处理了,每个事件都有着对应的中断号。本次实验的中断号为97、98、99.
由于在单片机中的事件非常多,所以对应的中断编号也非常多,有288个中断编号,所以在对这些终端编号进行设置时需要使用大量的寄存器。
【GICD层】
该层主要有以下任务:
1、使能事件,让模块能处理这个事件的中断
2、设置中断的优先级
3、选择交给哪个CPU处理
4、将事件传递给GICC层
(5)每次执行完处理代码后要将GICD层的标志位清除
使能:GICD_ISENABLERx寄存器
该寄存器用于对事件使能,每一位设置一个中断号,一个寄存器处理32个中断号,一共有9个寄存器对应288个中断号,我们要设置的是97、98、99号。我们把一个处理器能处理的中断编号数量称为处理量,可以得到以下公式:
中断编号/处理量 = 寄存器编号......对应位 (公式1)
所以我们要设置的就是第3个寄存器的第1,2,3位,分别设置为1即可使能。
设置优先级:GICD_IPRIORITYRx寄存器
该寄存器每8位设置一个中断编号,他的处理量为4,根据上面的公式1可知只需要把第24个寄存器的对应位全设置为0即可,他的设置位数有些不一样,用的是高5位设置,所以在设置的时候需要留意。
设置CPU处理:GICD_TARGETSRx寄存器
该寄存器用于设置哪个CPU处理该中断,它的处理量同样为4,也就是8位设置一个中断编号但是它只使用第0位和第1位进行设置,哪位被设置为1就交由哪个cpu处理,都设置为1则同时交给两个cpu处理。根据公式1,我们只需要把第24个寄存器中的这些位设置为01即可
传递给GICC层:GICD_CTLR寄存器
该寄存器主要用于将中断编号能传递到GICC层,只需要把寄存器的第一位设置为即可
清楚GICD层标志位:GICD_ICPENDRx
他的处理量是32,根据公式1只需要把对应位设置为1即可清除标志位
【GICC层】
该层的设置不针对于某个中断号,而是宏观的一些设置
该层的工作为:
1、设置中断掩码
2、将组0的中断交给处理器处理
(3)在中断处理程序中获取要处理的中断号
(4)执行完处理程序需要清楚GICC层的标志位
设置中断掩码:GICC_PMR
这个寄存器和设置优先级的寄存器很类似,也是使用的8位中的高5位,但是它并不针对某个中断号,所以这个寄存器只有一个。需要注意的是,这里设置的掩码必须要大于中断优先级,否则中断无法执行。这里全设置为1即可。
交给处理器处理:GICC_CTRL
将第一位设置为1即可
获取中断号:GICC_IAR
该寄存器用于在处理程序中获取中断号,以便于在处理对应的中断。这个寄存器的0-9位存放的中断号,只需要直接取出来就能使用。
清除GICC层标志位:GICC_EOIR
每次执行完中断程序后都要清除GICC里存放着的终端编号,也就是把EOIR的0-9位设置为中断编号即可把对应的终端编号的GICC层标志位清除
【总结】
本次实验的寄存器可以分成【设置】和【读写】两个步骤所需要的寄存器,在【设置】时需要初始化一些中断或者事件,在【读写】时要清除每一层的标志位,否则中断都会重复执行,在GICC层多了一个获取编号的寄存器。具体的情况如下
【实验现象】
https://blink.csdn.net/details/1667742?spm=1001.2014.3001.5501
【代码】
main.c
#include "key_inc.h"
void delay(int ms)
{
int i,j;
for(i=ms;i>0;i--)
{
for(j=2000;j>0;j--)
{}
}
}
int main()
{
key1_init();
led_init();
key2_init();
key3_init();
while(1)
{
printf("main function\n");
delay(1000);
}
return 0;
}
key_inc.c
#include "key_inc.h"
void key1_init()
{
// 使能GPIOF模块的时钟RCC
RCC->MP_AHB4ENSETR |= (0x1 << 5);
// 设置PF9为输入模式
GPIOF->MODER &= (~(0x3 << 18));
// 设置PF9产生exti9事件
EXTI->EXTICR3 &= (~(0xff << 8));
EXTI->EXTICR3 |= (0x5 << 8);
// 通过下降沿检测exti9是否发生
EXTI->FTSR1 |= (0x1 << 9);
// 取消对exti9的中断屏蔽
EXTI->C1IMR1 |= (0x1 << 9);
// 设置exti9的全局使能
GICD->ISENABLER[3] |= (0x1 << 3);
// 设置中断优先级
GICD->IPRIORITYR[24] &= (~(0x1f << 27));
// 设置cpu0处理该中断
GICD->ITARGETSR[24] |= (0x1 << 24);
// 设置组0可以被转发到GICC层
GICD->CTRL |= (0x1);
// 设置中断优先级的掩码
GICC->PMR &= (~(0x1f << 3));
GICC->PMR |= (0x1f << 3);
// 设置组0中断交给指定处理器处理
GICC->CTRL |= (0x1);
}
void key2_init()
{
// 设置PF7为输入模式
GPIOF->MODER &= (~(0x3 << 14));
// 设置PF7产生exti7事件
EXTI->EXTICR2 &= (~(0xff << 24));
EXTI->EXTICR2 |= (0x5 << 24);
// 通过下降沿检测exti7是否发生
EXTI->FTSR1 |= (0x1 << 7);
// 取消对exti7的中断屏蔽
EXTI->C1IMR1 |= (0x1 << 7);
// 设置exti7的全局使能
GICD->ISENABLER[3] |= (0x1 << 1);
// 设置中断优先级
GICD->IPRIORITYR[24] &= (~(0x1f << 11));
// 设置cpu0处理该中断
GICD->ITARGETSR[24] |= (0x1 << 8);
// 设置组0可以被转发到GICC层
GICD->CTRL |= (0x1);
// 设置中断优先级的掩码
GICC->PMR &= (~(0x1f << 3));
GICC->PMR |= (0x1f << 3);
// 设置组0中断交给指定处理器处理
GICC->CTRL |= (0x1);
}
void key3_init()
{
//设置pf8为输入模式
GPIOF->MODER &= (~(0x3 << 16));
//设置pf8出发exti8事件
EXTI->EXTICR3 &= (~(0xff));
EXTI->EXTICR3 |= (0x5);
//通过下降沿检测exti8
EXTI->FTSR1 |= (0x1 << 8);
//不屏蔽exti8事件产生的中断
EXTI->C1IMR1 |= (0x1 << 8);
//使能exti
GICD->ISENABLER[3] |= (0x1 << 2);
//设置中断优先级
GICD->IPRIORITYR[24] &= (~(0x1f << 19));
//让cpu0处理该中断
GICD->ITARGETSR[24] |= (0x1 << 16);
//可以将该事件传输到GICC层
GICD->CTRL |= (0x1);
//设置优先级掩码
GICC->PMR &= (~(0x1f << 3));
GICC->PMR |= (0x1f << 3);
//设置中断交给指定处理器处理
GICC->CTRL |= (0x1);
}
do_irq.c
#include "key_inc.h"
extern void printf(const char *fmt, ...);
unsigned int i = 0;
void do_irq(void)
{
//获取中断号
unsigned int irqno;
static int flag3 = 0;
static int flag2 = 0;
static int flag1 = 0;
irqno = GICC->IAR & (0x3ff);
switch (irqno)
{
case 99:
if(flag3 %2 == 0)
{
led3_on();
GICD->ICPENDR[3] |= (0x1 << 9);
EXTI->FPR1 |= (0x1 << 9);
}
else
{
led3_off();
GICD->ICPENDR[3] |= (0x1 << 9);
EXTI->FPR1 |= (0x1 << 9);
}
flag3++;
break;
case 97:
if (flag2 % 2 == 0)
{
led2_on();
GICD->ICPENDR[3] |= (0x1 << 7);
EXTI->FPR1 |= (0x1 << 7);
}
else
{
led2_off();
GICD->ICPENDR[3] |= (0x1 << 7);
EXTI->FPR1 |= (0x1 << 7);
}
flag2++;
break;
case 98:
if (flag1 % 2 == 0)
{
led1_on();
GICD->ICPENDR[3] |= (0x1 << 8);
EXTI->FPR1 |= (0x1 << 8);
}
else
{
led1_off();
GICD->ICPENDR[3] |= (0x1 << 8);
EXTI->FPR1 |= (0x1 << 8);
}
flag1++;
break;
default:
break;
}
GICC->EOIR |= irqno;
}