EXTI
目录
一、工作原理
1.1.EXTI模块简介

**EXTI(External Interrupt and Event ControllerI):**外部中断和事件控制器
EXTI检测引脚电平的高低变化
GPIO接收引脚输入的高低电平
类似于一个铃铛挂在每组GPIO上的IO引脚上,检测到电平变化时就触发中断
在中断响应函数中编写处理代码,当电平发生变化时就可以自动执行相应程序

1.2.EXTI应用举例

通过创建previous、current两个变量捕捉按钮按下和松开的瞬间

通过EXTI检测电平的上升沿和下降沿产生中断,在中断函数中切换LED亮灭状态
1.3.线的概念
EXTI中一共有20条线,可以产生20路不同的中断


一组GPIO引脚最多16个
EXIT中断数量也为16个
用复用器将这条EXTI线分配给GPIO的某一个组对应编号的引脚
同一时刻只能有一组GPIO对应编号的引脚使能这条EXTI的功能

1.4.线的内部结构

复用器将线分配给GPIO的某一个组对应编号的引脚
边沿检测电路从输入信号中检测上升沿和下降沿
上升沿和下降沿经过或门可产生双边沿信号
将上升沿、下降沿、双边信号输入到复用器
软件触发:只需向软件写0或写1就可以触发
二、按钮实验
2.1.搭建电路


cpp
#include "stm32f10x.h"
//声明板载LED初始化函数
void App_OnBoardLED_Init(void);
//声明按钮初始化函数
void App_Button_Init(void);
int main(void)
{
App_OnBoardLED_Init();
App_Button_Init();
while(1)
{
}
}
//创建板载LED初始化函数
void App_OnBoardLED_Init(void)
{
/*启动GPIOC时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PC13引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
/*通用输出开漏模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PC13引脚*/
GPIO_Init(GPIOC,&GPIO_InitStruct);
/*熄灭板载LED*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
}
//创建按钮初始化函数
void App_Button_Init(void)
{
/*启动GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA5引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA5引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*选择PA6引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA6引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
}
2.2.分配EXTI线
cpp
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource,uint8_t GPIO_PinSource);
解析:
**参数1:**端口号
- GPIO_PortSourceGPIOA
- GPIO_PortSourceGPIOB
- GPIO_PortSourceGPIOC
- GPIO_PortSourceGPIOD
**参数2:**线编号
- GPIO_PinSource0 ~ GPIO_PinSource15
**作用:**使用AFIO模块为EXTI的线选择IO引脚
cpp
//创建按钮初始化函数
void App_Button_Init(void)
{
//#1:初始化PA5和PA6
/*启动GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA5引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA5引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*选择PA6引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA6引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#2:为EXTI5和EXTI6分配引脚
/*开启AFIO的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/*线5选择端口A*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource5);
/*线6选择端口A*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource6);
}
2.3.配置EXTI线的参数
cpp
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
解析:
**参数:**初始化的参数结构体地址
**作用:**用来初始化EXTI的一条线
**补充:**EXTI_InitTypeDef结构
cpp
struct EXTI_InitTypeDef
{
uint32_t EXTI_Line;
EXTIMode_TypeDef EXTI_Mode;
EXTITrigger_TypeDef EXTI_Teigger;
FunctionalState EXTI_LineCmd;
};
分析:
**1.EXTI_Line:**线编号
- EXTI_Line0 ~ EXTI_Line19
**2.EXTI_Mode:**选择模式
- EXTI_Mode_Interrupt:中断
- EXTI_Mode_Event:事件
**3.EXTI_Teigger:**边沿
- EXTI_Teigger_Rising:上升沿
- EXTI_Teigger_Falling:下降沿
- EXTI_Teigger_Rising_Falling:双边沿
**4.EXTI_LineCmd:**开关
- ENABLE:闭合
- DISABLE:断开
cpp
//创建按钮初始化函数
void App_Button_Init(void)
{
//#1:初始化PA5和PA6
/*启动GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA5引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA5引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*选择PA6引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA6引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#2:为EXTI5和EXTI6分配引脚
/*开启AFIO的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/*线5选择端口A*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource5);
/*线6选择端口A*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource6);
//#3:初始化EXTI的线
/*声明EXTI结构变量*/
EXTI_InitTypeDef EXTI_InitStruct;
/*选择EXTI_Line5*/
EXTI_InitStruct.EXTI_Line = EXTI_Line5;
/*中断模式*/
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
/*上升沿采集*/
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
/*闭合开关*/
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
/*初始化Line5*/
EXTI_Init(&EXTI_InitStruct);
/*选择EXTI_Line6*/
EXTI_InitStruct.EXTI_Line = EXTI_Line6;
/*中断模式*/
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
/*上升沿采集*/
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
/*闭合开关*/
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
/*初始化Line6*/
EXTI_Init(&EXTI_InitStruct);
}
**注:**EXTI模块比较特殊,无需使能EXTI模块的时钟
2.4.配置NVIC模块

