外部中断概述

       STM32的每个IO都可以作为外部中断输入。
  STM32的中断控制器支持19个外部中断/事件请求:
    线0~15:对应外部IO口的输入中断。
    线16:连接到PVD输出。
    线17:连接到RTC闹钟事件。
    线18:连接到USB唤醒事件。

  每个外部中断线可以独立的配置触发方式(上升沿,下降沿或者双边沿触发),触发/屏蔽,专用的状态位。

  从上面可以看出,STM32供IO使用的中断线只有16个,但是STM32F10x系列的IO口多达上百个,STM32F103ZET6(112),
STM32F103RCT6(51),那么中断线怎么跟io口对应呢?

外部中断通用I/O映像

在这里插入图片描述

  通过该图可知,每一个中断线都对应了7组中的对应编号的IO口(这边以ZET6:112个IO=16个 * 7组)。
    有如下关系:
      GPIOx.0映射到EXTI0
      GPIOx.1映射到EXTI1
      …
      GPIOx.15映射到EXTI15
注意:每个时间只能有一个I/O可映射到对应的中断线。

外部中断服务函数

       是不是16个中断线就可以分配16个中断服务函数呢?
  IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数(下表少写了EXTI0)

在这里插入图片描述

从表中可以看出,外部中断线5~9分配一个中断向量,共用一个服务函数外部中断线10 ~15分配一个中断向量,共用一个中断服务函数。

中断服务函数列表

       EXTI0_IRQHandler
  EXTI1_IRQHandler
  EXTI2_IRQHandler
  EXTI3_IRQHandler
  EXTI4_IRQHandler
  EXTI9_5_IRQHandler
  EXTI15_10_IRQHandler

外部中断设置步骤

       ①映射
  ②中断使能、触发模式等使能方式。
  ③编写中断服务函数

一般配置步骤:

       ①初始化IO口为输入。
    GPIO_Init();
  ② 开启IO口复用时钟。    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
  ③设置IO口与中断线的映射关系。
    void GPIO_EXTILineConfig();
  ④初始化线上中断,设置触发条件等。
    EXTI_Init();
  ⑤配置中断分组(NVIC),并使能中断。
    NVIC_Init();
  ⑥编写中断服务函数。
    EXTIx_IRQHandler();
  ⑦清除中断标志位
    EXTI_ClearITPendingBit();

外部中断常用库函数

①void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
//设置IO口与中断线的映射关系
exp: GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);

②void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
//初始化中断线:触发方式等

③ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
//判断中断线中断状态,是否发生

④void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
//清除中断线上的中断标志位

硬件电路

LED

在这里插入图片描述

按键

在这里插入图片描述

具体实现

新建模版,在HARDWARE文件夹添加exit文件。然后就是填入工程,添加头文件路径等等操作。

第一步:初始化IO口为输入

#include "stm32f10x.h"
void EXTIX_Init(void){
	GPIO_InitTypeDef GPIO_InitStruce;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_GPIOE , ENABLE);
	GPIO_InitStruce.GPIO_Mode=GPIO_Mode_IPU;//上拉输入
	GPIO_InitStruce.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_3;
	GPIO_InitStruce.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOE, &GPIO_InitStruce);
	
	GPIO_InitStruce.GPIO_Mode=GPIO_Mode_IPD;//下拉输入
	GPIO_InitStruce.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStruce.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruce);

}

第二步:开启IO口复用时钟
增加:RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO , ENABLE);

第三步:设置IO口与中断线的映射关系
该函数存在于:stm32f10x_gpio.h中
增加:GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);

第四步:初始化线上中断,设置触发条件等。
该函数存在于:stm32f10x_exti.h中
增加:

EXTI_InitTypeDef EXTI_InitStruce;
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);
	EXTI_InitStruce.EXTI_Line=EXTI_Line4;	//确定中断线
	EXTI_InitStruce.EXTI_LineCmd=ENABLE;//使能
	EXTI_InitStruce.EXTI_Mode=EXTI_Mode_Interrupt;//模式:中断
	EXTI_InitStruce.EXTI_Trigger=EXTI_Trigger_Falling;//触发方式(根据按键电路图,选择下降沿触发)
	
	EXTI_Init(&EXTI_InitStruce);

第五步:配置中断分组(NVIC),并使能中断。

该函数存在于:misc.h中
通道的参数是在stm32f10x,h中找到IRQn结尾的。
增加:

EXTI_Init(&EXTI_InitStruce);
	
	
	NVIC_InitTypeDef NVIC_InitStruce;
	NVIC_InitStruce.NVIC_IRQChannel=EXTI4_IRQn ;//设置通道
	NVIC_InitStruce.NVIC_IRQChannelCmd = ENABLE;//使能
	NVIC_InitStruce.NVIC_IRQChannelPreemptionPriority=2;//设置优先级
	NVIC_InitStruce.NVIC_IRQChannelSubPriority=2;//设置子优先级
	NVIC_Init(&NVIC_InitStruce);

