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

STM32G0矩阵键盘中断消抖问题及SSD显示异常求助

问题:STM32G0矩阵键盘EXTI中断按键抖动与显示异常排查

基于STM32G0芯片,为4x3矩阵键盘实现了EXTI4_15_IRQHandler中断处理函数,搭配4位SSD数码管。当前存在以下问题:

  • 按下按键时出现抖动问题,所有SSD数码管显示相同数字,但调试模式下一切正常
  • 添加延时后问题仍未解决
  • 移除中断内延时后,仅最后编写的Row3按键功能正常,其他行按键触发时所有SSD显示相同数字

原代码

中断处理函数

/* Interrupt Handler */
void EXTI4_15_IRQHandler(void){
    /* keypad press from C1 */
            KeypadAllRows_RESET();
    GPIOB->ODR |= (1U << 0);        //Row4
    if((GPIOB->IDR & (1U << 5)) == (1U << 5)) //2
        KeyPress = 0;

    KeypadAllRows_RESET();
    GPIOA->ODR |= (1U << 8);       //Row1
    if((GPIOB->IDR & (1U << 4)) == (1U << 4)) //1
        KeyPress = 1;

    if((GPIOB->IDR & (1U << 5)) == (1U << 5)) //2
            KeyPress = 2;

    if((GPIOB->IDR & (1U << 9)) == (1U << 9)) //3
        KeyPress = 3;

    KeypadAllRows_RESET();
    GPIOB->ODR |= (1U << 8);       //Row2
    if((GPIOB->IDR & (1U << 4)) == (1U << 4)) //4
        KeyPress = 4;

    if((GPIOB->IDR & (1U << 5)) == (1U << 5)) //5
        KeyPress = 5;

    if((GPIOB->IDR & (1U << 9)) == (1U << 9)) //6
        KeyPress = 6;

    KeypadAllRows_RESET();
    GPIOB->ODR |= (1U << 2);       //Row3
    if((GPIOB->IDR & (1U << 4)) == (1U << 4)) //7
        KeyPress = 7;

    if((GPIOB->IDR & (1U << 5)) == (1U << 5)) //8
        KeyPress = 8;

    if((GPIOB->IDR & (1U << 9)) == (1U << 9)) //9
        KeyPress = 9;


    SSD_Digit1 = SSD_Digit2;
    SSD_Digit2 = SSD_Digit3;
    SSD_Digit3 = SSD_Digit4;
    SSD_Digit4 = KeyPress;

    EXTI->RPR1 |= (1U << 4);
    EXTI->RPR1 |= (1U << 5);
    EXTI->RPR1 |= (1U << 9);
    KeypadAllRows_SET();
}

主循环代码

while(1) {
    SSD_Close();
    GPIOA->ODR |= (1U <<  7); //D4
    SSD_SET(SSD_Digit4);
    delay(200);

    SSD_Close();
    GPIOB->ODR |= (1U <<  7); //D3
    SSD_SET(SSD_Digit3);
    delay(200);

    SSD_Close();
    GPIOA->ODR |= (1U << 15); //D2
    SSD_SET(SSD_Digit2);
    delay(200);

    SSD_Close();
    GPIOA->ODR |= (1U <<  9); //D1
    SSD_SET(SSD_Digit1);
    delay(200);
}

问题根源分析

  1. 中断扫描逻辑缺陷:中断触发后,未在找到按键时立即停止扫描,后续行的电平检测会覆盖KeyPress值,最终只有最后一行的设置生效。
  2. 按键抖动未处理:调试模式下执行速度慢,抖动被自然过滤,但实际运行时抖动会导致电平误判或多次触发中断。
  3. 数码管扫描延时过长:主循环中200ms的延时会严重拖慢中断响应,导致按键事件处理不及时。
  4. EXTI标志位清除不规范:直接操作寄存器时容易出现遗漏,增加中断重复触发的概率。

修复方案

1. 优化中断内按键扫描逻辑

找到按键后立即终止扫描,避免值被覆盖:

