STM32_EXTI外部中断学习笔记

发布者:雅意盎然最新更新时间:2019-02-18 来源: eefocus关键字:STM32  EXTI  外部中断 手机看文章 扫描二维码
随时随地手机看文章

参考资料:《STM32F4xx中文参考手册》系统配置控制器以及中断和事件章节。


EXTI( External interrupt /evet controller)


之前接触过51单片机的都了解到51单片机有两个外部中断 ,分别为外部中断0、1。用来实时地处理外部事件的一种内部机制。当某种外部事件发生时,单片机的中断系统将迫使CPU暂停正在执行的程序,转而去进行中断事件的处理;中断处理完毕后.又返回被中断的程序处,继续执行下去。而STM32的则有与之功能相同的外部中断事件控制器。外部中断/事件控制器(EXTI)管理了控制器的 23个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。


23个中断事件线包含了16个与I/O有关的外部中断/事件线和其余七根 EXTI 线

由上图可以看出PA-PI口的各个位对应EXT0~EXTI15 共16个外部中断/事件线。


可以通过配置SYSCFG_EXTICR1~CR4来选择用于外部中断事件的I/O口。

其余7个如下图所示

下表即为所列的外部中断事件线

下图即为 外部中断/ 事件控制器框图

图中左下角的输入线即I/O口的输入状态,通过配置上升沿触发或下降沿触发选择寄存器来选择输入线的触发状态(可以是只有上升沿触发、只有下降沿触发或者上升沿和下降沿都触发),如果检测到有边沿跳变就输出有效信号 1,否则就输出无线信号0,出来的信号作为一路或门电路的输入,另外一输入来自软件中断事件寄存器(EXTI_SWIER)。EXTI_SWIER 允许我们通过程序控制就可以启动中断/事件线,当或门电路输入中有一路信号为1,那么就会输出信号1,或门电路输出的信号出来分两路,下面路作为与门的输入,通过配置事件屏蔽寄存器,两个都为1则输出为1,脉冲发生器则输出脉冲,这个脉冲信号,就是产生事件的线路最终的产物,可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC等等。或门电路出来的上面一路通过配置中断屏蔽寄存器,然后同样经过一个与门电路后输出,作为挂起请求寄存寄存器的输入,如果中断屏蔽寄存器和挂起请求寄存器同时为1,那么就可以输出作为NVIC中断控制器的输入了,即可以实现系统中断事件控制。产生中断线路目的是把输入信号输入到NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。中断在嵌入式应用中占有非常重要的地位,几乎每个控制器都有中断功能。中断对保证紧急事件得到第一时间处理是非常重要的,接下来通过一个小实验要操作一下外部中断


硬件电路图如下所示


我要实现的操作是当我每按下一次开关,LED灯改变一次状态(可以参考固件库点亮LED灯和输入按键检测以及NVIC概述)。


要利用外部中断使LED灯改变状态,首先就是需要检测I/O口的状态,当I/O口满足触发条件,就进入中断,执行中断服务程序。


在进行编程之前我们先理一理编程的思路


1、首先要初始化按键和LED相关的GPIO


2、接下来要初始化EXTI相关寄存器用来产生中断


3、初始化NVIC,用于处理中断。


4、编写中断服务函数


5、主函数


1、所以我们第一步就应该配置KEY1,KEY2的端口,那么应该配置KEY1、KEY2的GPIO为哪种状态才能与中断产生联系呢,

通过查阅手册了解到上图所表达的意思就是要使用外部中断线的话,端口必须被配置成输入模式,所以我们就要开始对GPIO进行初始化初始化包括按键GPIO端口的初始化与LED灯GPIO的初始化。首先我先把与硬件相关的宏定义放在定义好的bsp_exti.h与bsp_led.h文件中,方便程序的移植与可读性,代码如下


//引脚定义

/*******************************************************/

#define KEY1_INT_GPIO_PORT                GPIOA         //按键1端口宏定义

#define KEY1_INT_GPIO_CLK                 RCC_AHB1Periph_GPIOA //按键1端口总线时钟宏定义

