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

Arduino定时器中Delay()正确用法:解决按钮响应延迟问题

Arduino 10分钟倒计时定时器优化方案

你遇到的核心问题是delay()函数的阻塞特性——当程序执行delay(1000)或delay(5000)时,整个程序会暂停,完全无法响应按钮输入。解决办法是用Arduino的millis()函数实现非阻塞式计时,让程序在等待计时的同时仍能持续检测按钮状态。

优化要点

  • 用millis()替代delay()跟踪时间间隔,避免程序阻塞
  • 给按钮添加简单消抖逻辑,防止误触发
  • 将蜂鸣器的5秒响铃也改为非阻塞实现,避免倒计时结束后程序卡死
  • 重构倒计时逻辑,让loop()能快速循环,实时响应输入

修改后的完整代码

const int btnOne = 8;
const int btnTwo = 9;
const int clockPin = 10;
const int dataPin = 11;
const int buzzer = 3;

// 按钮状态相关
int lastBtnOneState = HIGH;
int lastBtnTwoState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50; // 50ms消抖

// 计时相关
const long totalTime = 600000; // 10分钟=600000ms
long remainingTime = totalTime;
int timerDisplay = 1000; // 四位数码管显示:1000代表10:00
bool isRunning = false;
bool isBeeping = false;

unsigned long previousMillis = 0;
const unsigned long interval = 1000; // 每秒更新一次
unsigned long beepStartMillis = 0;
const unsigned long beepDuration = 5000; // 蜂鸣器响5秒

void setup() {
  Serial.begin(9600);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(btnOne, INPUT_PULLUP);
  pinMode(btnTwo, INPUT_PULLUP);
  pinMode(buzzer, OUTPUT);
  Display.show(timerDisplay); // 初始化显示10:00
}

void loop() {
  unsigned long currentMillis = millis();

  // 处理按钮1:启停控制(带消抖)
  int currentBtnOneState = digitalRead(btnOne);
  if (currentBtnOneState != lastBtnOneState) {
    lastDebounceTime = currentMillis;
  }
  if ((currentMillis - lastDebounceTime) > debounceDelay) {
    if (currentBtnOneState != lastBtnOneState) {
      lastBtnOneState = currentBtnOneState;
      if (currentBtnOneState == LOW) { // 按钮按下(INPUT_PULLUP模式下按下为LOW)
        isRunning = !isRunning;
        Serial.println(isRunning ? "定时器启动" : "定时器暂停");
      }
    }
  }

  // 处理按钮2:重置定时器(带消抖)
  int currentBtnTwoState = digitalRead(btnTwo);
  if (currentBtnTwoState != lastBtnTwoState) {
    lastDebounceTime = currentMillis;
  }
  if ((currentMillis - lastDebounceTime) > debounceDelay) {
    if (currentBtnTwoState != lastBtnTwoState) {
      lastBtnTwoState = currentBtnTwoState;
      if (currentBtnTwoState == LOW) {
        remainingTime = totalTime;
        timerDisplay = 1000;
        Display.show(timerDisplay);
        isRunning = false;
        isBeeping = false;
        noTone(buzzer);
        Serial.println("定时器已重置");
      }
    }
  }

  // 非阻塞倒计时逻辑
  if (isRunning && !isBeeping && remainingTime > 0) {
    if (currentMillis - previousMillis >= interval) {
      previousMillis = currentMillis;
      remainingTime -= interval;
      timerDisplay--;
      Display.show(timerDisplay);
      Serial.print("剩余时间(ms): ");
      Serial.println(remainingTime);

      // 倒计时结束,启动蜂鸣器
      if (remainingTime <= 0) {
        isRunning = false;
        isBeeping = true;
        beepStartMillis = currentMillis;
        tone(buzzer, 200);
        Display.show(0); // 显示0000
        Serial.println("倒计时结束");
      }
    }
  }

  // 非阻塞蜂鸣器控制
  if (isBeeping) {
    if (currentMillis - beepStartMillis >= beepDuration) {
      isBeeping = false;
      noTone(buzzer);
      Serial.println("蜂鸣器停止");
    }
  }
}

关键代码解释

  1. 非阻塞计时:

    • currentMillis = millis()获取当前系统运行时间(毫秒)
    • 通过对比currentMillis和previousMillis,判断是否到了1秒的更新间隔,只在满足条件时执行倒计时操作,其余时间程序可以继续检测按钮输入
  2. 按钮消抖:

    • 利用debounceDelay过滤按钮按下时的电平抖动,只有当电平稳定50ms后才判定为有效输入,避免误触发
  3. 非阻塞蜂鸣器:

    • 倒计时结束后记录蜂鸣器启动时间beepStartMillis,每次loop()检查是否达到5秒时长,到达后停止蜂鸣器,期间程序仍能响应按钮重置操作

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 13:30:51