You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32L072CZ双EXTI中断异常排查:操作Switch-1时Switch-2中断ISR误触发问题分析

分析STM32L072CZ LoRa节点EXTI中断误触发问题

开发背景

我基于STM32L072CZ微控制器做项目开发,集成了LoRa通信功能,是在LoRa LSN50-V2(集成STM32与LoRa芯片的节点)的底层固件基础上编写应用层程序。

配置说明

我把PB14(对应Switch-1)和PB15(对应Switch-2)两个引脚配置成上升沿+下降沿触发的EXTI中断,用宏定义来管理引脚、时钟及端口的初始化、配置和反初始化,相关代码如下:

void mainInit( void ) { 
 GPIO_InitTypeDef GPIO_InitStruct={0}; 
 GPIO_MAIN_CLK_ENABLE(); 
 GPIO_InitStruct.Pin = GPIO_MAIN_PIN; 
 GPIO_InitStruct.Mode =GPIO_MODE_IT_RISING_FALLING; 
 GPIO_InitStruct.Pull = GPIO_PULLUP; 
 GPIO_InitStruct.Speed=GPIO_SPEED_HIGH; 
 HW_GPIO_Init( GPIO_MAIN_PORT, GPIO_MAIN_PIN, &GPIO_InitStruct ); 
 /* Enable and set EXTI lines 4 to 15 Interrupt to the lowest priority */ 
 HAL_NVIC_SetPriority(EXTI4_15_IRQn, 0, 0); 
 HAL_NVIC_EnableIRQ(EXTI4_15_IRQn); 
} 
void dgInit( void ) { 
 GPIO_InitTypeDef GPIO_InitStruct={0}; 
 GPIO_DG_CLK_ENABLE(); 
 GPIO_InitStruct.Pin = GPIO_DG_PIN; 
 GPIO_InitStruct.Mode =GPIO_MODE_IT_RISING_FALLING; 
 GPIO_InitStruct.Pull = GPIO_PULLUP; 
 GPIO_InitStruct.Speed=GPIO_SPEED_HIGH; 
 HW_GPIO_Init( GPIO_DG_PORT, GPIO_DG_PIN, &GPIO_InitStruct ); 
 /* Enable and set EXTI lines 4 to 15 Interrupt to the lowest priority */ 
 HAL_NVIC_SetPriority(EXTI4_15_IRQn, 1, 0); 
 HAL_NVIC_EnableIRQ(EXTI4_15_IRQn); 
} 
void mainIoDeInit( void ) { 
 GPIO_InitTypeDef GPIO_InitStruct={0}; 
 GPIO_InitStruct.Pull = GPIO_PULLDOWN; 
 GPIO_InitStruct.Mode =GPIO_MODE_IT_RISING_FALLING; 
 HW_GPIO_Init( GPIO_MAIN_PORT, GPIO_MAIN_PIN, &GPIO_InitStruct ); 
} 
void dgIoDeInit( void ) { 
 GPIO_InitTypeDef GPIO_InitStruct={0}; 
 GPIO_InitStruct.Pull = GPIO_PULLDOWN; 
 GPIO_InitStruct.Mode =GPIO_MODE_IT_RISING_FALLING; 
 HW_GPIO_Init( GPIO_DG_PORT, GPIO_DG_PIN, &GPIO_InitStruct ); 
} 
/* IRQ handler functionality*/ 
//HAL_GPIO_EXTI_IRQHandler( GPIO_PIN_14 ); 
if (joined_flags==1) { 
 if(__HAL_GPIO_EXTI_GET_IT(GPIO_DG_PIN)!=RESET) { 
 HAL_Delay(30); 
 mainStreamer=0; 
 dgStreamer=1; 
 __HAL_GPIO_EXTI_CLEAR_IT(GPIO_DG_PIN); 
 HAL_GPIO_EXTI_IRQHandler(GPIO_DG_PIN); 
 } 
 //HAL_GPIO_EXTI_IRQHandler( GPIO_PIN_15 ); 
 if(__HAL_GPIO_EXTI_GET_IT(GPIO_MAIN_PIN)!=RESET) { 
 HAL_Delay(30); 
 mainStreamer=1; 
 dgStreamer=0; 
 __HAL_GPIO_EXTI_CLEAR_IT(GPIO_MAIN_PIN); 
 HAL_GPIO_EXTI_IRQHandler(GPIO_MAIN_PIN); 
 } 
}

