You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

STM32L072CZ LoRa节点中操作Switch-1时Switch-2误触发中断的原因咨询

偶发中断混淆问题:操作Switch-1却触发Switch-2的ISR的原因与解决方法

我在基于STM32L072CZ的LoRa LSN50-V2节点上开发应用层时,也遇到过类似的中断混淆问题。结合你提供的代码和STM32的EXTI机制,咱们来拆解问题根源和解决办法:

先理清楚你的场景与代码现状

你配置了PB14(Switch-1,对应GPIO_MAIN_PIN)和PB15(Switch-2,对应GPIO_DG_PIN)为上升/下降沿触发的外部中断,共享EXTI4_15_IRQn中断通道。多数情况下中断能正确更新状态,但偶发操作Switch-1时,错误触发Switch-2的中断逻辑。

你的核心中断处理代码片段:

/* 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);
    }
}

问题根源分析

  1. 中断标志位的重复处理冲突
    你先手动调用__HAL_GPIO_EXTI_CLEAR_IT清除标志,紧接着又调用HAL_GPIO_EXTI_IRQHandler——而这个HAL库函数内部也会自动清除对应引脚的EXTI中断标志。这种重复操作会导致标志位状态混乱,尤其在有信号毛刺或者中断触发时序接近时,系统可能误判触发源。

  2. HAL库中断流程被打乱
    STM32 HAL库的EXTI中断设计是:共享IRQ通道(比如EXTI4_15_IRQn)的入口函数中,逐个调用对应引脚的HAL_GPIO_EXTI_IRQHandler,然后在HAL_GPIO_EXTI_Callback中处理业务逻辑。你现在的写法跳过了标准流程,手动检查标志位再调用HAL处理函数,很容易引发逻辑冲突。

  3. ISR中使用HAL_Delay的风险
    HAL_Delay依赖SysTick定时器中断,而在ISR中调用它:

    • 如果SysTick的中断优先级低于EXTI,会导致延迟时间不准;
    • 延迟期间如果有其他中断触发(比如LoRa模块的中断),会导致EXTI标志位堆积,进一步加剧混淆;
    • 阻塞ISR执行,影响整个系统的实时性。
  4. 同一个IRQ通道的优先级被重复覆盖
    你在mainInitdgInit中都调用了HAL_NVIC_SetPriority(EXTI4_15_IRQn, ...)——EXTI4_15_IRQn是同一个中断通道,多次设置优先级会直接覆盖之前的配置,最终优先级是最后一次设置的值(你的代码中是1)。这会打乱中断响应的预期顺序,可能导致中断处理时序异常。

解决办法

1. 回归HAL库标准中断处理流程

重构你的中断入口和回调函数,遵循HAL的设计逻辑:

// 共享IRQ通道的入口函数
void EXTI4_15_IRQHandler(void)
{
    // 按顺序处理两个引脚的EXTI中断
    HAL_GPIO_EXTI_IRQHandler(GPIO_DG_PIN);
    HAL_GPIO_EXTI_IRQHandler(GPIO_MAIN_PIN);
}

// HAL库的EXTI回调函数,在这里处理业务逻辑
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (joined_flags != 1) return;

    // 优化防抖:建议用定时器防抖替代HAL_Delay,这里先保留兼容你的逻辑
    // 注意:如果必须用HAL_Delay,要确保SysTick优先级低于EXTI4_15_IRQn
    HAL_Delay(30);

    // 重新检查中断标志,确保是有效触发
    if (GPIO_Pin == GPIO_DG_PIN && __HAL_GPIO_EXTI_GET_IT(GPIO_DG_PIN) != RESET)
    {
        mainStreamer = 0;
        dgStreamer = 1;
        __HAL_GPIO_EXTI_CLEAR_IT(GPIO_DG_PIN);
    }
    else if (GPIO_Pin == GPIO_MAIN_PIN && __HAL_GPIO_EXTI_GET_IT(GPIO_MAIN_PIN) != RESET)
    {
        mainStreamer = 1;
        dgStreamer = 0;
        __HAL_GPIO_EXTI_CLEAR_IT(GPIO_MAIN_PIN);
    }
}

2. 移除重复的优先级设置

HAL_NVIC_SetPriority(EXTI4_15_IRQn, ...)HAL_NVIC_EnableIRQ(EXTI4_15_IRQn)移到一个初始化函数中(比如mainInit),只执行一次,避免重复覆盖:

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 );

    // 只在这里配置一次共享IRQ的优先级和使能
    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 );

    // 移除这里的优先级设置和IRQ使能
}

3. 替换ISR中的HAL_Delay为定时器防抖

在ISR中启动一个30ms的定时器,定时器溢出时再检查引脚状态并更新标志位,这样ISR可以快速退出,避免阻塞:

// 全局变量标记防抖状态
uint8_t main_switch_debounce = 0;
uint8_t dg_switch_debounce = 0;

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (joined_flags != 1) return;

    if (GPIO_Pin == GPIO_DG_PIN)
    {
        dg_switch_debounce = 1;
        // 启动30ms定时器(假设你用TIM2,需提前初始化)
        HAL_TIM_Base_Start_IT(&htim2);
    }
    else if (GPIO_Pin == GPIO_MAIN_PIN)
    {
        main_switch_debounce = 1;
        HAL_TIM_Base_Start_IT(&htim2);
    }
}

// 定时器溢出回调
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM2)
    {
        HAL_TIM_Base_Stop_IT(&htim2);
        
        if (dg_switch_debounce)
        {
            dg_switch_debounce = 0;
            // 检查引脚实际状态,确认触发有效
            if (HAL_GPIO_ReadPin(GPIO_DG_PORT, GPIO_DG_PIN) == GPIO_PIN_RESET)
            {
                mainStreamer = 0;
                dgStreamer = 1;
            }
        }
        else if (main_switch_debounce)
        {
            main_switch_debounce = 0;
            if (HAL_GPIO_ReadPin(GPIO_MAIN_PORT, GPIO_MAIN_PIN) == GPIO_PIN_RESET)
            {
                mainStreamer = 1;
                dgStreamer = 0;
            }
        }
    }
}

4. 确认EXTI引脚映射配置

检查HW_GPIO_Init函数是否正确开启了SYSCFG时钟(STM32L0系列需要手动开启),并正确映射了PB14到EXTI14、PB15到EXTI15。如果底层函数没处理,需要在初始化中添加:

__HAL_RCC_SYSCFG_CLK_ENABLE();

总结

最可能导致你问题的直接原因是中断标志位的重复清除操作打乱了HAL库的标准中断流程,再加上ISR中使用HAL_Delay引发的时序异常。按照上面的步骤调整后,应该能解决偶发的中断混淆问题。

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

火山引擎 最新活动