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("蜂鸣器停止"); } } }
关键代码解释
非阻塞计时:
currentMillis = millis()获取当前系统运行时间(毫秒)- 通过对比
currentMillis和previousMillis,判断是否到了1秒的更新间隔,只在满足条件时执行倒计时操作,其余时间程序可以继续检测按钮输入
按钮消抖:
- 利用
debounceDelay过滤按钮按下时的电平抖动,只有当电平稳定50ms后才判定为有效输入,避免误触发
- 利用
非阻塞蜂鸣器:
- 倒计时结束后记录蜂鸣器启动时间
beepStartMillis,每次loop()检查是否达到5秒时长,到达后停止蜂鸣器,期间程序仍能响应按钮重置操作
- 倒计时结束后记录蜂鸣器启动时间
内容的提问来源于stack exchange,提问作者Nicolatey
相关产品推荐
相关产品推荐