问题现象

大部分时候中断都能正常触发并更新开关状态,但偶尔会出问题:当Switch-2处于空闲状态时,我只操作Switch-1,却触发了Switch-2的中断服务程序(ISR)。


问题原因分析

结合STM32 EXTI的工作机制和你的代码实现,我梳理了几个可能的诱因:

1. EXTI中断组的优先级配置逻辑有误

你要注意,PB14和PB15都属于EXTI4-15中断组,它们共享同一个EXTI4_15_IRQn中断向量。你在mainInit()里给这个中断向量设了优先级0,又在dgInit()里改成了优先级1——但STM32的硬件逻辑里,同一个中断向量下的多个EXTI线,优先级配置是全局生效的,不会单独区分引脚。而且当多个EXTI线同时触发时,硬件不会帮你排序,得靠ISR里的代码逐个判断触发源,这就给误判留了空间。

2. 中断标志位的处理顺序混乱

看你的ISR代码,你先手动调用__HAL_GPIO_EXTI_CLEAR_IT()清除标志,然后又调用HAL_GPIO_EXTI_IRQHandler()——这个HAL函数内部还会再执行一次标志位清除,同时还会触发HAL_GPIO_EXTI_Callback()回调。如果此时刚好有电磁干扰导致另一个引脚的标志位被意外置位,就很容易被误当成有效触发来处理。

另外,绝对不要在ISR里用HAL_Delay()!HAL_Delay依赖SysTick定时器计时,而中断上下文会打断SysTick的计数,不仅延时不准,还会拉长ISR的执行时间,让其他中断的标志位累积,增加误触发的概率。

3. 硬件干扰或开关抖动的影响

虽然你配置了上拉电阻,但LoRa节点工作时的射频信号可能会产生电磁干扰,给引脚带来毛刺信号,误触发EXTI中断。你加的30ms延时本来是用来防抖的,但放在ISR里执行反而会让问题恶化——这段时间里如果有干扰信号触发了另一个引脚的标志,就会被当成有效中断处理。

4. 反初始化函数的逻辑不符合预期

你的mainIoDeInit()和dgIoDeInit()里,只是把上拉改成了下拉,但引脚仍然保持GPIO_MODE_IT_RISING_FALLING的中断模式。如果某些场景下调用了这些反初始化函数,引脚还是会响应电平变化,要是此时下拉后的引脚出现电平波动,就可能误触发中断,而且此时的处理逻辑和正常初始化后的逻辑不一致,更容易出问题。


建议的修复方向

  • 调整ISR的标志位处理流程:先判断中断标志,然后把防抖逻辑移到主循环(用定时器记录触发时间,主循环判断是否达到防抖时长再更新状态),最后只清除当前触发的标志位,不要再调用HAL_GPIO_EXTI_IRQHandler(),避免重复操作标志位。
  • 移除ISR中的延时函数:改用定时器防抖,比如在ISR里记录当前的系统tick值,然后在主循环中检查当前tick和记录的tick差值是否大于防抖时间,再执行状态更新。
  • 简化中断优先级配置:因为两个引脚共享同一个中断向量,只需要在初始化时设置一次优先级即可,不需要重复设置。
  • 优化硬件抗干扰:给开关引脚并接100nF的陶瓷滤波电容,布线时远离LoRa射频电路,减少干扰信号的影响。
  • 修正反初始化函数:如果反初始化是为了关闭中断,应该把引脚模式改成GPIO_MODE_INPUT或者GPIO_MODE_ANALOG,同时关闭对应的EXTI中断线,彻底禁用中断触发。

内容的提问来源于stack exchange,提问作者Annakkili Arumugam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 06:44:29