STM32F103 外部中断(EXTI)介绍以及代码

STM32 外部中断/事件控制器由19个产生事件/中断要求的边沿检测器组成。每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入线都可以被独立的屏蔽。挂起寄存器保持着状态线的中断要求。

对以上序号进行说明:

①InputLine:外部信号输入线,总共有 19 个来源,分别是 GPIO0~15(总共 16 个),正好连接到 EXTI0~15,EXTI16 连接到 PVD 输出,EXTI17 连接到 RTC 闹钟事件,EXTI18 连接到 USB,所以 /19 代表 19 个通道

②Edge Detect :边缘检测,可以是上升沿触发,也可以是下降沿触发,还可以是上升沿和下降沿都可以分别触发,对应图中 ②-1 和 ②-2

③Software interrupt event register:软件中断事件寄存器

④Pending request register:挂起寄存器

⑤interrupt mask register:中断屏蔽寄存器

整个外部中断的流程是这样的

1、选择外部信号输入线

2、信号选择以什么方式来触发,可以是上升沿或下降沿等

3、信号经过或门到 Pending request register,如果发生了触发,该寄存器的 PRx 位将置 1

4、最后,整个信号和中断屏蔽寄存器经过与门到 NVIC,这要求中断屏蔽寄存器必须置 1

如果用 GPIO 作为外部中断,需要配置 AFIO,来选择是哪个端口,可以是 GPIOA/GPIOB/GPIOC/GPIOD/GPIOE/GPIOF/GPIOG,需要注意的是,对于同一个 Pin 脚,只能选择配置一个端口,例如我配置 Pin15 为 GPIOC,那么就不能再用用 GPIOA/B/D/E/F/G 15 作为 EXTI ,如下图所示

代码设计思路:

1、初始化 GPIOC15 为下拉输入模式

2、初始化 EXTI,为上升沿触发

3、配置 NVIC

4、编写中断服务函数

5、每进一次中断,OLED 显示屏上计数就会+1

#include "stm32f10x.h"                  // Device header
/* EXTI 通过 AFIO_EXTICR1 的 EXTI0[3:0] 位选择配置为 PA0/PB0/PC0...
 * 选择用 C15 作为 EXTI 的接口,整个流程如下
 * ①初始化 GPIOC15,配置下拉输入(因为我要配置上升沿触发),配置 AFIO
 * ②初始化 EXTI
 * ③配置 NVIC
 * ④编写中断服务函数
 * ⑤用 OLED 显示屏记录进入中断的次数
 */
 //将 GPIO 初始化为浮空输入模式
 void EXTI_GPIOInit(void)
 {
     GPIO_InitTypeDef GPIO_InitStructure;
     
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//开启 AFIO 时钟配置进入中断的 GPIO 端口
     
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOC, &GPIO_InitStructure);

     GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource15);//配置 AFIO 来选择需要的 GPIO
 }
 
 //外部中断初始化
 void EXTI15_Init(void)
 {
     EXTI_InitTypeDef EXTI_InitStructure;
     EXTI_InitStructure.EXTI_Line = EXTI_Line15;//因为是 GPIOC15,所以选择为外部通道 15
     EXTI_InitStructure.EXTI_LineCmd = ENABLE;//打开中断使能
     EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//选择是中断事件还是中断
     EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//选择触发中断的方式,有上升压、下降沿还有上升和下降沿
     EXTI_Init(&EXTI_InitStructure);
 }
 
 void NVIC15_Init(void)
 {
     NVIC_InitTypeDef NVIC_InitStructure;
     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
     NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//选择中断向量表中的中断
     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
     NVIC_Init(&NVIC_InitStructure);
 }
 
 void All_Init(void)
 {
    EXTI_GPIOInit();
    EXTI15_Init();
    NVIC15_Init();
 }
#include "stm32f10x.h"                  // Device header
#include "OLED7.h"
#include "OLED.h"
#include "Delay.h"
#include "EXTI.h"

uint8_t flag = 0;
int main(void)
{
	uint8_t num = 0;
	OLED7_Init();
	All_Init();
	OLED7_ShowString(1, 1, "Hello");
	for (; ;)
	{
		if(flag)
		{
			flag = 0;
			OLED7_ShowNum(1, 8, num, 3);
			num++;
			Delay_ms(500);
		}
	}
    return 0;
}

void EXTI15_10_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line15))
	{
		flag = 1;
		EXTI_ClearITPendingBit(EXTI_Line15);
	}
}