| 上一篇 | 下一篇 |
|---|---|
| GPIO |
目 录
中断(⭐️)
提前说一下:中断服务函数内执行的程序不宜太复杂,中断执行时间也不宜太长,
定义:打断 CPU 执行正常的程序,转而处理紧急程序,然后返回原程序继续运行,就叫中断。
作用及意义:实时控制、故障处理、数据传输。高效处理紧急程序,不会一直占用 CPU 资源。
两个关键名词(模块):
- NVIC:Nested Vectored Interrupt Controller (嵌套向量中断控制器)
- EXTI:External Interrupt/Event Controller (外部中断/事件控制器)
中断的基本流程:

注意事项:
中断服务函数(ISR)执行时间应尽可能短,通常建议在微秒(μs)级别完成,一般不超过几十到几百微秒,绝对不应达到“秒”级。不然会影响系统实时性、可能导致中断丢失、破坏系统稳定性。也就是说:ISR 中只做紧急、简单的操作,如读取寄存器、置标志位、发信号,避免在 ISR 中使用 delay() 延时,进行大量计算或复杂逻辑,复杂的逻辑可以通过标志位识别,在 main 函数中执行。
① NVIC
简介:Nested Vectored Interrupt Controller,嵌套向量中断控制器,属于内核(M3/4/7)。NVIC 是内核的一部分:它是 ARM Cortex-M 处理器内核的一个标准组件,直接集成在 CPU 内核中,负责管理所有中断源(包括来自 EXTI 的请求),可以说是 “ 中断的管理者 ” 或 “ 调度中心 ” 。
NVIC 支持 256 个中断(16内核 + 240外部),支持 256 个优先级(允许裁剪),ST 公司就将其裁剪成了 16 个( 16个中断优先级 )。STM 的所有型号的内核中断数、外部中断数、中断优先级个数如下表所示:
| STM32型号 | 内核中断 | 外部中断 | 中断优先级 |
|---|---|---|---|
| STM32F103xx | 10 | 60 | 16 |
| STM32F407xx | 10 | 82 | 16 |
| STM32F429xx | 10 | 91 | 16 |
| STM32F750xx | 10 | 98 | 16 |
| STM32F767xx | 10 | 110 | 16 |
| STM32H743xx | 10 | 150 | 16 |
| STM32H750xx | 10 | 150 | 16 |
NVIC 在处理优先级时,并不区分该中断是内部还是外部产生的。统一标准:所有中断都使用相同的 16 级优先级(4 位有效)进行配置和比较。统一调度:最终的响应顺序完全由抢占优先级、响应优先级和中断号共同决定,内部中断和外部中断在这个体系下是平等竞争的。
当中断发生之后,CPU 会自动执行相应的中断服务函数,而中断服务函数的入口地址被定义在了中断向量表里面。
1. 中断向量表
简介:ARM Cortex-M 系列 MCU 的中断向量表是一个存储在内存起始地址(通常是 Flash 的 0x00000000 或经过 ST 等厂商重定向的起始地址)的数组。这个表的每一项(向量)都存储了一个函数指针(以 4 字节对齐),指向特定中断或异常处理程序的入口地址,也就是存放各个中断服务函数程序的首地址。
中断向量表定义在 .s 启动文件中,STM32F1 的表内容具体如下,每个中断向量都以 Handler 结尾,0 表示保留,前面是 10 个内核中断,下面是 60 个外部中断(MCU参考手册里会有详细的表格描述):
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; External Interrupts
DCD WWDG_IRQHandler ; Window Watchdog
DCD PVD_IRQHandler ; PVD through EXTI Line detect
DCD TAMPER_IRQHandler ; Tamper
DCD RTC_IRQHandler ; RTC
DCD FLASH_IRQHandler ; Flash
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1
DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2
DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3
DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4
DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5
DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6
DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7
DCD ADC1_2_IRQHandler ; ADC1 & ADC2
DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX
DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0
DCD CAN1_RX1_IRQHandler ; CAN1 RX1
DCD CAN1_SCE_IRQHandler ; CAN1 SCE
DCD EXTI9_5_IRQHandler ; EXTI Line 9..5
DCD TIM1_BRK_IRQHandler ; TIM1 Break
DCD TIM1_UP_IRQHandler ; TIM1 Update
DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation
DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare
DCD TIM2_IRQHandler ; TIM2
DCD TIM3_IRQHandler ; TIM3
DCD TIM4_IRQHandler ; TIM4
DCD I2C1_EV_IRQHandler ; I2C1 Event
DCD I2C1_ER_IRQHandler ; I2C1 Error
DCD I2C2_EV_IRQHandler ; I2C2 Event
DCD I2C2_ER_IRQHandler ; I2C2 Error
DCD SPI1_IRQHandler ; SPI1
DCD SPI2_IRQHandler ; SPI2
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD USART3_IRQHandler ; USART3
DCD EXTI15_10_IRQHandler ; EXTI Line 15..10
DCD RTC_Alarm_IRQHandler ; RTC Alarm through EXTI Line
DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend
DCD TIM8_BRK_IRQHandler ; TIM8 Break
DCD TIM8_UP_IRQHandler ; TIM8 Update
DCD TIM8_TRG_COM_IRQHandler ; TIM8 Trigger and Commutation
DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare
DCD ADC3_IRQHandler ; ADC3
DCD FSMC_IRQHandler ; FSMC
DCD SDIO_IRQHandler ; SDIO
DCD TIM5_IRQHandler ; TIM5
DCD SPI3_IRQHandler ; SPI3
DCD UART4_IRQHandler ; UART4
DCD UART5_IRQHandler ; UART5
DCD TIM6_IRQHandler ; TIM6
DCD TIM7_IRQHandler ; TIM7
DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1
DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2
DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3
DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
__Vectors_End
汇编伪指令 DCD:为程序分配连续的 4 字节存储单元,并用指定的值进行初始化。
2. NVIC 相关寄存器
三个外部中断寄存器,一个共用的优先级分组寄存器。
| 寄存器名 | 位数 | 寄存器个数 | 备注 |
|---|---|---|---|
| 中断使能寄存器(ISER) | 32 | 8 | 每个位控制一个中断 只给外部中断使用,所以只用了 240 位,其余 16 位保留 |
| 中断除能寄存器(ICER) | 32 | 8 | 每个位控制一个中断 只给外部中断使用,所以只用了 240 位,其余 16 位保留 |
| 应用程序中断及复位控制寄存器(AIRCR) | 32 | 1 | ST 公司只用了 3 个位( [10:8]),来控制优先级分组(5组) |
| 外部中断优先级设置寄存器(IPR) | 8 | 240 | 对应 240 个外部中断 每 8 位对应一个中断,而 STM32 只使用其中的高 4 位 在 CMSIS 底层库中,是用一个数组 IP[] 来索引的 |
| 系统异常优先级设置寄存器(SHPR) | 给内核中断使用的,但详细情况不清楚 |
工作原理(给出了一个例子,优先级的顺序已知):