#define KEY1_INT_GPIO_PIN                 GPIO_Pin_0         //按键1端口引脚宏定义

#define KEY1_INT_EXTI_PORTSOURCE          EXTI_PortSourceGPIOA //按键1中断端口宏定义

#define KEY1_INT_EXTI_PINSOURCE           EXTI_PinSource0         //按键1中断端口引脚宏定义

#define KEY1_INT_EXTI_LINE                EXTI_Line0                 //按键1外部中断线宏定义

#define KEY1_INT_EXTI_IRQ                 EXTI0_IRQn //按键1的中断请求类型宏定义

 

#define KEY1_IRQHandler                   EXTI0_IRQHandler //按键1的中断服务函数名宏定义

 

#define KEY2_INT_GPIO_PORT                GPIOC

#define KEY2_INT_GPIO_CLK                 RCC_AHB1Periph_GPIOC

#define KEY2_INT_GPIO_PIN                 GPIO_Pin_13

#define KEY2_INT_EXTI_PORTSOURCE          EXTI_PortSourceGPIOC

#define KEY2_INT_EXTI_PINSOURCE           EXTI_PinSource13

#define KEY2_INT_EXTI_LINE                EXTI_Line13

#define KEY2_INT_EXTI_IRQ                 EXTI15_10_IRQn

 

#define KEY2_IRQHandler                   EXTI15_10_IRQHandler

#include "stm32f4xx_gpio.h"

 

#define GPIO_R_Pin  GPIO_Pin_10

#define GPIO_R_Port GPIOH

 

#define GPIO_G_Pin  GPIO_Pin_11

#define GPIO_G_Port GPIOH

 

#define GPIO_B_Pin  GPIO_Pin_12

#define GPIO_B_Port GPIOH

 

/************************控制LED灯亮灭的宏***************/

 

/*直接操作寄存器的方法控制IO*/

 

#define  LED_PORT_OUT_HI(p,i) { p->BSRRL = i ;} //输出为高电平

#define  LED_PORT_OUT_LO(p,i) { p->BSRRH = i ;} //输出为低电平

#define  LED_PORT_OUT_Toggle(p,i)         { p->BSRRL ^= i ;}        //输出为反状态

 

/*定义控制IO的宏*/

 

#define LED_R_Toggle         LED_PORT_OUT_Toggle(GPIO_R_Port,GPIO_R_Pin)

#define LED_R_ON LED_PORT_OUT_LO(GPIO_R_Port,GPIO_R_Pin)

#define LED_R_OFF LED_PORT_OUT_HI(GPIO_R_Port,GPIO_R_Pin)

 

#define LED_G_Toggle         LED_PORT_OUT_Toggle(GPIO_G_Port,GPIO_G_Pin)

#define LED_G_ON LED_PORT_OUT_LO(GPIO_G_Port,GPIO_G_Pin)

#define LED_G_OFF LED_PORT_OUT_HI(GPIO_G_Port,GPIO_G_Pin)

 

#define LED_B_Toggle         LED_PORT_OUT_Toggle(GPIO_B_Port,GPIO_B_Pin)

#define LED_B_ON LED_PORT_OUT_LO(GPIO_B_Port,GPIO_B_Pin)

#define LED_B_OFF LED_PORT_OUT_HI(GPIO_B_Port,GPIO_B_Pin)

 

#define LED_RGBOFF   LED_R_OFF;\

LED_G_OFF;\

LED_B_OFF

接下来开始初始化按键的GPIO与LED的端口引脚,在初始化按键的GPIO引脚时因为要与外部中断产生联系,所以通过查阅数据手册,所以要将按键初始化为输入模式,代码分别如下。

void bsp_exti_key_gpio_config()

{

GPIO_InitTypeDef GPIO_InitStructure;


/*开启按键GPIO口的时钟*/

RCC_AHB1PeriphClockCmd(KEY1_INT_GPIO_CLK|KEY2_INT_GPIO_CLK ,ENABLE);

/* 选择按键1的引脚 */ 

        GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;

        /* 设置引脚为输入模式 */ 

        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;    

        /* 设置引脚不上拉也不下拉 */

        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

        /* 使用上面的结构体初始化按键 */

        GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);