cpp
int main(void)
{
/*设置中断优先级分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
App_OnBoardLED_Init();
App_Button_Init();
while(1)
{
}
}
//创建按钮初始化函数
void App_Button_Init(void)
{
//#1:初始化PA5和PA6
/*启动GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA5引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA5引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*选择PA6引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA6引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#2:为EXTI5和EXTI6分配引脚
/*开启AFIO的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/*线5选择端口A*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource5);
/*线6选择端口A*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource6);
//#3:初始化EXTI的线
/*声明EXTI结构变量*/
EXTI_InitTypeDef EXTI_InitStruct;
/*选择EXTI_Line5*/
EXTI_InitStruct.EXTI_Line = EXTI_Line5;
/*中断模式*/
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
/*上升沿采集*/
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
/*闭合开关*/
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
/*初始化Line5*/
EXTI_Init(&EXTI_InitStruct);
/*选择EXTI_Line6*/
EXTI_InitStruct.EXTI_Line = EXTI_Line6;
/*中断模式*/
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
/*上升沿采集*/
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
/*闭合开关*/
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
/*初始化Line6*/
EXTI_Init(&EXTI_InitStruct);
//#4:配置中断
/*声明NVIC结构变量*/
NVIC_InitTypeDef NVIC_InitStruct;
/*设置EXTI9_5中断名称*/
NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn;
/*设置抢占优先级*/
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
/*子优先级*/
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
/*闭合中断开关*/
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
/*初始化NVIC*/
NVIC_Init(&NVIC_InitStruct);
}
**注:**在stm32f10x.h中搜索枚举IRQn,寻找中断的名称
2.5.编写中断响应函数
2.5.1.确定中断响应函数的名称
打开startup文件,在中断向量表中找中断响应函数
cpp
void EXTI9_5_IRQHandler(void);
2.5.2.判断哪条线触发了中断
cpp
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
解析:
**参数:**线编号
返回值:
- 标志位为0,返回RESET
- 标志位为1,返回SET
cpp
//创建EXTI9_5中断服务函数
void EXTI9_5_IRQHandler(void)
{
/*如果是线5触发的中断*/
if(EXTI_GetFlagStatus(EXTI_Line5) == SET)
{
/*亮灯*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
}
/*如果是线6触发的中断*/
if(EXTI_GetFlagStatus(EXTI_Line6) == SET)
{
/*灭灯*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
}
}
2.5.3.清除标志位
cpp
void EXTI_ClearFlag(uint32_t EXTI_Line);
cpp
//创建EXTI9_5中断服务函数
void EXTI9_5_IRQHandler(void)
{
/*如果是线5触发的中断*/
if(EXTI_GetFlagStatus(EXTI_Line5) == SET)
{
/*清除中断标志位*/
EXTI_ClearFlag(EXTI_Line5);
/*亮灯*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
}
/*如果是线6触发的中断*/
if(EXTI_GetFlagStatus(EXTI_Line6) == SET)
{
/*清除中断标志位*/
EXTI_ClearFlag(EXTI_Line6);
/*灭灯*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
}
}
2.6.总代码
cpp
#include "stm32f10x.h"
//声明板载LED初始化函数
void App_OnBoardLED_Init(void);
//声明按钮初始化函数
void App_Button_Init(void);
//声明EXTI9_5中断服务函数
void EXTI9_5_IRQHandler(void);
int main(void)
{
/*设置中断优先级分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
App_OnBoardLED_Init();
App_Button_Init();
while(1)
{
}
}
//创建EXTI9_5中断服务函数
void EXTI9_5_IRQHandler(void)
{
/*如果是线5触发的中断*/
if(EXTI_GetFlagStatus(EXTI_Line5) == SET)
{
/*清除中断标志位*/
EXTI_ClearFlag(EXTI_Line5);
/*亮灯*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
}
/*如果是线6触发的中断*/
if(EXTI_GetFlagStatus(EXTI_Line6) == SET)
{
/*清除中断标志位*/
EXTI_ClearFlag(EXTI_Line6);
/*灭灯*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
}
}
//创建板载LED初始化函数
void App_OnBoardLED_Init(void)
{
/*启动GPIOC时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PC13引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
/*通用输出开漏模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PC13引脚*/
GPIO_Init(GPIOC,&GPIO_InitStruct);
/*熄灭板载LED*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
}
//创建按钮初始化函数
void App_Button_Init(void)
{
//#1:初始化PA5和PA6
/*启动GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA5引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA5引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*选择PA6引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA6引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#2:为EXTI5和EXTI6分配引脚
/*开启AFIO的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/*线5选择端口A*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource5);
/*线6选择端口A*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource6);
//#3:初始化EXTI的线
/*声明EXTI结构变量*/
EXTI_InitTypeDef EXTI_InitStruct;
/*选择EXTI_Line5*/
EXTI_InitStruct.EXTI_Line = EXTI_Line5;
/*中断模式*/
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
/*上升沿采集*/
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
/*闭合开关*/
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
/*初始化Line5*/
EXTI_Init(&EXTI_InitStruct);
/*选择EXTI_Line6*/
EXTI_InitStruct.EXTI_Line = EXTI_Line6;
/*中断模式*/
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
/*上升沿采集*/
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
/*闭合开关*/
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
/*初始化Line6*/
EXTI_Init(&EXTI_InitStruct);
//#4:配置中断
/*声明NVIC结构变量*/
NVIC_InitTypeDef NVIC_InitStruct;
/*设置EXTI9_5中断名称*/
NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn;
/*设置抢占优先级*/
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
/*子优先级*/
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
/*闭合中断开关*/
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
/*初始化NVIC*/
NVIC_Init(&NVIC_InitStruct);
}
