这个框图有两条路,第一条路是外部中断控制器,另外一条是事件控制器。在这一节我们主要关注的是外部中断控制器,也就是上面那条路。
我们从输入线开始沿着这条路来讲一下相关寄存器的配置:
首先是输入线,选择输入线的寄存器是AFIO_EXTICR1(外部中断配置寄存器),通过控制这个寄存器我们就可以选择EXTIx外部中断输入源。
然后会进入边沿检测电路,通过对EXTI_RTSR(上升沿触发选择寄存器)或者是EXTI_FTSR(下降沿触发选择寄存器)中输入线相应的位置1,就可以允许该输入线上的上升沿或者下降沿触发中断检测电路的输出为1。
再然后就会进入一个“或门”此时我们需要在EXTI_SWIER(软件中断事件寄存器)相应位置1,这里需要注意,这一步主要是为了让EXTI_PR(请求挂起寄存器)相应位自动置1,而不是我们后边再对EXTI_PR(请求挂起寄存器)相应位写1,这个挂起寄存器比较特殊。
最后我们需要配置EXTI_IMR(中断屏蔽寄存器)相应位,开放中断线上的请求,因为最后是一个“与门”只有两条路均为1的时候它另外一端才会输出1,如果你不开放中断线上请求,也就是该位仍然为0,在“与门”输出端将会是0。
框图外的就是NVIC中断控制器的配置,在下一部分会进行详细描述。
NVIC两个相关的头文件:
core_cm3.h
misc.h
NVIC特性:
68个可屏蔽中断通道(不包含16个Cortex™-M3的中断线);
16个可编程的优先等级(使用了4位中断优先级);
低延迟的异常和中断处理;
电源管理控制;
系统控制寄存器的实现;
NVIC编程的参考手册:
《 STM32F10xxx Cortex-M3编程手册》
typedef struct
{
__IO uint32_t ISER[8]; /* 中断使能寄存器 */
uint32_t RESERVED0[24];
__IO uint32_t ICER[8]; /* 中断清除寄存器 */
uint32_t RSERVED1[24];
__IO uint32_t ISPR[8]; /* 中断使能悬起寄存器 */
uint32_t RESERVED2[24]; /* 中断清除悬起寄存器 */
__IO uint32_t ICPR[8]; /* 中断有效位寄存器 */
uint32_t RESERVED3[24];
__IO uint32_t IABR[8]; /* 中断优先级寄存器 8Bit*/
uint32_t RESERVED4[56];
__IO uint8_t IP[240]; /* 中断触发寄存器 */
uint32_t RESERVED5[644];
__O uint32_t STIR;
} NVIC_Type;
========================================================================================
NVIC_PriorityGroup | PreemptionPriority | SubPriority | Description
=======================================================================================
NVIC_PriorityGroup_0 | 0 | 0-15 | 0 bits for pre-emption priority
| | | 4 bits for subpriority
---------------------------------------------------------------------------------------
NVIC_PriorityGroup_1 | 0-1 | 0-7 | 1 bits for pre-emption priority
| | | 3 bits for subpriority
--------------------------------------------------------------------------------------
NVIC_PriorityGroup_2 | 0-3 | 0-3 | 2 bits for pre-emption priority
| | | 2 bits for subpriority
---------------------------------------------------------------------------------------
NVIC_PriorityGroup_3 | 0-7 | 0-1 | 3 bits for pre-emption priority
| | | 1 bits for subpriority
--------------------------------------------------------------------------------------
NVIC_PriorityGroup_4 | 0-15 | 0 | 4 bits for pre-emption priority
| | | 0 bits for subpriority
=======================================================================================
具体配置优先级在我们固件库编程里面是在NVIC的结构体里面
typedef struct
{
uint8_t NVIC_IRQChannel;
/*!< Specifies the IRQ channel to be enabled or disabled. */
uint8_t NVIC_IRQChannelPreemptionPriority;
/*!< Specifies the pre-emption priority for the IRQ channel*/
uint8_t NVIC_IRQChannelSubPriority;
/*!< Specifies the subpriority level for the IRQ channel specifie*/
FunctionalState NVIC_IRQChannelCmd;
/*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel*/
} NVIC_InitTypeDef;
在EXTI中我们用到NVIC的内容其实不多,一个是使能寄存器一个是优先级寄存器,我们配置NVIC的目的也是,使能中断,一个选择我们外部中断的优先级,我们固件库编程只需要两个函数就能配置完成
NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);//NVIC初始化
NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);//选择分组
1.NVIC初始化函数
NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
2.NVIC优先级组别选择函数
NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
3.时钟使能函数
RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE); //使能GPIOE的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能AFIO的时钟,AFIO用来选择中断输入线
4.引脚初始化函数
GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
5.EXTI输入线的配置函数
GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
6.EXTI初始化函数
EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
使能中断请求:
a.初始化所使用的GPIO
b.初始化EXTI
配置中断优先级
编写中断服务函数
# ifndef __EXTI_H
# define __EXTI_H
void exti_config(void);
# endif
void exti_config(void)
{
GPIO_InitTypeDef GPIO_Initstructure;
EXTI_InitTypeDef EXTI_Initstructure;
NVIC_InitTypeDef NVIC_Initstructure;
/* 配置中断优先级 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_Initstructure.NVIC_IRQChannel = EXTI3_IRQn;
NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_Initstructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_Initstructure);
/* 初始化所使用的GPIO KEY */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE); //时钟使能
GPIO_Initstructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Initstructure.GPIO_Pin = GPIO_Pin_3;
GPIO_Init(GPIOE,&GPIO_Initstructure); //PE3初始化
/* 初始化EXTI */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
EXTI_Initstructure.EXTI_Line = EXTI_Line3;
EXTI_Initstructure.EXTI_LineCmd = ENABLE;
EXTI_Initstructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_Initstructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_Initstructure);
}
这里主要讲一下初始化EXTI部分:
/* 初始化EXTI */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能AFIO的时钟
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3); //控制AFIO寄存器,选择输入线
EXTI_Initstructure.EXTI_Line = EXTI_Line3;
/* 这里虽然看起来像是选择输入线,但其实它是确定在EXTI相关寄存器中具体操作的是哪一位
,输入线的选择是在AFIO中进行的 */
EXTI_Initstructure.EXTI_LineCmd = ENABLE;
/* 中断屏蔽寄存器相应位置1,中断使能 */
EXTI_Initstructure.EXTI_Mode = EXTI_Mode_Interrupt;
/* 模式选择 */
EXTI_Initstructure.EXTI_Trigger = EXTI_Trigger_Falling;
/* 触发模式选择 */
EXTI_Init(&EXTI_Initstructure);
写在stm32f10x_it.h头文件中,也可以直接写在EXTI.h的头文件中:
void EXTI3_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3) != RESET)
{
GPIOB->ODR ^= GPIO_Pin_5;
}
EXTI_ClearITPendingBit(EXTI_Line3); //清除中断标志寄存器
}
这里实现的功能是按一下KEY1可以实现灯从灭到亮或者是从亮到灭,我用的是正点原子的精英版,大家如果使用其他板子只需要将IO口换一下即可。