/* 选择按键2的引脚 */ 

        GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN;  

        /* 其他配置与上面相同 */

        GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure); 

 

}

void LED_Config(void)

{

GPIO_InitTypeDef GPIO_LED_Struct;


RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOH , ENABLE);  



GPIO_LED_Struct.GPIO_Mode  =  GPIO_Mode_OUT;

GPIO_LED_Struct.GPIO_Speed =  GPIO_Fast_Speed;

GPIO_LED_Struct.GPIO_OType =  GPIO_OType_PP;

GPIO_LED_Struct.GPIO_PuPd  =  GPIO_PuPd_UP;              


GPIO_LED_Struct.GPIO_Pin   =  GPIO_R_Pin;

GPIO_Init(GPIO_R_Port, &GPIO_LED_Struct);        

 

GPIO_LED_Struct.GPIO_Pin   =  GPIO_B_Pin;

GPIO_Init(GPIO_B_Port, &GPIO_LED_Struct);


GPIO_LED_Struct.GPIO_Pin   =  GPIO_G_Pin;

GPIO_Init(GPIO_G_Port, &GPIO_LED_Struct);


LED_RGBOFF;                        

}

 


关于按键及LED的GPIO配置可以参考STM32按键输入检测和STM32固件库点亮LED灯


2、当我们配置好按键的GPIO后,接下来我们要进行EXTI的有关配置,由于23个外部中断事件线相关的外设都是挂载在APB2总线上的,所以们要线打开APB2总线上的时钟,因为STM32的外设设计的都是很棒的,当外设不工作时,都处于休眠状态,要想配置使用相关的外设,首先要打开对应的时钟(关于时钟配置这块可以参考STM32时钟树介绍)

  /* 使能 SYSCFG 时钟 ,使用GPIO外部中断时必须使能SYSCFG时钟*/

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

打开时钟后,首先要做的是什么?,肯定是要先先配置EXTI的输入线,我们要把按键的GPIO端口与EXTI联系起来,这里我们使用了STM32自带的库函数SYSCFG_EXTILineConfig函数,这个函数用来选择GPIO的引脚作为EXTI的中断事件线,函数有两个参数,一个是GPIO的端口(A~I)另一个是端口的哪个引脚(0~15),无返回值。函数说明大致意思就是这样。

  /* 连接 EXTI 中断源 到key1引脚 */

  SYSCFG_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE,KEY1_INT_EXTI_PINSOURCE);

  /* 连接 EXTI 中断源 到key2 引脚 */

  SYSCFG_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE,KEY2_INT_EXTI_PINSOURCE);

配置完输入后,接下来就要开始对EXTI进行配置,在官方的固件库exti.h文件中可以找到EXTI_InitTypeDef,进行配置,STM32的固件库非常的好用,齐全,每一种外设在对应的.h文件中都可以找到xx_InitTypeDef这个初始化结构体和xx_Init初始化配置函数,非常棒。


  EXTI_InitTypeDef EXTI_InitStructure; 

 

  /* 选择 EXTI 中断源 */

  EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;

  /* 中断模式 */

  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;

  /* 下降沿触发 */

  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;  

  /* 使能中断/事件线 */

  EXTI_InitStructure.EXTI_LineCmd = ENABLE;

  EXTI_Init(&EXTI_InitStructure);

 

  /* 选择 EXTI 中断源 */

  EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;

  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;

  /* 上升沿触发 */

  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  

  EXTI_InitStructure.EXTI_LineCmd = ENABLE;

  EXTI_Init(&EXTI_InitStructure);

好,上面的就已经把EXTI前面的准备(红色方框里的)工作都已经做好了。


3、当中断发生后,EXTI会产生一个中断请求,接来下就要配置NVIC的中断优先级等来处理中断信号,在讲配置NVIC时说,首先要使能中断源(就上是我上面配置EXTI的操作)已经完成,接下来要进行初始化 NVIC_InitTypeDef结构体。


 void NVIC_Configuration(void)

