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

Arduino RGB循环立即停止问题求助:按下Button2无法中断当前循环

Arduino RGB灯循环立即停止问题解决方案

你的问题根源在于使用了阻塞式的delay()函数——一旦程序进入delay(500),就会完全暂停执行,直到500ms延时结束,期间即使中断触发设置了stopCycle = true,也没法立刻响应,必须等当前延时跑完才会回到loop里检查状态。

要实现按下按钮立即停止的效果,必须用非阻塞式定时替代delay(),核心思路是用millis()记录每个颜色状态的起始时间,在loop循环中不断检查时间差是否达到切换条件,同时每次循环都检查stopCycle状态,这样就能做到实时响应中断。

修改后的完整代码

// 引脚定义
const int redLed = 9;
const int greenLed = 8;
const int blueLed = 7;
const int button1 = 3;
const int button2 = 2;

// 状态变量
volatile boolean stopCycle = false; // 中断修改的变量必须加volatile
unsigned long previousMillis = 0;
const long interval = 500; // 每个颜色的亮灯时长
const long offInterval = 10; // 熄灭间隔时长
int currentColor = 0; // 0:蓝, 1:红, 2:绿, 3:熄灭间隔

void setup() {
  pinMode(redLed, OUTPUT);
  pinMode(blueLed, OUTPUT);
  pinMode(greenLed, OUTPUT);
  pinMode(button1, INPUT);
  pinMode(button2, INPUT);
  
  attachInterrupt(digitalPinToInterrupt(button1), restartCycle, CHANGE);
  attachInterrupt(digitalPinToInterrupt(button2), stopCycleImmediately, CHANGE);
  
  // 初始关灯
  turnOffAllLeds();
}

void loop() {
  // 先检查是否需要停止,优先级最高
  if (stopCycle) {
    turnOffAllLeds();
    return;
  }

  unsigned long currentMillis = millis();

  // 根据当前状态处理定时逻辑
  switch(currentColor) {
    case 0: // 亮蓝色
      if (currentMillis - previousMillis >= interval) {
        digitalWrite(blueLed, LOW);
        previousMillis = currentMillis;
        currentColor = 3; // 切换到熄灭间隔
      }
      break;
    case 1: // 亮红色
      if (currentMillis - previousMillis >= interval) {
        digitalWrite(redLed, LOW);
        previousMillis = currentMillis;
        currentColor = 3;
      }
      break;
    case 2: // 亮绿色
      if (currentMillis - previousMillis >= interval) {
        digitalWrite(greenLed, LOW);
        previousMillis = currentMillis;
        currentColor = 3;
      }
      break;
    case 3: // 熄灭间隔
      if (currentMillis - previousMillis >= offInterval) {
        previousMillis = currentMillis;
        // 切换到下一个颜色,循环回到蓝色
        currentColor = (currentColor + 1) % 3;
        // 点亮对应颜色
        switch(currentColor) {
          case 0: digitalWrite(blueLed, HIGH); break;
          case 1: digitalWrite(redLed, HIGH); break;
          case 2: digitalWrite(greenLed, HIGH); break;
        }
      }
      break;
  }
}

// 重启循环的中断函数
void restartCycle() {
  stopCycle = false;
  // 重置状态,从蓝色开始
  currentColor = 0;
  previousMillis = millis();
  turnOffAllLeds();
  digitalWrite(blueLed, HIGH);
}

// 立即停止的中断函数
void stopCycleImmediately() {
  stopCycle = true;
  turnOffAllLeds();
}

// 辅助函数:关闭所有LED
void turnOffAllLeds() {
  digitalWrite(redLed, LOW);
  digitalWrite(blueLed, LOW);
  digitalWrite(greenLed, LOW);
}

关键修改点说明

  1. volatile修饰符:stopCycle是被中断函数修改的变量,必须加volatile,防止编译器优化导致主程序读取到旧值。
  2. 非阻塞定时:用millis()记录时间,替代delay(),让程序在每个loop周期都能检查stopCycle状态,一旦触发立即停止。
  3. 状态机设计:用currentColor变量跟踪当前的灯效状态,每个状态只处理自己的定时逻辑,逻辑清晰且响应及时。
  4. 中断函数优化:在重启循环时直接重置状态,确保重新开始时从蓝色亮灯起步,避免状态混乱。

这样修改后,按下Button2时,中断会立即设置stopCycle = true,下一次loop循环就会执行关灯操作,完全不会等到当前颜色的延时结束,实现真正的“立即停止”。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 08:57:13