STM32 HAL库 STM32CubeMx -- 外部中断

文章目录

  • 一、STM32 中断概述
  • 二、NVIC 简介
    • NVIC 寄存器简介
    • 中断优先级
    • NVIC 中断配置固件库
    • 中断编程
  • 三、外部中断(EXTI)
    • EXTI 简介
    • EXTI 功能框图
    • 中断/事件线
  • 四、STM32CubeMx 配置
  • 五、代码分析
  • 附录


一、STM32 中断概述

中断,是指处理机处理程序运行中出现的紧急事件的整个过程。程序运行过程中,系统外部、系统内部或者现行程序本身若出现紧急事件,处理机立即中止现行程序的运行,自动转入相应的处理程序(中断服务程序),待处理完后,再返回原来的程序运行,这整个过程称为程序中断。当处理机接受中断时,只需暂停一个或几个周期而不执行处理程序的中断,称为简单中断,中断又可分为屏蔽中断和非屏蔽中断两类。

STM32 中断非常强大,每个外设都可以产生中断,所以之后讲的很多外设都可以使用中断,在这里先总体讲解一下中断,之后讲到相应外设的时候再将每个外设的中断使用方法;

F103 在内核水平上搭载了一个异常响应系统,支持为数众多的系统异常和外部中断。其中系统异常有8 个(如果把Reset 和HardFault 也算上的话就是10 个),外部中断有60 个。除了个别异常的优先级被定死外,其它异常的优先级都是可编程的。有关具体的系统异常和外部中断可在HAL 库文件stm32f1xx.h 这个头文件查询到,在IRQn_Type 这个结构体里面包含了F103 系列全部的异常声明。

F103 系统异常清单

优先级 优先级类型 名称 说明 地址
-3 固定 Reset 复位 0X0000 0004
-2 固定 NMI 不可屏蔽中断。RCC 时钟安全系统(CSS) 连接到NMI 向量 0X0000 0008
-1 固定 HardFault 所有类型的错误 0X0000 000C
0 可编程 MemManage 存储器管理 0X0000 0010
1 可编程 BusFault 预取指失败,存储器访问失败 0X0000 0014
2 可编程 UsageFault 未定义的指令或非法状态 0X0000 0018
3 可编程 SVCall 通过SWI 指令调用的系统服务 0X0000 002C
4 可编程 Debug Monitor 调试监控器 0X0000 0030
5 可编程 PendSV 可挂起的系统服务 0X0000 0038
6 可编程 SysTick 系统嘀嗒定时器 0X0000 003C

F103 外部中断清单

编号 优先级 优先级类型 名称 说明 地址
0 7 可编程 WWDG 窗口看门狗中断 0X00000040
1 8 可编程 PVD 连到EXTI 的电源电压检测(PVD) 中断 0X00000044
2 9 可编程 TAMPER 侵入检测中断 0X00000048
···
57 64 可编程 DMA2 通道2 DMA2 通道2 中断 0X00000124
58 65 可编程 DMA2 通道3 DMA2 通道3z 中断 0X0000
59 66 可编程 DMA2 通道4_5 DMA2 通道4 和通道5 中断 0X0000012C

二、NVIC 简介

NVIC 是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对Cortex-M3 内核里面的NVIC 进行裁剪,把不需要的部分去掉,所以说STM32的NVIC 是Cortex-M3 的NVIC 的一个子集。

NVIC 寄存器简介

在固件库中,NVIC 的结构体定义给每个寄存器都预留了很多位。不过STM32F103 可用不了这么多,只是用了部分而已,具体使用了多少可参考《Cortex-M3 内核编程手册》NVIC 寄存器映射。

typedef struct {
__IOM uint32_t ISER[8U]; // 中断使能寄存器
uint32_t RESERVED0[24U];
__IOM uint32_t ICER[8U]; // 中断清除寄存器
uint32_t RSERVED1[24U];
__IOM uint32_t ISPR[8U]; // 中断使能悬起寄存器
uint32_t RESERVED2[24U];
__IOM uint32_t ICPR[8U]; // 中断清除悬起寄存器
uint32_t RESERVED3[24U];
__IOM uint32_t IABR[8U]; // 中断有效位寄存器/
uint32_t RESERVED4[56U];
__IOM uint8_t IP[240U]; // 中断优先级寄存器(8Bit wide)
uint32_t RESERVED5[644U];
__OM uint32_t STIR; // 软件触发中断寄存器
} NVIC_Type;

在配置中断的时候我们一般只用ISER、ICER 和IP 这三个寄存器,ISER 用来使能中断,ICER 用来失能中断,IP 用来设置中断优先级。

中断优先级

在NVIC 有一个专门的寄存器:中断优先级寄存器NVIC_IPRx,用来配置外部中断的优先级,IPR宽度为8bit,原则上每个外部中断可配置的优先级为0~255,数值越小,优先级越高。但是绝大多数CM3 芯片都会精简设计,以致实际上支持的优先级数减少,在F103 中,只使用了高4bit,
如下所示:

bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
用于表达优先级 未使用,读回为0

用于表达优先级的这4bit,又被分组成抢占优先级和子优先级

如果有多个中断同时响应,抢占优先级高的就会抢占抢占优先级低的优先得到执行,如果抢占优先级相同,就比较子优先级;

如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,优先级越高。

优先级的分组由内核外设SCB 的应用程序中断及复位控制寄存器AIRCR 的PRIGROUP[10:8] 位决定,F103 分为了5 组;

设置优先级分组可调用库函数HAL_NVIC_SetPriority() 实现,有关NVIC 中断相关的库函数都在库文件stm32f1xx_hal_cortex.c 和stm32f1xx_hal_cortex.h 中。

void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority,
uint32_t SubPriority)
{
uint32_t prioritygroup = 0x00U;
/* Check the parameters */
assert_param(IS_NVIC_SUB_PRIORITY(SubPriority));
assert_param(IS_NVIC_PREEMPTION_PRIORITY(PreemptPriority));
prioritygroup = NVIC_GetPriorityGrouping();
NVIC_SetPriority(IRQn, NVIC_EncodePriority(prioritygroup,
PreemptPriority, SubPriority));
}

NVIC 中断配置固件库

固件库文件core_cm3.h 的最后,还提供了NVIC 的一些函数,这些函数遵循CMSIS 规则,只要是Cortex-M3 的处理器都可以使用,具体如下:

void NVIC_EnableIRQ(IRQn_Type IRQn) //使能中断
void NVIC_DisableIRQ(IRQn_Type IRQn) //失能中断
void NVIC_SetPendingIRQ(IRQn_Type IRQn) //设置中断悬起位
void NVIC_ClearPendingIRQ(IRQn_Type IRQn) //清除中断悬起位
uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) //获取悬起中断编号
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) //设置中断优先级
uint32_t NVIC_GetPriority(IRQn_Type IRQn) //获取中断优先级
void NVIC_SystemReset(void) //系统复位

中断编程

在配置每个中断的时候一般有3 个编程要点:

  1. 使能外设某个中断,这个具体由每个外设的相关中断使能位控制。比如串口有发送完成中断,接收完成中断,这两个中断都由串口控制寄存器的相关中断使能位控制。
  2. 配置EXTI 中断源、配置中断优先级。
  3. 编写中断服务函数。

会使用下面的函数:

//NVIC 初始化结构体
/* 配置EXTI 中断源、配置中断优先级*/
HAL_NVIC_SetPriority(IRQn,PreemptPriority, SubPriority)
/*
1. IRQn:用来设置中断源,不同的中断中断源不一样,且不可写错,即使写错了程序也不会报错,只会导致不响应中断。具体的成员配置可参考stm32f103xe.h 头文件里面的IRQn_Type 结构体定义,这个结构体包含了所有的中断源。
2. PreemptionPriority:抢占优先级,具体的值要根据优先级分组来确定,。
3. SubPriority:子优先级,具体的值要根据优先级分组来确定。
*/

在启动文件startup_stm32f103xe.s 中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。实际的中断服务函数都需要我们重新编写,为了方便管理我们把中断服务函数统一写在stm32f1xx_it.c 这个库文件中。

关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口,直接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。

注意:这个是中断的底层原理部分,由于直接使用这些函数配置有点复杂,在HAL库里面对于中断函数的编程进行了简写,对于中断使用会在之后的STM32CubeMx + Keil 里面详细讲述


三、外部中断(EXTI)

EXTI 简介

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

EXTI 功能框图

EXTI 的功能框图包含了EXTI 最核心内容;

STM32 HAL库 STM32CubeMx -- 外部中断_第1张图片
在上图可以看到很多在信号线上打一个斜杠并标注“20”字样,这个表示在控制器内部类似的信号线路有20 个,这与EXTI 总共有20 个中断/事件线是吻合的。

EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所不同。

首先看上图中红色虚线指示的电路流程。它是一个产生中断的线路,最终信号流入到NVIC 控制器内;

编号1 是输入线,EXTI 控制器有19 个中断/事件输入线,这些输入线可以通过寄存器设置为任意一个GPIO,也可以是一些外设的事件。输入线一般是存在电平变化的信号;

编号2 是一个边沿检测电路,它会根据上升沿触发选择寄存器(EXTI_RTSR) 和下降沿触发选择寄存器(EXTI_FTSR) 对应位的设置来控制信号触发;边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号1 给编号3 电路,否则输出无效信号0。而EXTI_RTSR 和EXTI_FTSR 两个寄存器可以控制器需要检测哪些类型的电平跳变过程,可以是只有上升沿触发、只有下降沿触发或者上升沿和下降沿都触发;

编号3 电路实际就是一个或门电路,它一个输入来自编号2 电路,另外一个输入来自软件中断事件寄存器(EXTI_SWIER)。EXTI_SWIER 允许我们通过程序控制就可以启动中断/事件线,这在某些地方非常有用。我们知道或门的作用就是有1 就为1,所以这两个输入随便一个有有效信号1
就可以输出1 给编号4 和编号6 电路;

编号4 电路是一个与门电路,它一个输入是编号3 电路,另外一个输入来自中断屏蔽寄存器(EXTI_IMR)。与门电路要求输入都为1 才输出1,导致的结果是如果EXTI_IMR 设置为0 时,那不管编号3 电路的输出信号是1 还是0,最终编号4 电路输出的信号都为0;如果EXTI_IMR设置为1 时,最终编号4 电路输出的信号才由编号3 电路的输出信号决定,这样我们可以简单的控制EXTI_IMR 来实现是否产生中断的目的。编号4 电路输出的信号会被保存到挂起寄存器(EXTI_PR) 内,如果确定编号4 电路输出为1 就会把EXTI_PR 对应位置1;

编号5 是将EXTI_PR 寄存器内容输出到NVIC 内,从而实现系统中断事件控制。

接下来我们来看看绿色虚线指示的电路流程。它是一个产生事件的线路,最终输出一个脉冲信号;产生事件线路是在编号3 电路之后与中断线路有所不同,之前电路都是共用的。

编号6 电路是一个与门,它一个输入来自编号3 电路,另外一个输入来自事件屏蔽寄存器(EXTI_EMR)。如果EXTI_EMR 设置为0 时,那不管编号3 电路的输出信号是1 还是0,最终编号6 电路输出的信号都为0;如果EXTI_EMR 设置为1 时,最终编号6 电路输出的信号才由编号3 电路的输出信号决定,这样我们可以简单的控制EXTI_EMR 来实现是否产生事件的目的;

编号7 是一个脉冲发生器电路,当它的输入端,即编号6 电路的输出端,是一个有效信号1 时就会产生一个脉冲;如果输入端是无效信号就不会输出脉冲;

编号8 是一个脉冲信号,就是产生事件的线路最终的产物,这个脉冲信号可以给其他外设电路使用,比如定时器TIM、模拟数字转换器ADC 等等,这样的脉冲信号一般用来触发TIM 或者ADC
开始转换。

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

另外,EXTI 是在APB2 总线上。

中断/事件线

EXTI 有20 个中断/事件线,每个GPIO 都可以被设置为输入线,占用EXTI0 至EXTI15,还有另外七根用于特定的外设事件,4 根特定外设中断/事件线由外设触发。

中断/事件线 输入源
EXTI0 PX0(X 可为A,B,C,D,E,F,G,H,I)
EXTI1 PX1(X 可为A,B,C,D,E,F,G,H,I)
EXTI2 PX2(X 可为A,B,C,D,E,F,G,H,I)
EXTI3 PX3(X 可为A,B,C,D,E,F,G,H,I)
EXTI4 PX4(X 可为A,B,C,D,E,F,G,H,I)
EXTI5 PX5(X 可为A,B,C,D,E,F,G,H,I)
EXTI6 PX6(X 可为A,B,C,D,E,F,G,H,I)
EXTI7 PX7(X 可为A,B,C,D,E,F,G,H,I)
EXTI8 PX8(X 可为A,B,C,D,E,F,G,H,I)
EXTI9 PX9(X 可为A,B,C,D,E,F,G,H,I)
EXTI10 PX10(X 可为A,B,C,D,E,F,G,H,I)
EXTI11 PX11(X 可为A,B,C,D,E,F,G,H,I)
EXTI12 PX12(X 可为A,B,C,D,E,F,G,H,I)
EXTI13 PX13(X 可为A,B,C,D,E,F,G,H,I)
EXTI14 PX14(X 可为A,B,C,D,E,F,G,H,I)
EXTI15 PX15(X 可为A,B,C,D,E,F,G,H,I)
EXTI16 PVD 输出
EXTI17 RTC 闹钟事件
EXTI18 USB 唤醒事件
EXTI19 以太网唤醒事件(只适用互联型)

EXTI0 至EXTI15 用于GPIO,通过编程控制可以实现任意一个GPIO 作为EXTI 的输入源。由表可知,EXTI0 可以通过AFIO 的外部中断配置寄存器1(AFIO_EXTICR1) 的EXTI0[3:0] 位选择配置为PA0、PB0、PC0、PD0、PE0、PF0、PG0、PH0 或者PI0,见图17_2。其他EXTI 线(EXTI中断/事件线) 使用配置都是类似的。

四、STM32CubeMx 配置

为了试验外部中断,这里使用的示例是,使用按键控制LED灯的翻转
将按键连接的引脚设置为外部中断触发模式,当按键按下,触发外部中断,在中断处理函数或者中断回调函数里面进行LED灯的翻转操作;

下面进行STM32CubeMx的配置:

关于STM32CubeMx的基本配置,可以参考这篇博客:STM32 CubeMx教程 – 基础知识及配置使用教程

配置RCC时钟,这里选择使用外部晶振模式(Crystal/Ceramic Resonator);

STM32 HAL库 STM32CubeMx -- 外部中断_第2张图片

配置SYS,debug模式,由于我使用的是ST-Link,所以这里使用Serial Wire;

STM32 HAL库 STM32CubeMx -- 外部中断_第3张图片
观察原理图,得以得到按键的引脚是PC1、PC13,LED灯的引脚是PA8、PD2;

STM32 HAL库 STM32CubeMx -- 外部中断_第4张图片
将两个LED灯(PA8、PD2)配置为输出模式,

STM32 HAL库 STM32CubeMx -- 外部中断_第5张图片
将按键(PC1)设置为外部中断模式,PC13同理;

STM32 HAL库 STM32CubeMx -- 外部中断_第6张图片

配置GPIO界面

先配置LED(PA8),初始电平都可以(反正后期只是实现电平翻转功能)、输出模式选择推挽输出、不使用内部电阻、输出速度选择最大,PD2同理

STM32 HAL库 STM32CubeMx -- 外部中断_第7张图片

然后配置GPIO中断模式,由于按键另一头接地,所以初始电平要设置为高电平,这样按键按下时候,就是低电平,就可以被检测到,所以这里模式选择使用下降沿触发模式、内部电阻选择上拉电阻,初始电平为高电平。PC1、PC13均为这样设置。

STM32 HAL库 STM32CubeMx -- 外部中断_第8张图片
STM32 HAL库 STM32CubeMx -- 外部中断_第9张图片
GPIO mode:

上升沿触发检测的外部中断模式(External Interrupt Mode with Rising edge trigger detection)

下降沿触发检测的外部中断模式(External Interrupt Mode with Falling edge trigger detectiort)

上升/下降沿触发检测的外部中断模式(External Interrupt Mode with Risinq/Falling edge trigger detection)

上升沿触发检测的外部事件模式(External Event Mode with Rising edge trigger detection)

l 下降沿触发检测的外部事件模式(External Event Mode with Falling edge trigger detection)

上升/下降沿触发检测的外部事件模式(External Event Mode with Rising/Falling edge trigger detectiont)

这里中断触发分为外部中断触发和内部事件触发;

中断和事件的区别:

中断是当IO达到中断条件后会向CPU产生中断请求

事件是事先设置好的任务,当单片机达到要求将通过硬件的方式处理事先设置好的任务,而不向CPU请求中断,比如DMA、AD转换等

从外部激励信号来看,中断和事件的产生源都可以是一样的,之所以分成2个部分,由于中断是需要CPU参与的,需要软件的中断服务函数才能完成中断后产生的结果。但是事件,是靠脉冲发生器产生一个脉冲,进而由硬件自动完成这个事件产生的结果,当然相应的联动部件需要先设置好,比如引起DMA操作,AD转换等。

GPIO Pull-up/Pull-down :
No pull-up and no pull-down : 不使用内部电阻
Pull-up :上拉电阻,初始化为高电平
Pull-down : 下拉电阻,初始化为低电平

配置NVIC

使能GPIO中断;

可以直接点击GPIO界面内的NVIC来进入GPIO的中断控制器或是System Core下面的NVIC,进入全局中断控制器;
STM32 HAL库 STM32CubeMx -- 外部中断_第10张图片

STM32 HAL库 STM32CubeMx -- 外部中断_第11张图片
Priority Group:

STM32F103系列使用了4位中断优先级设置,一共可实现16个可编程的优先等级。优先级的配置需要使用NVIC,即嵌套向量中断控制器。NVIC控制着整个芯片中断相关的功能,跟内核紧密耦合,是内核里面的一个外设。

在具体配置时一般只用ISER、ICER和IP这3个寄存器,其中ISER用于使能中断,ICER用于清除中断,IP用于设置中断优先级。

用于表达优先级分组的寄存器NVIC_IPRTx宽度为8位,但是f103只使用其中高4位。

这4位又被分为抢占优先级和次优先级两组。多个中断同时响应时,抢占优先级高的就会先于抢占优先级低的执行;如果抢占优先级相同,就比较子优先级;如果子优先级也相同,就比较它们的硬件中断编号,编号越小优先级越高。

优先级的分组由内核外设SCB的应用程序中断及复位控制寄存器AIRCR的PRIGROUP[10:8]位决定。实际配置时程序中通过调用库函数NVIC_PriorityGroupConfig()实现。

优先级分组 主优先级 次优先级 描述
NVIC_PriorityGroup_0 0 0~15 主-0bit,子-4bit
NVIC_PriorityGroup_1 0~1 0~7 主-1bit,子-3bit
NVIC_PriorityGroup_2 0~3 0~3 主-2bit,子-2bit
NVIC_PriorityGroup_3 0~7 0~1 主-3bit,子-1bit
NVIC_PriorityGroup_4 0~15 0 主-4bit,子-0bit

Priority Group共有5个选项,与上面表格中的配置相对应,该选项配置完毕后,再在NVIC Interrupt Table中对各中断优先级分别进行配置。

0 bits for pre-emption priority 4 bits for subpriority

1 bits for pre-emption priority 3 bits for subpriority

2 bits for pre-emption priority 2 bits for subpriority

3 bits for pre-emption priority 1 bits for subpriority

4 bits for pre-emption priority 0 bits for subpriority

NVIC Interrupt Table:

F103系列在内核上搭建了一个异常响应系统,支持10个系统异常和外部中断,这部分一般不需要开发者进行配置。

Non maskable interrupt: 不可屏蔽中断。RCC时钟安全系统(CSS)连接到NMI向量

Hard fault interrupt: 所有类型的错误

Memory management fault: 存储器管理

Prefetch fault, memory access fault: 预取指失败,存储器访问失败

Undefined instruction or illegal state: 未定义的指令或非法状态

System service call via SWI instruction: 通过SWI指令调用的系统服务

Debug monitor: 调试监控器

Pendable request for system service: 可挂起的系统服务

Time base: System tick timer: 系统嘀嗒定时器

EXIT line1 interrupt : 外部中断

配置时钟树

STM32 HAL库 STM32CubeMx -- 外部中断_第12张图片

五、代码分析

实现按键触发中断,在中断里面进行LED灯的翻转操作。

生成代码以后,进入程序;

首先,我们在左边stm32f1xx_it.c 里面找到中断处理函数;

STM32 HAL库 STM32CubeMx -- 外部中断_第13张图片
由于我们使用了两个按键,所以会显示俩个外部触发源的中断处理函数,但是这两个函数里面都引用了 中断处理函数(HAL_GPIO_EXTI_IRQHandler());

// void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);
// 功能:  外部中断服务函数,清除中断标志位

/**
  * @brief  This function handles EXTI interrupt request.
  * @param  GPIO_Pin: Specifies the pins connected EXTI line
  * @retval None
  */
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

在这个函数里面主要执行两个操作,一个是清除中断标志位,另一个是继续调用了一个函数,中断回调函数(HAL_GPIO_EXTI_Callback(GPIO_Pin);)

// void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
// 功能:  中断回调函数,可以理解为中断函数具体要响应的动作。

/**
  * @brief  EXTI line detection callbacks.
  * @param  GPIO_Pin: Specifies the pins connected EXTI line
  * @retval None
  */
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(GPIO_Pin);
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_GPIO_EXTI_Callback could be implemented in the user file
   */
}

__weak 是一个弱化标识,带有这个的函数就是一个弱化函数,就是你可以在其他地方写一个名称和参数都一模一样的函数,编译器就会忽略这一个函数,而去执行你写的那个函数;也就是重写弱化函数;

而UNUSED(GPIO_Pin) ,这就是一个防报错的定义,当传进来的GPIO端口号没有做任何处理的时候,编译器也不会报出警告。

这个回调函数还有一点非常便利的地方,就是当同时有多个中断使能的时候,STM32CubeMX会自动地将几个中断的服务函数规整到一起并调用一个回调函数,也就是无论几个中断,我们只需要重写一个回调函数并判断传进来的端口号即可,还是非常方便的。

中断执行操作为先执行中断处理函数,然后中断处理函数调用中断回调函数;
意味着又多个中断源时候,可以选择把处理操作写在中断处理函数里面,也可以写在中断回调函数里面(这里会提供两种情况的写法)

如果我们想在函数里面加入延时程序(HAL_Delay),整个函数就会陷入卡死的状况,具体分析在STM32 HAL库 中断处理中使用延时函数(HAL_Delay)

这个问题也有两个解放办法,一个是自己重写一个延时函数,另一个是更改系统滴答定时和外部中断的优先级;

这里采用修改优先级的办法

STM32 HAL库 STM32CubeMx -- 外部中断_第14张图片

可以将处理写在中断处理函数(void EXTI1_IRQHandler(void))里面 + 中断回调函数(void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin))里面,中断处理函数处理针对于某一种外设要进行的操作,中断回调函数里面处理所有中断都会进行操作;

/**
  * @brief This function handles EXTI line1 interrupt.
  */
void EXTI1_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI1_IRQn 0 */
	HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);
	HAL_Delay(500);
	HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);	
  /* USER CODE END EXTI1_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
  /* USER CODE BEGIN EXTI1_IRQn 1 */

  /* USER CODE END EXTI1_IRQn 1 */
}

/**
  * @brief This function handles EXTI line[15:10] interrupts.
  */
void EXTI15_10_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI15_10_IRQn 0 */
	HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_2);
	HAL_Delay(500);
	HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_2);

  /* USER CODE END EXTI15_10_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
  /* USER CODE BEGIN EXTI15_10_IRQn 1 */

  /* USER CODE END EXTI15_10_IRQn 1 */
}

/* USER CODE BEGIN 1 */

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  
	HAL_Delay(1000);
	HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);
	HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_2);

}

/* USER CODE END 1 */

也可以将所有操作都写在中断回调函数里面,为了区分不同外设触发的不同操作,可以根据触发中断不同IO口进行判断

/* USER CODE BEGIN 1 */

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  	if(GPIO_Pin==GPIO_PIN_1)
	{ 
   
		HAL_Delay(20);
		if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_1)==GPIO_PIN_RESET)
		{ 
			HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);
			HAL_Delay(500);
			HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);	
		}
	}
	
	  if(GPIO_Pin==GPIO_PIN_13)
	{ 
   
		HAL_Delay(20);
		if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)==GPIO_PIN_RESET)
		{ 
			HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_2);
			HAL_Delay(500);
			HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_2);
		}
	}
	
	
	HAL_Delay(1000);
	HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);
	HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_2);

}

/* USER CODE END 1 */

附录

本节中的程序代码:STM32 HAL库 外部中断

你可能感兴趣的:(stm32,单片机,嵌入式硬件)