首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >STM32中断详述——外部EXTI

STM32中断详述——外部EXTI

作者头像
秋名山码神
发布2023-04-28 10:11:21
发布2023-04-28 10:11:21
1.5K0
举报
文章被收录于专栏:码神随笔码神随笔

前置知识

  • 中断:在主程序运行过程中,出现了特定的中断源,使得CPU暂停当前正在运行中的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续执行,可以参考图1所示。

图1 中断程序图

  • 中断优先级:当有多个中断源同时请求中断的时候,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
  • 中断嵌套:其中一个中断正在运行中,又出现了一个新的更高级的中断,CPU再次暂停当前中断程序,转而运行新的程序,处理完成后依次返回。如图2所示:

图2 嵌套中断程序图

STM32 中断

STM32F1系列

  • 68个可屏蔽中断通道,包含EXTI,TIM,ADC,USART,SPI,I2C,RTC等多个外设
  • 使用NVIC统一管理中断,每个中断通道都有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级

NVIC基本结构

NVIC在STM32中,它是用来统一分配中断优先级和管理中断的,是一个内核外设,NVIC的结构图如下图3所示

图3 NVIC结构图 由于CPU是主要用来运算的,故把中断分配的任务外包给了NVIC,他有很多个输入口,可以接很多的中断口。

NVIC中断分组

在STM32中,NVIC中断分组可以分为两个级别:全局中断分组和子优先级分组。

  1. 全局中断分组:用于配置所有中断请求的优先级分组方式,可以通过SCB(System Control Block)模块中的AIRCR(Application Interrupt and Reset Control Register)寄存器进行配置。全局中断分组主要影响到普通中断的优先级顺序。
  2. 子优先级分组:用于配置同一优先级中多个中断之间的响应顺序,通过NVIC模块的IPR(Interrupt Priority Register)寄存器进行配置。

在全局中断分组中,将中断的优先级分为4组,每组优先级数目不同。可根据实际需要选择合适的分组方式。具体的分组方式如下:

  1. 0位抢占优先级和4位响应优先级(0:4):将16个主优先级分成16/1=16个组,每组只包含一个优先级。
  2. 1位抢占优先级和3位响应优先级(1:3):将16个主优先级分成16/4=4个组,每组包含4个优先级。
  3. 2位抢占优先级和2位响应优先级(2:2):将16个主优先级分成16/16=1个组,所有中断的优先级相同。
  4. 3位抢占优先级和1位响应优先级(3:1):将16个主优先级分成16/64=1/4个组,每组包含64个优先级。

在选择中断分组时,需要权衡系统的可靠性和中断响应速度。如果需要更快的中断响应速度,则应当选取更高的优先级;如果需要更稳定的系统,则应降低优先级。

EXTI外部中断

在STM32单片机中,可以使用外部中断输入线(EXTI)来实现外部中断的响应。EXTI线为双边沿触发,可以同时支持上升沿和下降沿触发中断,还支持软件触发和信号线电平状态查询等多种功能。

在使用STM32中的EXTI外部中断时,需要注意以下几点:

  1. 配置GPIO引脚:首先需要将要使用的GPIO引脚配置为输入模式,同时使能外部中断线。
  2. 配置EXTI线:选择要使用的中断线并配置其触发方式,例如上升沿、下降沿、低电平、高电平等。
  3. 编写中断服务函数:当外部中断触发时,会跳转到对应的中断服务函数进行处理。需要编写相应的中断服务函数来响应中断事件。
  4. 关闭中断:在中断服务函数中对紧急情况进行处理后,需要及时将中断屏蔽以避免中断重复触发。

以下是一个简单的STM32EXTI外部中断的示例:

代码语言:javascript
复制
#include "stm32f10x.h"

void EXTI0_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        /* 处理中断事件 */

        /* 关闭中断 */
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

int main()
{
    /* 使能GPIO引脚和EXTI线 */
    GPIO_InitTypeDef GPIO_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;
    
    /* 配置GPIO引脚 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    /* 配置EXTI线 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
    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);
    
    /* 配置中断优先级 */
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    while(1)
    {
        /* 正常运行代码 */
    }
}

定义了一个名为EXTI0_IRQHandler的中断服务函数,通过读取EXTI_GetITStatus函数来检测是否有中断请求产生,执行完中断服务函数后,需要使用EXTI_ClearITPendingBit清除中断标志位并关闭中断。

上面的例子可能还是有点晦涩,我们先来看一下EXTI的基本结构,如图4所示:

图4 EXTI基本结构图