3. 中断优先级(抢占和响应、自然)
之所以会有这个抢占和响应的区分,就是因为 ST 公司将本来的 256 个一一对应的优先级裁剪成了 16 个,需要引入别的判别机制。
- 抢占优先级(pre):高抢占优先级可以打断 正在执行的低抢占优先级中断。
- 响应优先级(sub):当抢占优先级相同时,响应优先级高的先执行,但是不能互相打断。
- 自然优先级(中断号):
- 中断向量表的优先级,也就是在中断向量表中的物理位置(即中断号);
- 手册里内核中断号是负数,外部中断的中断号是正的。
- 抢占和响应都相同的情况下,自然优先级越高的,先执行。
- 数值越小(越靠前),优先级越高,包括负数。
总结起来就是:首先,抢占优先级高的可以打断低的。其次,当两个中断同时发生,并且抢占优先级相同时,响应优先级高的先执行;但是当两个抢占优先级相同的中断不是同时发生的时候,那么就谁先来的就先执行。最后,当两个中断同时发生,并且抢占和响应优先级全都相同时,咱们就看它俩的中断号,谁中断号靠前(数值小)就先执行。
中断优先级分组:
| 优先级分组 | AIRCR [10:8] | IPRx 位 [7:4] 分配 | 分配结果 |
|---|---|---|---|
| 0 | 111 | none :[7:4] | 0 位抢占优先级(0个),4 位响应优先级(16个) |
| 1 | 110 | [7] :[6:4] | 1 位抢占优先级(2个),3 位响应优先级(8个) |
| 2 | 101 | [7:6] :[5:4] | 2 位抢占优先级(4个),2 位响应优先级(4个) |
| 3 | 100 | [7:5] :[4] | 3 位抢占优先级(8个),1 位响应优先级(2个) |
| 4 | 011 | [7:4] :none | 4 位抢占优先级(16个),0 位响应优先级(0个) |
【注】一般情况下,只设置一次中断优先级分组,随意改变分组(改变抢占和响应的位数)会导致中断管理混乱。
当抢占优先级高的抢占了优先级低的,是不是这个低优先级的程序就中止了呢:
- 不是中止,是暂停:高抢占优先级中断发生时,低优先级中断服务程序会被暂停,而非终止。
- 自动保存上下文:CPU硬件自动将低优先级中断的执行状态(如寄存器、PC等)压入堆栈。
- 优先执行高优先级中断:系统转而执行高优先级ISR。
- 恢复并继续执行:高优先级中断处理完成后,自动恢复低优先级中断的上下文,从中断点继续执行。
- 真正“中止”的情况:只有当中断标志被清除、事件消失或中断被禁用时,低优先级中断才可能被丢弃。
4. NVIC 的使用
-
设置中断分组
寄存器:AIRCR[10:8],函数:
HAL_NVIC_SetPriorityGrouping。这个函数是在
HAL_Init()中调用的,属于系统初始化的一步。函数调用示例:/* NVIC_PRIORITYGROUP_0 对应分组0 NVIC_PRIORITYGROUP_1 对应分组1 NVIC_PRIORITYGROUP_2 对应分组2 NVIC_PRIORITYGROUP_3 对应分组3 NVIC_PRIORITYGROUP_4 对应分组4 */ HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2); //设置优先级分组2 -
设置中断优先级
寄存器:IPRx 位[7:4],函数:
HAL_NVIC_SetPriority。这个函数是在某个外设的初始化函数里调用的,属于外设初始化的一步,调用示例:
HAL_NVIC_SetPriority(USART1_IRQn,3,3); // USART1中断优先级设置, USART1_IRQn为中断号, 第一个3为抢占优先级, 第二个3为响应优先级 // 中断号要查定义, 抢占和响应优先级要在优先级分组的限制内进行选择中断号枚举类型定义:
typedef enum { /****** Cortex-M3 Processor Exceptions Numbers ***************************************************/ NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ HardFault_IRQn = -13, /*!< 3 Cortex-M3 Hard Fault Interrupt */ MemoryManagement_IRQn = -12, /*!< 4 Cortex-M3 Memory Management Interrupt */ BusFault_IRQn = -11, /*!< 5 Cortex-M3 Bus Fault Interrupt */ UsageFault_IRQn = -10, /*!< 6 Cortex-M3 Usage Fault Interrupt */ SVCall_IRQn = -5, /*!< 11 Cortex-M3 SV Call Interrupt */ DebugMonitor_IRQn = -4, /*!< 12 Cortex-M3 Debug Monitor Interrupt */ PendSV_IRQn = -2, /*!< 14 Cortex-M3 Pend SV Interrupt */ SysTick_IRQn = -1, /*!< 15 Cortex-M3 System Tick Interrupt */ /****** STM32 specific Interrupt Numbers *********************************************************/ WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */ PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */ TAMPER_IRQn = 2, /*!< Tamper Interrupt */ RTC_IRQn = 3, /*!< RTC global Interrupt */ FLASH_IRQn = 4, /*!< FLASH global Interrupt */ RCC_IRQn = 5, /*!< RCC global Interrupt */ EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */ EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ DMA1_Channel1_IRQn = 11, /*!< DMA1 Channel 1 global Interrupt */ DMA1_Channel2_IRQn = 12, /*!< DMA1 Channel 2 global Interrupt */ DMA1_Channel3_IRQn = 13, /*!< DMA1 Channel 3 global Interrupt */ DMA1_Channel4_IRQn = 14, /*!< DMA1 Channel 4 global Interrupt */ DMA1_Channel5_IRQn = 15, /*!< DMA1 Channel 5 global Interrupt */ DMA1_Channel6_IRQn = 16, /*!< DMA1 Channel 6 global Interrupt */ DMA1_Channel7_IRQn = 17, /*!< DMA1 Channel 7 global Interrupt */ ADC1_2_IRQn = 18, /*!< ADC1 and ADC2 global Interrupt */ USB_HP_CAN1_TX_IRQn = 19, /*!< USB Device High Priority or CAN1 TX Interrupts */ USB_LP_CAN1_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN1 RX0 Interrupts */ CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */ CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */ EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ TIM1_BRK_IRQn = 24, /*!< TIM1 Break Interrupt */ TIM1_UP_IRQn = 25, /*!< TIM1 Update Interrupt */ TIM1_TRG_COM_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt */ TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ USART1_IRQn = 37, /*!< USART1 global Interrupt */ USART2_IRQn = 38, /*!< USART2 global Interrupt */ USART3_IRQn = 39, /*!< USART3 global Interrupt */ EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ RTC_Alarm_IRQn = 41, /*!< RTC Alarm through EXTI Line Interrupt */ USBWakeUp_IRQn = 42, /*!< USB Device WakeUp from suspend through EXTI Line Interrupt */ TIM8_BRK_IRQn = 43, /*!< TIM8 Break Interrupt */ TIM8_UP_IRQn = 44, /*!< TIM8 Update Interrupt */ TIM8_TRG_COM_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt */ TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */ ADC3_IRQn = 47, /*!< ADC3 global Interrupt */ FSMC_IRQn = 48, /*!< FSMC global Interrupt */ SDIO_IRQn = 49, /*!< SDIO global Interrupt */ TIM5_IRQn = 50, /*!< TIM5 global Interrupt */ SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ UART4_IRQn = 52, /*!< UART4 global Interrupt */ UART5_IRQn = 53, /*!< UART5 global Interrupt */ TIM6_IRQn = 54, /*!< TIM6 global Interrupt */ TIM7_IRQn = 55, /*!< TIM7 global Interrupt */ DMA2_Channel1_IRQn = 56, /*!< DMA2 Channel 1 global Interrupt */ DMA2_Channel2_IRQn = 57, /*!< DMA2 Channel 2 global Interrupt */ DMA2_Channel3_IRQn = 58, /*!< DMA2 Channel 3 global Interrupt */ DMA2_Channel4_5_IRQn = 59, /*!< DMA2 Channel 4 and Channel 5 global Interrupt */ } IRQn_Type; -
使能中断
寄存器:ISERx,函数:
HAL_NVIC_EnableIRQ。此函数是在某个外设的初始化函数里调用的,属于外设初始化的一步,调用示例:
HAL_NVIC_EnableIRQ(USART1_IRQn); //使能USART1中断通道 // 其中 USART1_IRQn 也是中断号, 具体查看上面列出的所有中断号
在正点原子的例程中,中断分组已经在 HAL_Init() 函数中设置了分组 2 ,这是一个折中的方案。
② EXTI
简介:External interrupt/event Controller,外部中断事件控制器。通过检测外部或内部信号的边沿变化(上升沿、下降沿或双边沿),(根据配置)来产生中断或事件,其中:
- 中断:要进入 NVIC ,有相应的中断服务函数,需要 CPU 处理。
- 事件:外设在特定条件下产生一个硬件脉冲信号(Event),这个信号不进入 NVIC,而是直接通过内部事件总线 (Event Bus) 传递给其他外设。CPU 完全不知道这个事件的发生,不进入中断,不执行任何代码(例如:如 ADC 转换完成 → 触发 TIM 启动)。
F1 系列的单片机包含 20 个产生事件/中断请求的边沿检测器,即总共 20 条 EXTI 线。非互联型是 19 个(√)。
F1/F4/F7/H7 四个型号中 EXTI 支持的外部中断/事件请求(主要是 EXTI 线 0~15 ):