这边要注意,在这同时要在main.c中先写下该函数NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置NVIC中断分组,2位抢占优先级,2位响应优先级,设置优先级后,这边填写的抢占优先级才有效。

第六步: 编写中断服务函数&清除中断标志位
该函数在启动文件CORE文件夹下的startup_stm32f10x_hd.s中:EXTI4_IRQHandler
故可在exti,c文件中编写一下程序:

void EXTI4_IRQHandler(void){
	delay_ms(10);//防抖延时
	if(KEY0 == 0){
		LED0 = !LED0;
		LED1 = !LED1;
	}
	EXTI_ClearITPendingBit(EXTI_Line4);//手动清除标志位,exti.h中
}

程序代码总结:

exti.c文件:

#include "stm32f10x.h"
#include "delay.h"
#include "exti.h"

void EXTIX_Init(void){
	GPIO_InitTypeDef GPIO_InitStruce;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_GPIOE , ENABLE);//IO口时钟
	GPIO_InitStruce.GPIO_Mode=GPIO_Mode_IPU;//上拉输入
	GPIO_InitStruce.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_3;//KEY0|KEY1
	GPIO_InitStruce.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOE, &GPIO_InitStruce);	
	
	GPIO_InitStruce.GPIO_Mode=GPIO_Mode_IPD;//下拉输入
	GPIO_InitStruce.GPIO_Pin=GPIO_Pin_0;;//KEY UP
	GPIO_InitStruce.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruce);
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO , ENABLE);//IO口复用时钟

	EXTI_InitTypeDef EXTI_InitStruce;
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);
	EXTI_InitStruce.EXTI_Line=EXTI_Line4;	//确定中断线
	EXTI_InitStruce.EXTI_LineCmd=ENABLE;//使能
	EXTI_InitStruce.EXTI_Mode=EXTI_Mode_Interrupt;//模式:中断
	EXTI_InitStruce.EXTI_Trigger=EXTI_Trigger_Falling;//触发方式(根据按键电路图,选择下降沿触发)
	
	
	EXTI_Init(&EXTI_InitStruce);
	
	
	NVIC_InitTypeDef NVIC_InitStruce;
	NVIC_InitStruce.NVIC_IRQChannel=EXTI4_IRQn;//设置通道
	NVIC_InitStruce.NVIC_IRQChannelCmd = ENABLE;//使能
	NVIC_InitStruce.NVIC_IRQChannelPreemptionPriority=2;//设置优先级
	NVIC_InitStruce.NVIC_IRQChannelSubPriority=2;//设置子优先级
	NVIC_Init(&NVIC_InitStruce);


	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);
	
	
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP;			//模式选择推挽输出
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;					//引脚选择5
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;			//速度旋转50MHz
	GPIO_Init(GPIOB, &GPIO_InitStructure);					
	//第一个参数GPIOB
	//第二个参数是一个结构体变量,要用&去取,且该结构体中有三个变量
	//要根据assert_param中去确定要填写什么样的格式数值。
	GPIO_SetBits(GPIOB, GPIO_Pin_5);
	
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP;			//模式选择推挽输出
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;					//引脚选择5
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;			//速度旋转50MHz
	GPIO_Init(GPIOE, &GPIO_InitStructure);	
	//第一个参数GPIOE	
	//第二个参数是一个结构体变量,要用&去取,且该结构体中有三个变量
	//要根据assert_param中去确定要填写什么样的格式数值。
	GPIO_SetBits(GPIOE, GPIO_Pin_5);

}

void EXTI4_IRQHandler(void){
	delay_ms(10);//防抖延时
	if(KEY0 == 0){
		LED0 = !LED0;
		LED1 = !LED1;
	}
	EXTI_ClearITPendingBit(EXTI_Line4);//手动清除标志位,exti.h中
}


exti.h

#ifndef __EXIT_H
#define __EXIT_H
#include "sys.h"

void EXTIX_Init(void);	//外部中断初始化

#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)
#define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)

#define LED0 PBout(5)	// DS0
#define LED1 PEout(5)	// DS1
#endif

main.c

#include "stm32f10x.h"			//stm32必要的头文件
#include "delay.h"
#include "exti.h"

int main(void){
	delay_init();
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置NVIC中断分组,2位抢占优先级,2位响应优先级
	EXTIX_Init();
	LED0 = 0;
	GPIO_SetBits(GPIOB, GPIO_Pin_5);
	GPIO_SetBits(GPIOE, GPIO_Pin_5);
	while(1){
		delay_ms(1000);
	}	
}


实验成果

按下按键KEY0实现两个LED的点亮与熄灭。