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

Arduino 按钮抖动问题:外接下拉电阻及消抖电容后中断触发仍异常

Arduino按钮控制秒表异常问题修复方案

核心故障根因

  • 硬件接线与引脚配置冲突:外部电路为10k下拉设计,按钮未按下时引脚默认电平为LOW,但代码启用了INPUT_PULLUP内置上拉,强制引脚默认电平为HIGH,内外电平冲突导致引脚状态无法正确识别。
  • 中断触发边沿不匹配:按当前硬件接线,按钮按下时引脚电平从LOW跳变到HIGH(上升沿),松开时才是下降沿,代码配置FALLING边沿触发中断,只有松开按钮才会触发ISR,和预期的按下触发逻辑不符。
  • 无软件消抖逻辑:仅靠硬件电容无法完全消除按键抖动,抖动产生的多次跳变边沿会反复触发ISR,导致buttonState多次翻转,单次按下的操作结果完全随机。
  • 代码逻辑缺陷:
    1. buttonState未初始化,上电后初始状态不确定,启动逻辑混乱。
    2. 存储时间的变量z、ms定义为有符号long类型,millis()返回无符号unsigned long,运行时间超过24天会发生溢出错误。
    3. loop中只要buttonState == LOW就重复重置startTime,不符合秒表启停的逻辑要求。
    4. 遗漏LCD初始化语句lcd.begin(),也可能导致显示异常。

修复方案

方案1(推荐,无需改动现有硬件)

  1. 将pinMode(button, INPUT_PULLUP)改为pinMode(button, INPUT),匹配外部下拉电路。
  2. 中断触发边沿改为RISING,匹配按钮按下时的电平跳变方向。
  3. 增加ISR消抖逻辑,记录上次中断触发时间,间隔小于20ms的触发直接忽略。
  4. 初始化buttonState为LOW,修正时间变量类型,调整loop中的时间赋值逻辑,仅在秒表启动时赋值一次startTime。

方案2(改动硬件,省外部电阻)

把按钮接线改为:一端接GND,另一端直接接数字引脚2,去掉10k下拉电阻,保留INPUT_PULLUP配置,中断触发边沿保留FALLING即可。

修复后完整代码(适配原硬件接线)

#include <LiquidCrystal.h>

LiquidCrystal lcd(5, 6, 9, 10, 11, 12);

unsigned long volatile startTime = 0;
unsigned long volatile sec;
volatile boolean buttonState = LOW; // 初始化按钮状态
volatile unsigned long lastIsrTime = 0; // 消抖用的上次中断触发时间
const int button = 2;
unsigned long ms;
unsigned long z;

void setup() {
  Serial.begin(9600);
  pinMode(button, INPUT); // 改为INPUT匹配外部下拉电路
  lcd.begin(16,2); // 补充LCD初始化配置
  lcd.setCursor(0, 0);
  lcd.print("Stopwatch");
  attachInterrupt(digitalPinToInterrupt(button), isr, RISING); // 改为上升沿触发
}

void loop() {
  static bool lastRunState = LOW;
  // 仅在秒表从停止切换到启动时重置一次开始时间
  if (buttonState == HIGH && lastRunState == LOW) {
    startTime = millis();
    lastRunState = HIGH;
  }
  if (buttonState == LOW && lastRunState == HIGH) {
    lastRunState = LOW;
  }

  // 秒表运行逻辑
  if (buttonState == HIGH) {
    z = millis() - startTime;
    sec = z / 1000;
    ms = z % 1000;
    lcd.setCursor(0, 1);
    lcd.print(sec);
    lcd.print(".");
    // 毫秒补零对齐,避免数字残留
    if(ms < 10) lcd.print("00");
    else if(ms < 100) lcd.print("0");
    lcd.print(ms);
    lcd.setCursor(9, 1);
    lcd.print("Secs");
  }
}

// 中断服务程序增加消抖逻辑
void isr() {
  unsigned long now = millis();
  if (now - lastIsrTime < 20) { // 20ms消抖时长可根据实际按键情况调整
    return;
  }
  lastIsrTime = now;
  buttonState = !buttonState;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 15:18:04