EXTI 的主要特性:
每条 EXTI 线都可以单独配置:选择类型(中断或者事件)、触发方式(上升沿,下降沿或者双边沿触发)、支持软件触发、开启/屏蔽、有挂起状态位 。。比如说:外部输入了一个上升沿,配置产生中断(或事件)
1. EXTI 的工作原理


框图如上所示,通过与或门来控制。对应寄存器,我们只需要掌握上满四个标出的即可,软件触发和事件基本不用。
2. EXTI 相关寄存器
| 寄存器名 | 位数 | 功能 |
|---|---|---|
| 上升沿触发选择寄存器 EXTI_RTSR | 32 位,非互联型只用了前 0~18 位 | 19 位,每一位都对应一个 EXTI 线 位写 1 允许对应线上的上升沿触发 位写 0 禁止对应线上的上升沿触发 |
| 下降沿触发选择寄存器 EXTI_FTSR | 32 位,非互联型只用了前 0~18 位 | 19 位,每一位都对应一个 EXTI 线 位写 1 允许对应线上的下降沿触发 位写 0 禁止对应线上的下降沿触发 |
| 中断屏蔽寄存器 EXTI_IMR | 32 位,非互联型只用了前 0~18 位 | 19 位,每一位都对应一个 EXTI 线 位写 1 开放来自对应线上的中断请求 位写 0 屏蔽来自对应线上的中断请求 |
| 挂起寄存器 EXTI_PR | 32 位,非互联型只用了前 0~18 位 | 19 位,每一位都对应一个 EXTI 线 位为 0 表示没有发生触发请求 位为 1 表示发生了选择的触发请求 在该位中写 1 可以清除挂起状态 |
如果某一条线上,想要设置双边沿触发,那么将 EXTI_RTSR 和 EXTI_FTSR 对应位均设为 1 即可。
前三个寄存器是 CPU 主动写入来设置的,第四个寄存器可读可写,通过读来判断是否有请求,通过写 1 来清除挂起状态。
3. EXTI 和 IO 的映射关系
这里就是之前表格说的 EXTI 线 0~15 对应 GPIO PIN 0~15。
简介: 在 STM32 中,绝大多数外部中断 EXTI 是由 GPIO 引脚触发的。然而,每个 EXTI 线(如 EXTI0)在上电复位后并不会自动连接到某个特定的 GPIO 引脚。需要通过配置 AFIO 模块的 EXTICRx 寄存器,将目标 GPIO 引脚(如 PA0、PB0 等)的数字输入信号路由到对应的 EXTI 输入线。配置完成后,该引脚的电平变化(上升沿或下降沿)将被 EXTI 模块检测到,并可触发中断或事件。
AFIO:
Alternate Function lO,即复用功能 IO,主要用于重映射和 外部中断映射配置 。其功能如下(这里主要是第三个):
- 调试 IO 配置:不用管。
- 重映射配置:不用管。
- 外部中断配置:寄存器 AFIO_EXTICR1~4,配置 EXTI 中断线 0~15 对应到哪个具体 IO 口。
特别注意:配置 AFIO 寄存器之前要使能 AFIO 时钟,方法如下:HAL_RCC_AFIO_CLK_ENABLE(); 对应 RCC_APB2ENR 寄存器位 0 。
【注意】:
- 这个 EXTI 和 IO 的映射并不是 IO 的复用功能输入和输出,它使用的是 GPIO 的 “数字输入” 信号(来自 IDR),而非复用功能输出。并且必须将 GPIO 配置为某种输入模式(浮空、上拉、下拉)。
- IO 口的复用功能输出和输入是给 TIM、USART 这些外设使用的。
EXTI 和 IO 的对应关系(分时对应):
这俩的对应关系是靠 AFIO 模块的 EXTICRx 寄存器 配置的,EXTICR 寄存器总共有 4 个(AFIO_EXTICR1~4),每个寄存器控制 4 根 EXTI 线(共 16 根线)。并且其中一个引脚和某根 EXTI 线连接上之后,其他的几个引脚就不能再次与该 EXTI 线相连(就是说一个 EXTI 线每次只能连接一个引脚)。具体对应情况如下所示(具体要查看手册):