{

  NVIC_InitTypeDef NVIC_InitStructure;

  

  /* 配置NVIC为优先级组1 */

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

  

  /* 配置中断源:按键1 */

  NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;

  /* 配置抢占优先级:1 */

  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

  /* 配置子优先级:1 */

  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

  /* 使能中断通道 */

  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

  NVIC_Init(&NVIC_InitStructure);

  

  /* 配置中断源:按键2,其他使用上面相关配置 */  

  NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;

  NVIC_Init(&NVIC_InitStructure);

}

4、配置完NVIC后我们接下来就要进行第四步编写中断服务函数,中断服务函数的函数名在启动文件中都已经定义好了,注意引用的时候中断服务函数的名字不要打错,否则编译器不会报错,然后进入一个死循环当中,有关中断服务函数我建议以后所有的中断服务函数都放在一个文件中,避免出错。在编写中断服务函数时,我们首先要检查中断是否真的发生,然后执行中断程序,执行完程序后记得消除中断标志位为了下次进入中断。程序如下。


void KEY1_IRQHandler(void)

{

          //确保是否产生了EXTI Line中断

if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) 

{

// LED红色取反

LED_R_Toggle;

                //清除中断标志位

EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);     

}  

}

 

void KEY2_IRQHandler(void)

{

          //确保是否产生了EXTI Line中断

if(EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET) 

{

// LED蓝色取反

LED_B_Toggle;

                //清除中断标志位

EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);     

}  

}

5、接下来我们就编写主函数来实现实验最终现象


int main(void)

{

/* LED 端口初始化 */

LED_GPIO_Config();

 

/* 初始化EXTI中断,按下按键会触发中断,

        *  触发中断会进入stm32f4xx_it.c文件中的函数

*  KEY1_IRQHandler和KEY2_IRQHandler,处理中断,反转LED灯。

*/

         bsp_exti_key_gpio_config();

         EXTI_Key_Config();

         NVIC_Configuration() ;


/* 等待中断,由于使用中断方式,CPU不用轮询按键 */

while(1)                            

{

}

}

   好了,以上就是我在学习过程中的感悟与总结,写的不好敬请见谅。

关键字:STM32  EXTI  外部中断 引用地址:STM32_EXTI外部中断学习笔记

上一篇:SysTick_系统定时器实现流水灯
下一篇:STM32中断及NVIC概述

推荐阅读最新更新时间:2024-03-16 16:24

