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

STM32F401寄存器实现按键消抖失效问题求助

STM32F401按键消抖问题分析与修正

原代码核心问题

  1. 中断与轮询逻辑冲突:同时使用EXTI中断和轮询读取引脚,但中断仅翻转flag未与消抖逻辑联动,导致状态混乱。
  2. TIM3未初始化:消抖函数直接开启TIM3,但未配置时钟、预分频器等参数,计数器无法正确计时。
  3. 消抖逻辑错误:
    • 电平判断可能与硬件实际逻辑相反(通常按键按下为低电平,原代码高电平时记录计时);
    • pressTime仅记录按下瞬间的计数器值,未计算按键持续稳定时间;
    • lastButtonPress初始值与引脚默认状态(上拉输入为高电平)不匹配,首次判断会触发错误更新。
  4. 主循环逻辑混乱:debounceButton返回1后未重置press标志,导致后续调用持续返回1;for循环被x=x-1/x=x+1修改,逻辑冗余且易出错。
  5. EXTI初始化不完整:未看到EXTI15_10_Init()的具体实现,可能缺失GPIO输入模式、EXTI线路映射、触发方式配置。

修正方案:中断+定时器消抖(高效可靠)

1. 完善GPIO、EXTI、NVIC初始化

void GPIO_Config(void) {
    // 开启GPIOC时钟
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;
    // PC13配置为上拉输入(适配按键硬件)
    GPIOC->MODER &= ~GPIO_MODER_MODE13;
    GPIOC->PUPDR |= GPIO_PUPDR_PUPD13_0;
}

void EXTI_Config(void) {
    // 开启SYSCFG时钟
    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
    // 映射PC13到EXTI13
    SYSCFG->EXTICR[3] &= ~SYSCFG_EXTICR4_EXTI13;
    SYSCFG->EXTICR[3] |= SYSCFG_EXTICR4_EXTI13_PC;
    // EXTI13配置为下降沿触发(按键按下时触发)
    EXTI->FTSR |= EXTI_FTSR_TR13;
    // 使能EXTI13中断
    EXTI->IMR |= EXTI_IMR_MR13;
}

void NVIC_Config(void) {
    // 设置EXTI15_10中断优先级
    NVIC->IP[EXTI15_10_IRQn] = (1 << 4);
    // 使能EXTI15_10中断
    NVIC->ISER[EXTI15_10_IRQn / 32] |= (1 << (EXTI15_10_IRQn % 32));
}

void Interrupt_Init(void) {
    GPIO_Config();
    EXTI_Config();
    NVIC_Config();
}

2. 配置TIM3用于消抖计时(10ms中断一次)

void TIM3_Config(void) {
    // 开启TIM3时钟
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
    // 预分频器:APB1时钟42MHz,分频后计数器时钟1kHz
    TIM3->PSC = 42000 - 1;
    // 自动重装载值:10ms触发一次中断
    TIM3->ARR = 10 - 1;
    // 使能更新中断
    TIM3->DIER |= TIM_DIER_UIE;
    // 初始禁止定时器,等待EXTI触发后开启
    TIM3->CR1 &= ~TIM_CR1_CEN;
    // 配置TIM3中断优先级
    NVIC->IP[TIM3_IRQn] = (2 << 4);
    NVIC->ISER[TIM3_IRQn / 32] |= (1 << (TIM3_IRQn % 32));
}

3. 全局变量与中断处理函数

volatile bool buttonPressed = false; // 有效按键按下标志
volatile uint8_t debounceCount = 0;  // 消抖连续检测计数

// EXTI中断:触发后启动TIM3开始消抖计时
void EXTI15_10_IRQHandler(void) {
    if (EXTI->PR & EXTI_PR_PR13) {
        EXTI->PR |= EXTI_PR_PR13; // 清除中断标志
        TIM3->CNT = 0;            // 重置计数器
        TIM3->CR1 |= TIM_CR1_CEN; // 开启TIM3
    }
}

// TIM3中断:连续检测按键状态,确认稳定后标记有效按下
void TIM3_IRQHandler(void) {
    if (TIM3->SR & TIM_SR_UIF) {
        TIM3->SR &= ~TIM_SR_UIF; // 清除中断标志
        
        // 读取PC13状态:低电平表示按键按下
        if (!(GPIOC->IDR & GPIO_IDR_ID13)) {
            debounceCount++;
            // 连续2次检测到按下(20ms),确认有效
            if (debounceCount >= 2) {
                buttonPressed = true;
                debounceCount = 0;
                TIM3->CR1 &= ~TIM_CR1_CEN; // 关闭定时器
            }
        } else {
            // 按键释放,重置计数并关闭定时器
            debounceCount = 0;
            TIM3->CR1 &= ~TIM_CR1_CEN;
        }
    }
}

4. 简化主循环逻辑

int main(void) {
    Interrupt_Init();
    TIM3_Config();
    // 此处补充TIM2的PWM初始化代码(略)
    
    static int x = 0;
    static bool increasing = true;
    
    while (1) {
        // 处理按键事件:反转计数方向
        if (buttonPressed) {
            buttonPressed = false;
            increasing = !increasing;
        }
        
        // 更新TIM2的CCR1值
        if (increasing) {
            x = (x < 500) ? x + 1 : 500;
        } else {
            x = (x > 0) ? x - 1 : 0;
        }
        TIM2->CCR1 = x;
        delayTRIP(1);
    }
}

关键说明

  • 采用EXTI触发+定时器中断消抖的方式,避免轮询占用CPU资源,同时保证消抖可靠性;
  • 消抖时间通过连续检测次数调整(示例中为20ms),可根据硬件实际情况修改;
  • 主循环改用状态机方式控制计数方向,避免原for循环的逻辑混乱问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 15:35:01