void EXTI4_15_IRQHandler(void){
    KeyPress = -1; // 初始化无效值

    KeypadAllRows_RESET();
    GPIOB->ODR |= (1U << 0);        //Row4
    if((GPIOB->IDR & (1U << 5)) == (1U << 5)) { //2
        KeyPress = 0;
        goto KEY_SCAN_FINISH;
    }

    KeypadAllRows_RESET();
    GPIOA->ODR |= (1U << 8);       //Row1
    if((GPIOB->IDR & (1U << 4)) == (1U << 4)) { //1
        KeyPress = 1;
        goto KEY_SCAN_FINISH;
    }
    if((GPIOB->IDR & (1U << 5)) == (1U << 5)) { //2
        KeyPress = 2;
        goto KEY_SCAN_FINISH;
    }
    if((GPIOB->IDR & (1U << 9)) == (1U << 9)) { //3
        KeyPress = 3;
        goto KEY_SCAN_FINISH;
    }

    KeypadAllRows_RESET();
    GPIOB->ODR |= (1U << 8);       //Row2
    if((GPIOB->IDR & (1U << 4)) == (1U << 4)) { //4
        KeyPress = 4;
        goto KEY_SCAN_FINISH;
    }
    if((GPIOB->IDR & (1U << 5)) == (1U << 5)) { //5
        KeyPress = 5;
        goto KEY_SCAN_FINISH;
    }
    if((GPIOB->IDR & (1U << 9)) == (1U << 9)) { //6
        KeyPress = 6;
        goto KEY_SCAN_FINISH;
    }

    KeypadAllRows_RESET();
    GPIOB->ODR |= (1U << 2);       //Row3
    if((GPIOB->IDR & (1U << 4)) == (1U << 4)) { //7
        KeyPress = 7;
        goto KEY_SCAN_FINISH;
    }
    if((GPIOB->IDR & (1U << 5)) == (1U << 5)) { //8
        KeyPress = 8;
        goto KEY_SCAN_FINISH;
    }
    if((GPIOB->IDR & (1U << 9)) == (1U << 9)) { //9
        KeyPress = 9;
        goto KEY_SCAN_FINISH;
    }

KEY_SCAN_FINISH:
    if(KeyPress != -1) { // 仅有效按键更新数码管
        SSD_Digit1 = SSD_Digit2;
        SSD_Digit2 = SSD_Digit3;
        SSD_Digit3 = SSD_Digit4;
        SSD_Digit4 = KeyPress;
    }

    // 规范清除EXTI中断标志
    EXTI->RPR1 |= (EXTI_RPR1_RP4 | EXTI_RPR1_RP5 | EXTI_RPR1_RP9);
    KeypadAllRows_SET();
}

2. 添加软件消抖逻辑

检测到按键后延时10ms再次确认,避免抖动误判:

// 以Row4的按键检测为例
if((GPIOB->IDR & (1U << 5)) == (1U << 5)) {
    delay_ms(10); // 使用短延时函数,避免阻塞中断过久
    if((GPIOB->IDR & (1U << 5)) == (1U << 5)) {
        KeyPress = 0;
        goto KEY_SCAN_FINISH;
    }
}

更优方案是使用定时器消抖:第一次中断触发后启动定时器,定时器中断时再扫描按键,彻底避免中断阻塞。

3. 优化数码管扫描延时

缩短主循环延时至2ms,利用视觉暂留实现动态扫描,同时提升中断响应速度:

while(1) {
    SSD_Close();
    GPIOA->ODR |= (1U << 7); //D4
    SSD_SET(SSD_Digit4);
    delay_ms(2);

    SSD_Close();
    GPIOB->ODR |= (1U << 7); //D3
    SSD_SET(SSD_Digit3);
    delay_ms(2);

    SSD_Close();
    GPIOA->ODR |= (1U << 15); //D2
    SSD_SET(SSD_Digit2);
    delay_ms(2);

    SSD_Close();
    GPIOA->ODR |= (1U << 9); //D1
    SSD_SET(SSD_Digit1);
    delay_ms(2);
}

4. 规范GPIO操作

使用STM32 HAL库函数操作GPIO,确保原子性和可读性:

// 替换直接操作ODR的代码
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); // Row4

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 09:25:19