STM32内部Flash读写操作接口
源文件 /* ********************************************************************************************************* * 函 数 名: GetSector * 功能说明: 根据地址计算扇区首地址 * 形 参: 无 * 返 回 值: 扇区首地址 ********************************************************************************************************* */ uint32_t GetSector(uint32
[单片机]
串口通信—STM32串口功能框图讲解
STM32 的USART 简介 通用同步异步收发器(Universal Synchronous Asynchronous Receiver and Transmitter)是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。有别于USART 还有一个UART(Universal Asynchronous Receiver and Transmitter),它是在USART 基础上裁剪掉了同步通信功能,只有异步通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是UART。 串行通信一般是以帧格式传输数据,即是一帧一帧的传输,每帧包含有起始信号、数据信息、停止信息,可能还有校验信息。US
[单片机]
串口通信—<font color='red'>STM32</font>串口功能框图讲解
STM32 输入捕获 测量频率 PWM占空比
看了网上关于STM32输入捕获的资料,有几篇介绍的很不错,但是内容上还有一点问题,稍加修改,大家可以参考一下。 重要概念理解(对于理解输入捕获功能很重要,特别看了数据手册CCR1CCR2CCR3CCR3云里雾里) PWM输入捕获模式是输入捕获模式的特例,自己理解如下 1. 每个定时器有四个输入捕获通道IC1、IC2、IC3、IC4。且IC1 IC2一组,IC3 IC4一组。并且可是设置管脚和寄存器的对应关系。 2. 同一个TIx输入映射了两个ICx信号。 3. 这两个ICx信号分别在相反的极性边沿有效。 4. 两个边沿信号中的一个被选为触发信号,并且从模式控制器被设置成复位模式。 5. 当触发信号来临时,被设置成触发输入信号的捕获
[单片机]
STM32——GPIO设置:快速点亮第一个LED灯
简介 不同的开发板的原理图结构也不尽相同,笔者在这里使用野火的MINI-V3(F103VET6)简易开发板对GPIO口的设置做一个简单的介绍,并实现按键控制LED灯的亮灭。方便读者可以快速熟悉并灵活应用。 原理图分析 首先我们来看一下发光二极管部分和按键部分的原理图。 下面是按键的原理图部分: 在这里,我们将使用KEY1(PA0)来控制红灯PB5的亮灭。由原理图可知,其控制LED灯的PB5引脚为低电平时,灯亮。当KEY1按下时,PA0引脚由之前的低电平转为高电平(3V3)。明确了目的之后我们就可以分析GPIO口并进行设置了。 GPIO设置 经过上述的功能明确之后,我们将其分为两大类,分别是输入类(如按键)和
[单片机]
<font color='red'>STM32</font>——GPIO设置:快速点亮第一个LED灯
STM32 串口通讯 发送 接收
STM32的使用有利有弊,种类多---但是种类有太多,资料也是比较乱的,还有就是库的调用,经常忘记一些函数的使用------比如最常用的串口------ ------------------------------------------------------------------------------USART ----设置------------------------------- void USART1_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure;
[单片机]
基于STM32单片机的工业循环水极化控制系统设计
0 引言 工业生产中的循环水系统在运行中对淡水消耗非常大,同时,为防止工业设备结垢等现象,需要对循环水不断添加各种化学药剂,且需要不断地排放污水、补充新鲜水,这样既对水资源造成了很大的浪费又污染环境。鉴此,笔者设计了一种基于ARM的工业循环水极化控制系统。该系统通过极化场对水的极化作用 ,实现对工业循环水的处理功能,达到减少水资源消耗、避免使用化学药剂、有效防止水资源污染的目的。 1 系统总体设计方案 基于ARM 的工业循环水极化控制系统采用ST公司的STM32F103微控制器作为主控制核心,由极化能量检测电路实时检测循环水水质参数,经STM32F103运算处理后,由极化能量输出电路调整极化能量的输出,由LCD显示电路实时显
[单片机]
基于<font color='red'>STM32</font>单片机的工业循环水极化控制系统设计
STM32 使用 Keil MDK 中的软件逻辑分析仪参与硬件调试
这篇文章翻译自 ARM Keil Application Note 230 (1.2版)的前半部分。其中包括 STM32F4 处理器在 Keil MDK 中进行断点调试、变量实时观察,及逻辑分析仪参与硬件调试的实验。 原文使用的是 STM32F4-Discovery 开发板,我这里都改用 NUCLEO-F401RE 实现了。Discovery 板卡在新版本的 Pack Installer 中已没有 Blinky 例程支持,可以用 CMSIS-RTOS Blinky 来做,变量定义的位置等会有变化。 1) Keil 评估软件:MDK 4.7x 和 MDK 5 MDK 5 以 Software Pack 的形式分发特定于处理器的
[单片机]
<font color='red'>STM32</font> 使用 Keil MDK 中的软件逻辑分析仪参与硬件调试
如何利用定时器产生PWM波
摘要:利用定时器产生PWM波。然后利用32的外部中断和定时器来测量32输出的波形硬件:STM32F103C8T6核心板、示波器、串口调试助手所用到的的引脚为PA8和PA0。 测量方案:在第一次外部中断(上升沿触发)到之时,开启定时器,同时计数器清零。然后等待第二次中断到来,在第二次外部中断(上升沿触发)到之时,获取计数器的计数值,同时关闭计数器。因为知道了计数器计数一个数的时间,所以在第二次外部中断(上升沿触发)到之时,获取计数器的计数值,通过这个值就知道一个脉冲的时间周期。时间周期的倒数就是外部信号的频率。 一、利用TIM1的CH1产生PWM波 pwm.c #include pwm.h voidTIM1_PWM_Init
[单片机]
如何利用定时器产生PWM波
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved