写在前面:前面的学习中,我们接触了STM32的第一个外设GPIO,这也是最常用的一个外设;而除了GPIO外,中断也是一个十分重要且常用的外设;只有掌握了中断,再处理程序时才能掌握好解决实际问题的逻辑思路。此节我们学习STM32的中断;
? ? 在前面51的学习中,我们也介绍过中断的内容,但是相对于32来说,52的中断十分简单,只有5个中断源,中断的优先级,响应函数也是比较简单的,感兴趣的读者可以看一下下面的这篇博客。
CSDNhttps://mp.csdn.net/mp_blog/creation/editor/133904885
目录
中断:打断CPU执行正常的程序,转而处理紧急程序,然后返回原暂停的程序继续运行;
举例:当你正在写作业时,做到一半又去吃饭,吃完饭后又回来接着原来的作业继续完成。?
????????对于单片机来说,中断是指CPU正在处理某个事件A,发生了另一件事件B,请求CPU迅速去处理(中断发生);CPU暂时停止当前的工作(中断响应),转去处理事件B(中断服务);待CPU处理事件B完成后,再回到原来的事件A(断点)继续执行,这一过程称之为中断。
中断的作用和意义:
1.实时控制:在确定的时间内对相应事件做出相应;例如:温度控制;
2.故障处理:检测到故障,需要第一时间进行处理;
3.数据传输:不确定数据何时会来,利用中断进行控制;
中断的作用:高效处理紧急程序,并且不会占用CPU资源。
STM32 GPIO外部中断简图:
下面将分别对上述过程中的各个部分进行详细的描述;
? ? NVIC 即嵌套向量中断控制器,全称 Nested vectored interrupt controller。属于是内核的器件,其作用是对STM32中的中断进行管理,因为M3内核中的中断数量很多,当同时出现多个中断时,优先处理哪个中断?以及那些中断不处理等,都要靠NVIC 进行控制。M3 内核都是支持 256 个中断,其中包含了 16 个系统中断和 240 个外部中断,并且具有 256 级的可编程中断设置。
? ? NVIC支持:256个中断(16个内核+240个外部);支持256个优先级。
???但是对于ST公司来说,用不了M3内核中的所有中断以及中断优先级,进而对其进行了一定的裁剪。STM32中共有10个内核中断,60个外部中断,16个中断优先级;
? ? ? ? 在中断的使用中还有一个极其重要的一部分为中断服务函数(触发中断后,系统执行的部分,例如上文的吃饭过程)中断服务函数是中断的入口。
中断向量表:
定义一块固定的内存,以4字节对齐(32位),用于存放终端服务函数的首地址,系统已经将中断服务函数定义好了,放在中断向量表中,我们只需要进行调用即可。例如下面的中断向量表:
具体可参考:STM32F103的中文参考手册(9.1.2中断和异常向量)
在程序中的定义为:
中断向量表定义在启动文件中,发生中断时,CPU会自动执行对应的额中断服务函数;
NVIC相关的寄存器有很多,但是重要的有以下几个,也是需要进行掌握的:
名称 | 位数 | 个数 | 作用 |
中断使能寄存器(ISER) | 32 | 8 | 每一位控制一个中断(打开) |
中断失能寄存器(ICER) | 32 | 8 | 每一位控制一个中断(关闭) |
应用程序中断及复位控制寄存器(AIRCR) | 32 | 1 | 位[10:8]控制中断优先级分组 |
中断优先级寄存器IPR | 8 | 240 | 8个位对应一个中断,而STM32只使用高4位 |
1、ISER与ICER寄存器共有32*8=356,用于控制240个中断的打开与关闭;
2、AIRCR寄存器,位10、9、8三位用于控制优先级的分组,三位共2*2*2=8种,取其中的5组作为中断优先级的分组情况;
3、IPR寄存器,用于控制中断的优先级,包括抢占优先级与响应优先级,高4位控制,至于哪几位控制抢占,哪几位控制响应,由AIRCR寄存器说了算;
工作原理图:
工作过程:
????????当外部被出发时,首先进入ICER、ISER寄存器,用于控制是否开对应的中断,打开的中断进入IPR寄存器,进行中断优先级的判断,IPR寄存器受AIRCR寄存器控制,最后按照中断优先级依次进入CPU被执行。
? ? ? ? 内核中断由SHPR寄存器控制,SHPR与IPR寄存器属于同一级别;?
?STM32中断优先级基本概念:
1、抢占优先级(pre):高抢占优先级可以打断正在执行的低抢占优先级中断;
2、响应优先级(sub):当抢占优先级相同时,响应优先级高的先执行,但是不能相互打断;
3、抢占优先级和响应优先级都相同的情况下,自然优先级越高的先执行;
4、自然优先级:中断向量表中的优先级;
5、数值越小,表示优先级越高;
STM32中断优先级分组:
? ? ? ?在前面我们说过AIRCR寄存器,位10、9、8三位用于控制优先级的分组,但是只取其中的五组优先级分组;IPR寄存器,用于控制中断的优先级,包括抢占优先级与响应优先级,高4位控制,如下表所示:
优先级分组 | AIRCR[10:8] | IPR[7:4]分配 | 分配结果 |
0 | 111 | None:[7:4] | 抢占优先级(0位、0级) 响应优先级(4位、16级) |
1 | 110 | [7]:[6:4] | 抢占优先级(1位、2级) 响应优先级(3位、8级) |
2 | 101 | [7:6]:[6:4] | 抢占优先级(2位、4级) 响应优先级(2位、4级) |
3 | 100 | [7:5]:[4] | 抢占优先级(3位、8级) 响应优先级(1位、2级) |
4 | 011 | [7:4]:None | 抢占优先级(4位、16级) 响应优先级(0位、0级) |
????????假定设置中断优先级分组为 2,然后设置中断3的抢占优先级为 2,响应优先级为 1。中断6的抢占优先级为 3,响应优先级为0。?中断7的抢占优先级为 2,响应优先级为 0。那么这 3 个中断的优先级顺序为: 中断 7>中断 3>中断 6。 上面例子中的中断 3 和中断 7 都可以打断中断 6 的中断。而中断 7 和中断 3 却不可以相互打断!????????????????
????????在一个工程中,一般只设置一次中断优先级分组(AIRCR寄存器);
使用步骤:
1、设置中断分组:AIRCR寄存器[10:8];
HAL_NVIC_SetPriorityGrouping();
2、设置中断优先级:IPR寄存器[7:4];
HAL_NVIC_SetPriority();
3、使能中断:ISER寄存器;
HAL_NVIC_EnableIRQ();
1、HAL_NVIC_SetPriorityGrouping()
?中断分组的设置在HAL_Init()函数中配置:
?2、HAL_NVIC_SetPriority()
函数说明:
用于设置中断的抢占优先级和响应优先级(子优先级)
?函数形参:
形参 1 是中断号,可以选择范围:IRQn_Type 定义的枚举类型。
形参 2 是抢占优先级,可以选择范围:0 到 15。
形参 3 是响应优先级,可以选择范围:0 到 15。
?3、HAL_NVIC_EnableIRQ()
函数描述: 用于使能中断。
?函数形参: 形参 IRQn 是中断号,可以选择范围:IRQn_Type 定义的枚举类型
全称:外部中断事件控制器
? ? 包含20个产生事件/中断请求的边沿检测器,即20条EXIT线;
中断和事件的理解
中断:要进入NVIC,有相应的中断服务函数,需要CPU处理;
事件:不进入NVIC,仅用内部硬件自动控制,TIM,DMA,ADC等
EXIT线说明:
0-15:对应GPIO_PIN 0-15中断;
16:PVD输出;
17:RTC闹钟事件;
18:USB唤醒事件;
19:连接到以太网唤醒事件(只适用于互联型产品)
????????每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入线都可以独立地被屏蔽。挂起寄存器保持着状态线的中断请求
工作原理图:
????????其中按照控制功能划分,共分为4个部分;有两条主线,一条是由输入线到 NVIC 中断控制器(123),一条是由输入线到脉冲发生器(124)。
输入线:是线路的信息输入端,它可以通过配置寄存器设置为任何一个 GPIO 口,或者是一些外设的事件。输入线一般都是存在电平变化的信号。
1、边沿检测电路:上升沿触发选择寄存器和下降沿触发选择寄存器。边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号‘1’,就输出有效信号‘1’到标号2部分电路,否则输入无效信号‘0’。边沿跳变的标准在于对两个触发选择寄存器的设置。
2、或门电路:它的两个信号输入端分别是软件中断事件寄存器和边沿检测电路的输入信号。或门电路只要输入端有信号‘1’,就会输出‘1’,所以就会输出 ‘1’到标号3电路和标号4电路。
3、与门电路(标号3):两个信号输入端分别是中断屏蔽寄存器和标号2电路信号。如果中断屏蔽寄存器设置为 0 时,不管从标号2电路输出的信号特性如何,最终标号3电路输出的信号都是 0;假如中断屏蔽寄存器设置为 1 时,最终标号3电路输出的信号才由标号2电路输出信号决定,这样子就可以简单控制 中断屏蔽寄存器 来实现中断的目的。标号4电路输出 ‘1’就会把请求挂起寄存器(EXTI_PR)对应位置 1。
4、与门电路(标号4):输入端来自标号2电路以及来自于事件屏蔽寄存器。可以简单的控制事件屏蔽寄存器来实现是否产生事件的目的。标号4电路输出有效信号 1 就会使脉冲发生器电路产生一个脉冲,而无效信号就不会使其产生脉冲信号。脉冲信号产生可以给其他外设电路使用,例如定时器,模拟数字转换器等,这样的脉冲信号一般用来触发 TIM 或者 ADC 开始转换。
????????产生中断线路目的使把输入信号输入到 NVIC,进一步运行中断服务函数,实现功能。
????????在上述工作原理图中,所涉及的寄存器共有7个,但是对于外部中断来说,我们只需要掌握其中四个寄存器:上升沿触发选择寄存器(EXTI_RTSR)、下降沿触发选择寄存器(EXTI_FTSR)、挂起寄存器(EXTI_PR)与中断屏蔽寄存器(EXTI_IMR)。
1、上升沿触发选择寄存器(EXTI_RTSR)
????????该寄存器主要用于控制输入线进来的输入信号,上升沿时是否在边沿检测电路被检测出,20位共控制20条EXIT线;?
?2、下降沿触发选择寄存器(EXTI_FTSR)
????该寄存器主要用于控制输入线进来的输入信号,下升沿时是否在边沿检测电路被检测出,20位共控制20条EXIT线;?
3、挂起寄存器(EXTI_PR)?
该寄存器的作用主要有两个:
1、检测外部中断线上是否发生了选择的边沿事件,如果发生了,该位置1,并将信号传递给?与门电路,进而进入NVIC中;
2、在该位手动(软件)写入1,可以清除之前中断信号的1,主要作用是进入中断后,清除中断位,防止多次进入中断;
4、中断屏蔽寄存器(EXTI_IMR)
????????该寄存器的主要作用只有一个,就是是否允许来自中断线上的中断信号进入NVIC中断控制器。?
????????前面说过, EXTI 线 0~15:对应外部 IO 口的输入中断。但是STM32F1 供给 IO 口使用的中断线只有 16 个,但是 STM32F1 的 IO 口 却远远不止 16 个,所以 STM32 把 GPIO 管脚 GPIOx.0~GPIOx.15(x=A,B,C,D,E,F,G)分别对应中断线 0~15。这样子每个中断线对应了最多 9 个 IO 口,以线 0 为例:它对应了GPIOA.0、GPIOB.0、 GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0 和 GPIOG.0。而中断线每次只能连接到 1 个 IO 口上, 这样就需要通过配置决定对应的中断线配置到哪个 GPIO 上了。
GPIO 和中断线映射关系是在寄存器 AFIO_EXTICR1 ~ AFIO_EXTICR4 中配置的?
外部中断配置寄存器 1(AFIO_EXTICR1)
AFIO_EXTICR1 寄存器配置 EXTI0 到 EXTI3 线,包含的外部中断的引脚包括 PAx 到 PGx, x=0 到 3。
AFIO_EXTICR2 寄存器配置 EXTI4 到 EXTI7 线;
AFIO_EXTICR3 寄存器配置 EXTI8 到 EXTI11线;
AFIO_EXTICR4 寄存器配置 EXTI12到 EXTI15线;
特别注意:配置AFIO寄存器之前需要使能AFIO的时钟;
????????上述中断步骤共有两条线路,一条为GPIO外部中断的工作流程,即EXIT线(0-15)的工作流程,另外一条为其他外设中断的工作流程,即EXIT线(16-19)的工作流程。?
1、使能GPIO时钟;
2、设置GPIO的工作模式;(上拉、下拉、浮空输入)
3、使能AFIO时钟;(设置AFIO时钟,开启相关寄存器);
4、设置GPIO与EXIT映射关系;(选择IO对应EXIT输入线,AFIO_EXICR寄存器);
5、设置EXIT屏蔽、上升沿、下降沿;(设置EXIT对应通道的屏蔽和上升沿、下降沿触发,EMR、RTSR、FTSR寄存器);
6、设置NVIC,分3步(优先级分组、优先级以及使能);
7、设置中断服务函数(包括清除中断标志);
STM32 EXIT的HAL库设置步骤:
1、使能GPIO时钟--使用_HAL_RCC_GPIOx_CLK_ENANLE
2、GPIO/AFIO/EXIT设置--使用_HAL_GPIO_Init
这些步骤 HAL 库全部封装在 HAL_GPIO_Init 函数里面
3、设置中断分组--使用HAL_NVIC_SetpriorityGrouping
4、设置中断优先级--使用HAL_NVIC_Setpriority
5、使能中断--使用HAL_NVIC_EnableRQ
6、设置中断服务函数--EXIT_TRQrlander
STM32仅有EXIT0-4、EXIT9_5、EXIT15_10? 7个外部中断服务函数;
????????中断线 0-4,每个中断线对应一个中断函数,中断线 5-9 共用中断函数EXTI9_5_IRQHandler, 中断线 10-15 共用中断函数 EXTI15_10_IRQHandler。一般情况下,我们可以把中断控制逻辑直 接编写在中断服务函数中,但是 HAL 库把中断处理过程进行了简单封装。
一般外设的配置驱动模型为:
1、初始化
分为:时钟设置(包括时钟源与开启时钟);
? ? ? ? ? ?参数设置;
? ? ? ? ? ?IO设置(可选);
? ? ? ? ? ?中断设置(可选);
2、读函数:从外设读取数据(可选);
3、写函数:往外设写入数据(可选);
4、中断服务函数(可选)
这个内容在上一节GPIO的使用中已经说明;详细内容请看下面这个博客;
STM32--GPIO点亮LED灯(手把手,超详细)-CSDN博客
利用STM32F1精英开发板的独立按键模块KEY0控制LED0灯的亮灭;
1、LED设置? ? ? ?
????????在该开发板上的LED灯共有3个,本次我们点亮LED0,即对PB5进行操作;我们看到LED0右端接的是VCC3.3V,那么只需要控制左端的PB5输出即可;(输出0点亮,输出1熄灭);
? ? ? ? 我们还需要确定GPIOB_5的工作模式;我们采用推挽输出,其特点为:输出引脚电平,高电平为VDD,低电平为VSS;
2、KEY设置
? ? ? ? 在该开发板上的KEY共有三个按键,本次我们使用的是KEY0,即对PE4进行操作,当按键按下是,PE4输出低电平,当按键未按下时,PE4应为高电平;所以GPIOE_4的工作模式为上拉输入。?
void ?exti_init(void)
{
? ? GPIO_InitTypeDef ?gpio_init_struct;//GPIO初始化,时钟使能,引脚,工作模式
? ? __HAL_RCC_GPIOE_CLK_ENABLE();//使能GPIOE时钟
? ? gpio_init_struct.Pin=GPIO_PIN_4;//定义引脚
? ? gpio_init_struct.Mode=GPIO_MODE_IT_FALLING ;//定义工作模式
? ? gpio_init_struct.Pull=GPIO_PULLUP ;//定义上拉
? ? HAL_GPIO_Init(GPIOE,&gpio_init_struct);//GPIO/AFIO/EXIT设置
? ? HAL_NVIC_SetPriority(EXTI4_IRQn,2,0);//设置中断优先级
? ? HAL_NVIC_EnableIRQ(EXTI4_IRQn);//使能中断
}注:中断分组的设置已经在HAL_Init()函数中配置好了,无需配置,抢占优先级2位,响应优先级2位。
????????每开启一个中断,就必须编写其对应的中断服务函数,否则将会导致死机(CPU 将找不到 中断服务函数)。STM32F1 的 IO 口外部中断函数只有 7 个上文已经说明。
? ? ? ? 中断线 0-4,每个中断线对应一个中断函数,中断线 5-9 共用中断函数EXTI9_5_IRQHandler, 中断线 10-15 共用中断函数 EXTI15_10_IRQHandler。一般情况下,我们可以把中断控制逻辑直 接编写在中断服务函数中,但是 HAL 库把中断处理过程进行了简单封装。
?void EXTI4_IRQHandler(void)
? ? {
? ? ?HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
? ? ?__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4);
? ? }??????所有的外部中断服务函数里都只调用了同样一个函数 HAL_GPIO_EXTI_IRQHandler,该函 数是外部中断共用入口函数,函数内部会进行中断标志位清零,并且调用中断处理共用回调函 数 HAL_GPIO_EXTI_Callback。
????????HAL 库为了用户使用方便,提供了一个中断通用入口函数 HAL_GPIO_EXTI_IRQHandler, 在该函数内部直接调用回调函数 HAL_GPIO_EXTI_Callback。
????????该函数实现的作用非常简单,通过入口参数 GPIO_Pin 判断中断来自哪个 IO 口,然后清除 相应的中断标志位,最后调用回调函数 HAL_GPIO_EXTI_Callback()实现控制逻辑。在所有的外 部中断服务函数中直接调用外部中断共用处理函数 HAL_GPIO_EXTI_IRQHandler,然后在回调 函数 HAL_GPIO_EXTI_Callback 中通过判断中断是来自哪个 IO 口编写相应的中断服务控制逻 辑。
? void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)//实现控制逻辑
? ? ?{
? ? ? delay_ms(20);//消抖
? ? ? ? ?if( GPIO_Pin==GPIO_PIN_4)//判断中断来自哪个IO口
? ? ? ? ?{
? ? ? ? ? ? ? if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==0)//判断按键是否按下
? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); //led0进行状态取反
? ? ? ? ? ? ?}
? ? ? ? ?}
? ? ?}
exti.c文件
#include "./BSP/EXTI/exti.h"
#include "./SYSTEM/delay/delay.h"
void exti_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
__HAL_RCC_GPIOE_CLK_ENABLE();
gpio_init_struct.Pin=GPIO_PIN_4;
gpio_init_struct.Mode=GPIO_MODE_IT_FALLING ;
gpio_init_struct.Pull=GPIO_PULLUP ;
HAL_GPIO_Init(GPIOE,&gpio_init_struct);
HAL_NVIC_SetPriority(EXTI4_IRQn,2,0);
HAL_NVIC_EnableIRQ(EXTI4_IRQn);
}
void EXTI4_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(20);
if( GPIO_Pin==GPIO_PIN_4)
{
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==0)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
}
}
}
32中断点灯
链接:https://pan.baidu.com/s/11o3VNe0gEB9w4InOlhlVrA?
提取码:1022
总结: 本文主要讲解了STM32单片机的中断使用,主要内容有:中断的概念、NVIC、EXIT的介绍,以及中断使用的步骤。并且利用中断实现了按键控制LED灯。大家一定要反复阅读,才能掌握其中的思路,也要对实验部分进行多次尝试。。
创作不易,还请大家多多点赞支持!!!