一、EXTI 简介

EXTI(External interrupt/event controller)—外部中断/事件控制器,管理了控制器的 20个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。

二、EXTI框图



在图中可以看到很多在信号线上打一个斜杠并标注“20”字样,这表示在控制器内部的信号线有 20 路,我们只要明白其中一路的原理即可。

  • 输入线:EXTI 控制器有 20 个中断/事件输入线,这些输入线可以通过寄存器设置为任意一个 GPIO,也可以是一些外设的事件,输入线一般是存在电平变化的信号。
  • 边沿检测电路:它会根据上升沿触发选择寄存(EXTI_RTSR)和下降沿触发选择寄存器(EXTI_FTSR)对应位的设置来控制信号触发。边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号 1 ,否则输出无效信号0。而 EXTI_RTSR 和 EXTI_FTSR 两个寄存器可以控制器需要检测哪些类型的电平跳变过程,可以是只有上升沿触发、只有下降沿触发或者上升沿和下降沿都触发。
  • NVIC中断控制器:NVIC中断控制器接收到EXTI_PR 寄存器发送的内容,实现中断的发生。
  • 脉冲发生器:脉冲发生器发送信号后会产生事件线路,脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC等等,这样的脉冲信号一般用来触发 TIM 或者 ADC开始转换。

现在可以知道EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件。产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。

三、中断/事件线

EXTI有 20个中断/事件线,每个 GPIO都可以被设置为输入线,每个输入线的来源如图所示:



从图中可以看出STM32的外部中断只有16个,又是怎么使每个GPIO都可以实现中断了,通过编程控制可以实现任意一个 GPIO作为 EXTI的输入源。



从上图可知EXTI0 可以通过 AFIO 的外部中断配置寄存器 1(AFIO_EXTICR1)的EXTI0[3:0]位选择配置为 PA0、PB0、PC0、PD0、PE0、PF0、PG0,其他 EXTI线(EXTI中断/事件线)使用配置都是类似的。

四、EXTI 寄存器

  1. 中断屏蔽寄存器(EXTI_IMR)

  2. 事件屏蔽寄存器(EXTI_EMR)

  3. 上升沿触发选择寄存器(EXTI_RTSR)



    注意: 外部唤醒线是边沿触发的,这些线上不能出现毛刺信号。在写EXTI_RTSR寄存器时,在外部中断线上的上升沿信号不能被识别,挂起位也不会被置位。在同一中断线上,可以同时设置上升沿和下降沿触发。即任一边沿都可触发中断

  4. 下降沿触发选择寄存器(EXTI_FTSR)



    注意: 外部唤醒线是边沿触发的,这些线上不能出现毛刺信号。在写EXTI_FTSR寄存器时,在外部中断线上的下降沿信号不能被识别,挂起位不会被置位。在同一中断线上,可以同时设置上升沿和下降沿触发。即任一边沿都可触发中断。

  5. 软件中断事件寄存器(EXTI_SWIER)

  6. 挂起寄存器(EXTI_PR)

  7. 外部中断/事件寄存器映像

五、EXTI 程序分析

  1. EXTI 初始化程序的结构体
typedef struct
{
uint32_t EXTI_Line; // 中断/事件线
EXTIMode_TypeDef EXTI_Mode; // EXTI 模式
EXTITrigger_TypeDef EXTI_Trigger; // 触发类型
FunctionalState EXTI_LineCmd; // EXTI 使能
}EXTI_InitTypeDef;
  1. 初始化GPIO
//将GPIOA_0设置为下拉输入
void KEY_Init(void) //IO初始化
{
GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能PORTB,PORTE时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能AFIO复用功能时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 0 pin
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 设置成下拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA_0 }
  1. 初始化EXTI

    //GPIOA_0 中断线以及中断初始化配置   下降沿触发
void EXTIX_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure; GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0); // 选择GPIOA_0作为中断EXTI0的线路 EXTI_InitStructure.EXTI_Line=EXTI_Line0; //中断/事件线
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断触发
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
}
  1. 配置 GPIO 中断
    //初始化中断