在上图中AFIO、EXTI、NVIC都有其特定的作用:

  • AFIO(Alternate Function I/O):是一种外设复用功能,可以将单个GPIO端口的复用功能分配给多个外设,例如复用其他串行通信接口或IO端口。在外部中断中,为了允许对每个I/O引脚选择不同的中断线,需要使用AFIO来配置GPIO端口的复用功能,从而定义中断线的连接。
  • EXTI(External Interrupt/Event Controller):是管理外设中断/事件的控制器,通常用于激活处理器中的中断。EXTI外部中断线和GPIO Pin相关联,当线上电平发生变化时,EXTI会触发一次中断请求,并产生中断标志位。通过操作EXTI, 可以设置中断线的触发方式和优先级等参数。
  • NVIC(Nested Vectored Interrupt Controller):是STM32芯片中处理各种中断请求的一个内部模块,支持嵌套中断机制,用于优化系统的多任务管理。NVIC中,优先级分组分为:抢占优先级分组和响应优先级分组。抢占优先级越高,CPU在处理该中断时,会放弃低优先级中断的响应并快速地进入该中断处理函数中。

在STM32外部中断的使用中,这三个组件经常一同出现:

  1. AFIO和GPIO可以一起配置中断线路和端口,以便将输入引脚映射到正确的外部中断线路。
  2. EXTI和系统中断溢出控制器协同工作,确定是否还有活动中断,以及确保在执行给定中断的处理程序之前没有丢失或覆盖其他中断。
  3. NVIC控制中断处理程序的优先级,以确保在中断“堆栈”中按正确顺序处理多个中断。

下面我们通过一个红外传感器计次的代码来实际感受一下,外部中断:

红外传感器计次

代码语言:javascript
复制
#include "stm32f10x.h"                  // Device header

uint16_t CountSensor_Count;

void CountSensor_Init(void)
{
	//使用的GPIOB端口和复用中断功能,为外部中断线提供供电
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	
	//定义的GPIO_InitTypeDef结构体为GPIO配置结构体,用来给GPIOB14配置输入模式和上拉输入(Internal Pull Up)。GPIOB14的GPIO_Pin定义为GPIO_Pin_14,其它参数与对应引脚电路方案有关,根据需要进行修改,例如GPIO_Speed为GPIO_Speed_50MHz,这是为了配置管脚输出的最大时钟速度,也就是所有的管脚寄存器(ODR和BSR等,不包括MODER)都运行在50MHz的时钟下
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	//告诉NVIC如何映射中断线。使用GPIOB14时,可以将其用于EXTI15线路上,以便将此引脚映射到正确的线路
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
	
	//EXTI_InitTypeDef结构体类型和EXTI_Init()函数来初始化外部中断功能。针对 GPIOB14,需要使用EXTI_Line14枚举类型来选择要使用的中断线路。ENABLE命令行参数用于使能配置好的中断线路。EXTI_Mode设置为EXTI_Mode_Interrupt来使用EXTI的中断模式,而EXTI_Trigger则表示使用下降沿触发中断模式。这些配置后,再调用EXTI_Init()函数注册到 NVIC 的 EXTI 列表中,以确保能够正确响应中断事件
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line = EXTI_Line14;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	EXTI_Init(&EXTI_InitStructure);
	
	//在第二组中断中优先级分组,一共可以实现16个不同的优先级级别,包括4个抢占优先级和4个响应优先级。该函数可以用于设置 NVIC 的中断优先级分组,在 NVIC 中按照优先级的设置顺序执行相应中断服务程序。NVIC_PriorityGroup_2 表示在一个共享优先级中,两位用于变换优先级设置
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//首先定义了一个 NVIC_InitTypeDef 结构体类型,用于存储 NVIC 的各项参数信息。然后通过 .NVIC_IRQChannel 参数设置了 EXTI15_10 对应的中断通道,这表示配置的是 EXTI 产生的中断事件,其中 EXTI15_10 表示相关管脚的最大ID号,表示将配置EXTI的从15到10的中断响应优先级。接着,NVIC_InitStructure.NVIC_IRQChannelCmd设置为ENABLE以告诉 NVIC 此中断通道该项参数已经完成配置,可以开始中断处理。而 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 和NVIC_InitStructure.NVIC_IRQChannelSubPriority 这两项参数则是设置相应的抢占优先级和响应优先级。抢占优先级从 0 到 15,数值最小的优先级最高,而响应优先级的取值范围与抢占优先级的取值范围相同,因为这里两个通道的具体优先级是一样的,所以都设置为 1,。最后,调用函数 NVIC_Init(),向 NVIC 注册中断服务函数,这样 NVIC 就知道该如何优先处理 EXTI 捕获到的中断事件了。执行这些代码后,配置的 EXTI 引脚成功启动,产生中断信号后外部中断的优先级也正常被处理。
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
}

uint16_t CountSensor_Get(void)
{
	return CountSensor_Count;
}

void EXTI15_10_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line14) == SET)
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
		{
			CountSensor_Count ++;
		}
		EXTI_ClearITPendingBit(EXTI_Line14);
	}
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-04-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前置知识
  • STM32 中断
    • NVIC基本结构
      • NVIC中断分组
  • EXTI外部中断
    • 红外传感器计次
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档