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
相关产品推荐
相关产品推荐