void NVICX_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能按键WK_UP所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
}
  1. 中断程序

    这里的功能是当按键触发后切换led的状态
//外部中断2服务程序
void EXTI0_IRQHandler(void)
{
Delay(100);//消抖
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1) //检测按键
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)==1) //检测led灯的状态,进行切换
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}else{
GPIO_SetBits(GPIOB,GPIO_Pin_12);
} } EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位 }

注意:配置中断函数时,先确保提供的头文件中有相应的中断函数,否则会报错



六、程序源码

main.c 文件

#include "stm32f10x.h"
#include "stm32f10x_exti.h"
#include "misc.h" /*************** 配置LED用到的I/O口 *******************/
void LED_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); // 使能PC端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //选择对应的引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PC端口
GPIO_SetBits(GPIOB, GPIO_Pin_12 ); // 关闭LED
} void Delay(__IO u32 nCount)
{
for(; nCount != 0; nCount--);
} //将GPIOA_0设置为下拉输入
void KEY_Init(void) //IO初始化
{
GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能PORTB,PORTE时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能AFIO复用功能时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 0 pin
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 设置成下拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA_0 } /* 配置PA0为外部中断 */
void EXTIX_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure; GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0); // 选择GPIOA_0作为中断EXTI0的线路 EXTI_InitStructure.EXTI_Line=EXTI_Line0; //中断/事件线
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断触发
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
} //初始化中断
void NVICX_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能按键WK_UP所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
} //外部中断2服务程序
void EXTI0_IRQHandler(void)
{
Delay(100);//消抖
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1) //WK_UP按键
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)==1)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}else{
GPIO_SetBits(GPIOB,GPIO_Pin_12);
} } EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位 } int main(void)
{
SystemInit();
LED_GPIO_Config(); //LED 端口初始化
KEY_Init();
EXTIX_Init();
NVICX_Init();
while (1)
{ } }

七、编译运行



编译完成后下载程序并观察运行现象,当GPIOA_0引脚接地一次,LED灯的状态便切换一次

参考文献

STM32系统学习——EXTI(外部中断):https://blog.csdn.net/zxh1592000/article/details/80280715

STM32之EXTI——外部中断:https://www.cnblogs.com/alvis-jing/p/3678285.html

最新文章

  1. url参数中带有+号,服务器端解码之后没了
  2. Libpci库的调用
  3. Atitit.现在的常用gui技术与gui技术趋势评价总结
  4. 一道javascript面试题
  5. Maven3路程(四)用Maven创建Struts2项目
  6. Second Day learning English
  7. [转]MySQL数据库引擎介绍、区别、创建和性能测试的深入分析
  8. 夺命雷公狗---Thinkphp----5之数据库的链接
  9. Metasploit中使用Nessus插件命令
  10. Qt for Android遇到的几个错误解决[Win7 + Qt5.6 +jdk 8u91]
  11. mysql安装详细步骤图解
  12. Lua 5.2 Reference Manual
  13. django 静态文件的配置
  14. linux server 产生大量 Too many open files CLOSE_WAIT激增
  15. win7、centos7 双系统安装总结
  16. delphi删除整个目录及目录下的文件
  17. [Unity]制作游戏中名字板的过程记录
  18. Mac-OSX下Ruby更新
  19. SOS does not support the current target architecture解决方法
  20. 剑指offer64:滑动窗口的最大值

热门文章

  1. Wireshark基本使用(1)
  2. Table.Skip删除前面N….Skip/RemoveFirstN(Power Query 之 M 语言)
  3. 判断存在…Contains…(Power Query 之 M 语言)
  4. TFTP协议介绍-python实现tftp客户端
  5. wordpress的.htaccess很容易就被挂马啊
  6. 使用PostMan测试WebService接口教程
  7. IDEA报错 Unable to open debugger port (127.0.0.1:63342): java.net.BindException "Address already in use
  8. qt5读取所有本机IP
  9. 【LeetCode】695. Max Area of Island 解题报告(Python & C++)
  10. 【LeetCode】649. Dota2 Senate 解题报告(Python)