中断,是指处理机处理程序运行中出现的紧急事件的整个过程。程序运行过程中,系统外部、系统内部或者现行程序本身若出现紧急事件,处理机立即中止现行程序的运行,自动转入相应的处理程序(中断服务程序),待处理完后,再返回原来的程序运行,这整个过程称为程序中断。当处理机接受中断时,只需暂停一个或几个周期而不执行处理程序的中断,称为简单中断,中断又可分为屏蔽中断和非屏蔽中断两类。
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 是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对Cortex-M3 内核里面的NVIC 进行裁剪,把不需要的部分去掉,所以说STM32的NVIC 是Cortex-M3 的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));
}
固件库文件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 个编程要点:
会使用下面的函数:
//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(External interrupt/event controller)—外部中断/事件控制器,管理了控制器的20 个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。
EXTI 的功能框图包含了EXTI 最核心内容;
在上图可以看到很多在信号线上打一个斜杠并标注“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中断/事件线) 使用配置都是类似的。
为了试验外部中断,这里使用的示例是,使用按键控制LED灯的翻转;
将按键连接的引脚设置为外部中断触发模式,当按键按下,触发外部中断,在中断处理函数或者中断回调函数里面进行LED灯的翻转操作;
下面进行STM32CubeMx的配置:
关于STM32CubeMx的基本配置,可以参考这篇博客:STM32 CubeMx教程 – 基础知识及配置使用教程
配置RCC时钟,这里选择使用外部晶振模式(Crystal/Ceramic Resonator);
配置SYS,debug模式,由于我使用的是ST-Link,所以这里使用Serial Wire;
观察原理图,得以得到按键的引脚是PC1、PC13,LED灯的引脚是PA8、PD2;
配置GPIO界面
先配置LED(PA8),初始电平都可以(反正后期只是实现电平翻转功能)、输出模式选择推挽输出、不使用内部电阻、输出速度选择最大,PD2同理
然后配置GPIO中断模式,由于按键另一头接地,所以初始电平要设置为高电平,这样按键按下时候,就是低电平,就可以被检测到,所以这里模式选择使用下降沿触发模式、内部电阻选择上拉电阻,初始电平为高电平。PC1、PC13均为这样设置。
上升沿触发检测的外部中断模式(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,进入全局中断控制器;
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 : 外部中断
配置时钟树
实现按键触发中断,在中断里面进行LED灯的翻转操作。
生成代码以后,进入程序;
首先,我们在左边stm32f1xx_it.c 里面找到中断处理函数;
由于我们使用了两个按键,所以会显示俩个外部触发源的中断处理函数,但是这两个函数里面都引用了 中断处理函数(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)
这个问题也有两个解放办法,一个是自己重写一个延时函数,另一个是更改系统滴答定时和外部中断的优先级;
这里采用修改优先级的办法
可以将处理写在中断处理函数(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库 外部中断