其中:PX0 映射到 EXTI0 ;PX1 映射到 EXTI1;…;PX14 映射到 EXTI14;PX15 映射到 EXTI15 。
③ 如何使用中断
整个中断的流程图,包括 GPIO EXTI 中断、其他外设 EXTI 中断、外设中断(但是这里主要讲 GPIO EXTI 中断)。

1. STM32 EXTI 的 HAL 库配置步骤(GPIO外部中断):
-
使能 GPIO 时钟
- 使用宏
__HAL_RCC_GPIOx_CLK_ENABLE()配置。
- 使用宏
-
配置 GPIO / AFIO(SYSCFG) / EXTI ,可细分为:
-
设置 GPIO 输入模式
-
使能 AFIO 时钟
-
设置 EXTI 和 IO 对应关系
-
设置 EXTI 屏蔽,上/下沿
-
【注意】这些都是在 HAL 库中是使用函数
HAL_GPIO_Init()一步到位的。
-
-
配置 NVIC(前面说过)
-
设置中断分组:
HAL_NVIC_SetPriorityGrouping,此函数仅需设置一次!(这里给出但实际并不用设置,因为已经在
HAL_Init()中设置过了) -
设置中断优先级:
HAL_NVIC_SetPriority -
使能中断:
HAL_NVIC_EnablelRQ
-
-
设计中断服务函数
- 编写
EXTIx_IRQHandler中断服务函数,同时清除中断标志 ! - 中断服务函数中要调用中断处理公用函数(这里清除中断标志),然后公用函数中还要调用数据处理回调函数(见下方图解)。回调函数事先已经被定义了,这是一个弱定义函数,里面没有实际的函数体,我们通常会重定义该函数(名字不能变)。
- 中断服务函数和中断回调函数都不需要自己再声明了!原来的官方库中已经声明过了!
- 在单片机的 HAL 库开发中,!!!中断服务函数(Interrupt Service Routine, ISR)的名字必须与 .s 启动文件(startup file)中的中断向量表(Interrupt Vector Table)中定义的函数名完全一致!!!。
- 编写
【注意】STM32 仅有:EXTI0、EXTI1、EXTI2、EXTI3、EXTI4、EXTI9_5、EXTI15_10,7个外部中断服务函数。
2. HAL库 中断回调处理机制介绍
比较复杂,和标准库不同,这里先给出简图:

