STM32中断详述 您所在的位置:网站首页 stm外部中断 STM32中断详述

STM32中断详述

2024-03-04 13:27| 来源: 网络整理| 查看: 265

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

图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中断分组可以分为两个级别:全局中断分组和子优先级分组。

全局中断分组:用于配置所有中断请求的优先级分组方式,可以通过SCB(System Control Block)模块中的AIRCR(Application Interrupt and Reset Control Register)寄存器进行配置。全局中断分组主要影响到普通中断的优先级顺序。

子优先级分组:用于配置同一优先级中多个中断之间的响应顺序,通过NVIC模块的IPR(Interrupt Priority Register)寄存器进行配置。

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

0位抢占优先级和4位响应优先级(0:4):将16个主优先级分成16/1=16个组,每组只包含一个优先级。

1位抢占优先级和3位响应优先级(1:3):将16个主优先级分成16/4=4个组,每组包含4个优先级。

2位抢占优先级和2位响应优先级(2:2):将16个主优先级分成16/16=1个组,所有中断的优先级相同。

3位抢占优先级和1位响应优先级(3:1):将16个主优先级分成16/64=1/4个组,每组包含64个优先级。

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

EXTI外部中断

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

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

配置GPIO引脚:首先需要将要使用的GPIO引脚配置为输入模式,同时使能外部中断线。

配置EXTI线:选择要使用的中断线并配置其触发方式,例如上升沿、下降沿、低电平、高电平等。

编写中断服务函数:当外部中断触发时,会跳转到对应的中断服务函数进行处理。需要编写相应的中断服务函数来响应中断事件。

关闭中断:在中断服务函数中对紧急情况进行处理后,需要及时将中断屏蔽以避免中断重复触发。

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

#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外部中断的使用中,这三个组件经常一同出现:

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

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

红外传感器计次 #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); } }


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有