Arduino交通灯系统:实现双击按键暂停/续行功能的技术求助
Arduino交通灯系统:双击按键暂停/继续功能修复
问题概述
搭建小型Arduino交通灯系统时,无法通过双击按键实现程序暂停,且再次按键无法从当前运行位置继续执行,修改handleButtonPress函数后仍未解决。
问题根源分析
- 双击逻辑错误:原代码判断两次按键间隔大于800ms才计数,与双击需要的"短时间内两次按下"逻辑完全相反,导致无法触发双击事件。
- 按键事件冲突:
runFirstTask和runResetTask中直接读取按键状态,与handleButtonPress的按键处理逻辑冲突,干扰双击判断。 - 无状态保存机制:切换
programRunning状态时直接重置currentStep,未记录当前步骤的剩余运行时间,无法实现断点续行。 - 计数重置时机错误:按键释放时立即重置
buttonPressCount,导致第一次按下释放后计数清零,无法累计第二次按键。
修复后的完整代码
#define one_Red 3 #define one_Yellow 4 #define one_Green 5 #define two_Red 6 #define two_Yellow 7 #define two_Green 8 #define redLed 10 #define greenLed 9 #define button 2 // 系统状态 bool programRunning = false; bool systemPaused = false; // 新增:暂停状态标记 // 呼吸灯变量 int brightness = 0; int fadeAmount = 5; unsigned long previousMillis = 0; const long interval = 30; // 任务计时变量 unsigned long taskStartTime = 0; unsigned long remainingTime = 0; // 新增:记录当前步骤剩余时间 const unsigned long firstTaskDuration[] = {1000, 2000, 3000, 2000, 2000}; const unsigned long resetTaskDuration[] = {1000, 2000, 3000, 2000, 2000}; int currentStep = 0; // 按键处理变量 unsigned long lastButtonPress = 0; int buttonPressCount = 0; const unsigned long DOUBLE_CLICK_TIMEOUT = 300; // 双击超时时间(ms) const unsigned long DEBOUNCE_DELAY = 50; // 防抖延迟(ms) int lastButtonState = HIGH; unsigned long lastDebounceTime = 0; void setup() { pinMode(one_Red, OUTPUT); pinMode(one_Yellow, OUTPUT); pinMode(one_Green, OUTPUT); pinMode(two_Red, OUTPUT); pinMode(two_Yellow, OUTPUT); pinMode(two_Green, OUTPUT); pinMode(redLed, OUTPUT); pinMode(button, INPUT_PULLUP); // 初始状态 digitalWrite(one_Red, HIGH); digitalWrite(one_Yellow, HIGH); digitalWrite(one_Green, LOW); digitalWrite(two_Red, LOW); digitalWrite(two_Yellow, HIGH); digitalWrite(two_Green, HIGH); digitalWrite(redLed, HIGH); } void loop() { pulseRedLed(); handleButtonPress(); // 未暂停时执行任务,暂停时保持当前状态 if (!systemPaused) { if (programRunning) { runResetTask(); } else { runFirstTask(); } } } void handleButtonPress() { int currentButtonState = digitalRead(button); unsigned long currentMillis = millis(); // 按键防抖 if (currentButtonState != lastButtonState) { lastDebounceTime = currentMillis; } if ((currentMillis - lastDebounceTime) > DEBOUNCE_DELAY) { // 确认按键状态稳定 if (currentButtonState != lastButtonState) { lastButtonState = currentButtonState; if (currentButtonState == LOW) { // 按键按下事件 if (currentMillis - lastButtonPress <= DOUBLE_CLICK_TIMEOUT) { // 双击触发:切换暂停状态 systemPaused = !systemPaused; // 恢复计时:如果是从暂停恢复,重新计算任务开始时间 if (!systemPaused) { taskStartTime = currentMillis - (currentStep > 0 ? (programRunning ? resetTaskDuration[currentStep-1] : firstTaskDuration[currentStep-1]) - remainingTime : 0); } buttonPressCount = 0; } else { // 第一次按下,记录时间 buttonPressCount = 1; lastButtonPress = currentMillis; } } } } // 超时未第二次按下,重置计数 if (buttonPressCount == 1 && (currentMillis - lastButtonPress) > DOUBLE_CLICK_TIMEOUT) { buttonPressCount = 0; } } void runFirstTask() { // 移除原按键触发逻辑,统一由handleButtonPress处理 if (currentStep > 0) { unsigned long currentMillis = millis(); unsigned long elapsedTime = currentMillis - taskStartTime; unsigned long stepDuration = firstTaskDuration[currentStep - 1]; // 暂停时记录剩余时间 if (systemPaused) { remainingTime = stepDuration - elapsedTime; return; } if (elapsedTime >= stepDuration) { taskStartTime = currentMillis; switch (currentStep) { case 1: digitalWrite(one_Green, HIGH); digitalWrite(one_Yellow, LOW); break; case 2: digitalWrite(one_Yellow, HIGH); digitalWrite(one_Red, LOW); break; case 3: digitalWrite(two_Red, HIGH); digitalWrite(two_Yellow, LOW); break; case 4: digitalWrite(two_Yellow, HIGH); digitalWrite(two_Green, LOW); programRunning = true; currentStep = 0; return; } currentStep++; } } } void runResetTask() { // 移除原按键触发逻辑,统一由handleButtonPress处理 if (currentStep > 0) { unsigned long currentMillis = millis(); unsigned long elapsedTime = currentMillis - taskStartTime; unsigned long stepDuration = resetTaskDuration[currentStep - 1]; // 暂停时记录剩余时间 if (systemPaused) { remainingTime = stepDuration - elapsedTime; return; } if (elapsedTime >= stepDuration) { taskStartTime = currentMillis; switch (currentStep) { case 1: digitalWrite(two_Green, HIGH); digitalWrite(two_Yellow, LOW); break; case 2: digitalWrite(two_Yellow, HIGH); digitalWrite(two_Red, LOW); break; case 3: digitalWrite(one_Red, HIGH); digitalWrite(one_Yellow, LOW); break; case 4: digitalWrite(one_Yellow, HIGH); digitalWrite(one_Green, LOW); programRunning = false; currentStep = 0; return; } currentStep++; } } } void pulseRedLed() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; analogWrite(redLed, brightness); brightness += fadeAmount; if (brightness <= 0 || brightness >= 255) { fadeAmount = -fadeAmount; } } }
关键修改说明
- 新增暂停状态标记
systemPaused:独立于programRunning,专门控制系统是否暂停,避免任务切换逻辑干扰。 - 修复双击按键逻辑:
- 设置
DOUBLE_CLICK_TIMEOUT=300ms,判断两次按键间隔是否在超时内,符合双击操作习惯。 - 加入按键防抖处理,避免机械按键抖动导致的误触发。
- 设置
- 添加剩余时间记录
remainingTime:暂停时记录当前步骤已运行时间,恢复时重新计算任务开始时间,实现断点续行。 - 移除任务函数中的按键读取:将所有按键处理逻辑统一到
handleButtonPress,消除事件冲突。 - 优化计数重置逻辑:仅在双击超时未触发时重置计数,确保两次按键能正确累计。
内容的提问来源于stack exchange,提问作者Tiziano
相关产品推荐
相关产品推荐