④ 实例:按键触发外部中断控制LED
首先,分析按键 IO 应该设置成什么模式:
要看原理图中,按键连的是 VCC 还是 GND 。下方图中,当 KEY_UP 按键按下时, WK_UP 左边连接 VCC ,按键松开时应让此 IO 口处于低电平,那么 WK_UP 应该配置为输入下拉(默认低电平);当 KEY0 按键按下时(KEY1同理), KEY0 引脚左边连接接地,按键松开时应让此 IO 口处于高电平,那么 KEY0 应该配置为输入上拉(默认高电平)。

要求: 使用 KEY_UP 按键配置上升沿触发控制 LED0 的亮灭;使用 KEY0 按键配置下降沿触发控制 LED1 的亮灭;
部分核心代码如下:
exti.c 文件:
#include "exti.h"
#include "delay.h"
#include "led.h"
#include "key.h"
//外部中断初始化
void EXTI_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE(); // 开启GPIOA时钟
__HAL_RCC_GPIOE_CLK_ENABLE(); // 开启GPIOE时钟
GPIO_Initure.Pin=GPIO_PIN_0; // PA0
GPIO_Initure.Mode=GPIO_MODE_IT_RISING; // 上升沿触发
GPIO_Initure.Pull=GPIO_PULLDOWN;
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
GPIO_Initure.Pin=GPIO_PIN_3|GPIO_PIN_4; // PE3/PE4
GPIO_Initure.Mode=GPIO_MODE_IT_FALLING; // 下降沿触发
GPIO_Initure.Pull=GPIO_PULLUP;
HAL_GPIO_Init(GPIOE,&GPIO_Initure);
// 中断线0-PA0
HAL_NVIC_SetPriority(EXTI0_IRQn,2,0); // 抢占优先级为2,子优先级为0
HAL_NVIC_EnableIRQ(EXTI0_IRQn); // 使能中断线0
// 中断线3-PE3
HAL_NVIC_SetPriority(EXTI3_IRQn,2,2); // 抢占优先级为2,子优先级为2
HAL_NVIC_EnableIRQ(EXTI3_IRQn); // 使能中断线2
// 中断线4-PE4
HAL_NVIC_SetPriority(EXTI4_IRQn,2,3); // 抢占优先级为2,子优先级为3
HAL_NVIC_EnableIRQ(EXTI4_IRQn); // 使能中断线4
}
// EXTI0 中断服务函数
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); // 调用中断处理公用函数
}
// EXTI3 中断服务函数
void EXTI3_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3); // 调用中断处理公用函数
}
// EXTI4 中断服务函数
void EXTI4_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4); // 调用中断处理公用函数
}
// 中断服务程序中需要做的事情
// 在HAL库中所有的外部中断服务函数都会调用此函数
// GPIO_Pin:中断引脚号
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(100); // 消抖
switch(GPIO_Pin)
{
case GPIO_PIN_0:
if(WK_UP==1)
{
LED0=!LED0; // 控制LED0反转
}
break;
case GPIO_PIN_3:
if(KEY1==0) // 控制LED1反转
{
LED1=!LED1;
}
break;
case GPIO_PIN_4:
if(KEY0==0)
{
LED0=!LED0;
LED1=!LED1;
}
break;
}
}
其中 HAL_GPIO_Init() 函数配置中,引脚的边沿触发方式配置 GPIO_Initure.Mode 选择如下:
#define GPIO_MODE_IT_RISING 0x10110000u // 上升沿触发
#define GPIO_MODE_IT_FALLING 0x10210000u // 下降沿触发
#define GPIO_MODE_IT_RISING_FALLING 0x10310000u // 双边沿触发
exti.h 文件(中断服务函数和中断回调函数都不需要自己再声明了!原来的官方库中已经声明过了!):
#ifndef _EXTI_H
#define _EXTI_H
#include "sys.h"
void EXTI_Init(void);
#endif
main.c 文件:
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "exti.h"
/*下面主函数是使用HAL库函数实现控制IO口输出*/
int main(void)
{
HAL_Init(); // 初始化HAL库
Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
delay_init(72); // 初始化延时函数
LED_Init(); // 初始化LED
KEY_Init(); // 初始化按键
EXTI_Init(); // 初始化外部中断
while(1)
{
// 正常执行程序
HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5); // LED1翻转
delay_ms(1000);
}
}
HAL 库开发 ➤ 超详细的中断讲解(主要讲解外部中断),包括 NVIC、EXTI、配置方法和示例&spm=1001.2101.3001.5002&articleId=154005435&d=1&t=3&u=2dfe417af1b9416ba1b383e71ba75f92)
1492

被折叠的 条评论
为什么被折叠?



