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

Arduino交通灯系统:实现双击按键暂停/续行功能的技术求助

Arduino交通灯系统:双击按键暂停/继续功能修复

问题概述

搭建小型Arduino交通灯系统时,无法通过双击按键实现程序暂停,且再次按键无法从当前运行位置继续执行,修改handleButtonPress函数后仍未解决。

问题根源分析

  1. 双击逻辑错误:原代码判断两次按键间隔大于800ms才计数,与双击需要的"短时间内两次按下"逻辑完全相反,导致无法触发双击事件。
  2. 按键事件冲突:runFirstTask和runResetTask中直接读取按键状态,与handleButtonPress的按键处理逻辑冲突,干扰双击判断。
  3. 无状态保存机制:切换programRunning状态时直接重置currentStep,未记录当前步骤的剩余运行时间,无法实现断点续行。
  4. 计数重置时机错误:按键释放时立即重置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;
    }
  }
}

关键修改说明

  1. 新增暂停状态标记systemPaused:独立于programRunning,专门控制系统是否暂停,避免任务切换逻辑干扰。
  2. 修复双击按键逻辑:
    • 设置DOUBLE_CLICK_TIMEOUT=300ms,判断两次按键间隔是否在超时内,符合双击操作习惯。
    • 加入按键防抖处理,避免机械按键抖动导致的误触发。
  3. 添加剩余时间记录remainingTime:暂停时记录当前步骤已运行时间,恢复时重新计算任务开始时间,实现断点续行。
  4. 移除任务函数中的按键读取:将所有按键处理逻辑统一到handleButtonPress,消除事件冲突。
  5. 优化计数重置逻辑:仅在双击超时未触发时重置计数,确保两次按键能正确累计。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 09:10:57