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

EXTI按键中断异常:ButtonPress不更新且主程序卡顿求助

问题原因与解决方案

1. 核心原因:变量作用域冲突

你在全局作用域定义了uint32_t ButtonPress = 0;,但又在main()函数内重新定义了同名局部变量uint32_t ButtonPress = 0;。中断服务函数EXTI0_1_IRQHandler修改的是全局变量,而主循环的switch判断的是main内的局部变量,两者属于完全独立的内存空间,因此主程序永远看不到中断对变量的修改。

2. 关键补充问题:缺失volatile修饰

中断与主程序共享的变量必须用volatile修饰,目的是告诉编译器该变量可能被异步操作(比如中断)修改,禁止编译器将其缓存到寄存器中。如果不加volatile,编译器会优化主程序中对变量的读取逻辑,导致即使全局变量被中断修改,主程序也可能读取到旧值。

3. 额外优化点:中断标志位处理不严谨

你同时开启了上升沿和下降沿触发(RTSR1和FTSR1都置位),但仅清除了上升沿标志(EXTI->RPR1 |= (1U << 1);),未处理下降沿标志(需要操作EXTI->FPR1),可能导致中断重复触发。


修正后的代码

/* 1 sec is 1600000 */
void delay(volatile uint32_t);

// 全局变量添加volatile修饰
volatile uint32_t ButtonPress = 0;

/* Interrupt Handlers */
void EXTI0_1_IRQHandler(void){
    ButtonPress++;
    // 同时清除上升沿和下降沿中断标志
    EXTI->RPR1 |= (1U << 1);
    EXTI->FPR1 |= (1U << 1);
}

int main(void) {
    // 移除main内的同名局部变量定义
    /* Enable GPIOA clock */
    RCC->IOPENR |= (1U << 0);

    /* Setup PA0 as output */
    GPIOA->MODER &= ~(3U << 2*0);
    GPIOA->MODER |= (1U << 2*0);

    /* Setup PA1 as input */
    GPIOA->MODER &= ~(3U << 2*1);
    GPIOA->MODER |= (0U << 2*1);

    /* External interrupt at PA1 port */
    EXTI->EXTICR[0] |= (0U << 8*1);

    /* Mask and Rising/Falling on Px1 */
    EXTI->IMR1  |= (1U << 1);
    EXTI->RTSR1 |= (1U << 1);
    EXTI->FTSR1 |= (1U << 1);

    /* Setup NVIC */
    NVIC_SetPriority(EXTI0_1_IRQn, 0);
    NVIC_EnableIRQ(EXTI0_1_IRQn);

    while(1) {
        switch(ButtonPress){
            case 0:
                /* Turn off LED */
                GPIOA->ODR = (0U << 0);
                break;
            case 1:
                /* Turn on LED 2sec interval */
                GPIOA->ODR |= (1U << 0);
                delay(3200000);
                GPIOA->ODR ^= (1U << 0);
                delay(3200000);
                break;
            case 2:
                /* Turn on LED 1sec interval */
                GPIOA->ODR |= (1U << 0);
                delay(1600000);
                GPIOA->ODR ^= (1U << 0);
                delay(1600000);
                break;
            case 3:
                /* Turn on LED 0.5sec interval */
                GPIOA->ODR |= (1U << 0);
                delay(800000);
                GPIOA->ODR ^= (1U << 0);
                delay(800000);
                break;
            case 4:
                /* Turn on LED 0.1sec interval */
                GPIOA->ODR |= (1U << 0);
                delay(160000);
                GPIOA->ODR ^= (1U << 0);
                delay(160000);
                break;
            case 5:
                /* Turn on LED */
                GPIOA->ODR |= (1U << 0);
                break;
            case 6:
                /* Button Clear */
                ButtonPress = 0;
                break;
        }
    }
    return 0;
}

void delay(volatile uint32_t s) {
    for(; s>0; s--);
}

关键修改点总结

  • 删除main()函数内的局部ButtonPress变量,确保主程序和中断操作的是同一个全局变量
  • 给全局ButtonPress添加volatile修饰,避免编译器优化导致的读取旧值问题
  • 在中断服务函数中同时清除上升沿和下降沿的中断标志,防止重复触发中断

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 16:21:10