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

Arduino Uno:如何让事件记录循环与交通灯序列并行运行?

问题:阻塞式交通灯代码无法同时运行事件记录循环

我是Arduino新手,研究许久仍未找到明确解决方案。正在制作带按钮和行人信号灯的交通灯系统,已有按钮触发的交通灯运行序列,但需要一个持续运行的事件记录循环,用于输出信号灯状态变化及按钮按下事件。目前问题在于交通灯序列为阻塞式代码,会阻塞事件记录循环,两者无法同时工作。了解到需使用millis函数实现非阻塞代码,但不知具体如何操作。附上当前代码,其中灯序列的if语句无法正常工作:

const int red = 8;
const int yellow = 2;
const int green = 3;

const int p_red = 5;
const int p_green = 6;

const int button1 = 4; 
const int button2 = 7;

const int state1 = digitalRead(button1);
const int state2 = digitalRead(button2);

unsigned long waitTime = 500;

void setup() {
  
  pinMode(8, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, INPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, INPUT);
  Serial.begin(9600);
}

void loop() {
        // turn on the default lights (traffic green and pedestrian red)
  digitalWrite(green, HIGH);   
  digitalWrite(p_red, HIGH); 
    
  if(millis() >= waitTime)  {
    Serial.print("state: ");
    Serial.println(state1);
    //delay(1000);
  }

   if(state1 || state2 == HIGH) {
    // State 1  // ID Sequence: 3, 5, 6, 1, 5, 2, 10, 
    digitalWrite(green, HIGH);   
    digitalWrite(p_red, HIGH);
    delay(3000);
    digitalWrite(green, LOW);   
    // State 2
    digitalWrite(yellow, HIGH);
    delay(5000);
    digitalWrite(yellow, LOW);
    // State 3
    digitalWrite(red, HIGH);
    delay(6000);
    digitalWrite(p_red, LOW);
    // State 4
    digitalWrite(p_green, HIGH);
    delay(1000);
    digitalWrite(p_green, LOW);
    // State 5 - Flashing red v 5 cycles (200ms)
    for (int x=0+1; x<5; x++) {
    digitalWrite(p_red, HIGH);
    delay(200);
    digitalWrite(p_red, LOW);
    delay(200);
    } // State 6
    digitalWrite(red, HIGH);   
    digitalWrite(p_red, HIGH);
    delay(2000);
    // State 7
    digitalWrite(red, LOW);
    digitalWrite(green, HIGH);   
    digitalWrite(p_red, HIGH);
    delay(10000);
    // State 8
    digitalWrite(green, HIGH);   
    digitalWrite(p_red, HIGH);
    delay(8000);
    }
}

问题分析与解决方案

原代码核心问题

  • 按钮状态未实时更新:state1和state2在初始化时只读取一次,之后不会再获取按钮的当前状态,导致按钮触发逻辑完全失效。
  • 阻塞式delay:所有状态切换都用delay(),会让整个程序卡在当前步骤,无法同时处理事件记录、按钮检测等其他任务。
  • 默认灯状态重复执行:loop()里每次都会执行digitalWrite(green, HIGH)和digitalWrite(p_red, HIGH),会覆盖状态切换后的灯状态,导致序列无法正常运行。
  • millis()使用错误:直接用millis() >= waitTime只会触发一次,无法实现周期性的事件记录。

非阻塞式实现方案(状态机+millis)

用状态机管理交通灯的各个阶段,用millis()记录每个状态的开始时间,判断是否到达切换条件,同时在loop()中实时处理按钮检测和事件记录:

const int red = 8;
const int yellow = 2;
const int green = 3;

const int p_red = 5;
const int p_green = 6;

const int button1 = 4; 
const int button2 = 7;

// 交通灯状态枚举
enum TrafficState {
  STATE_DEFAULT,       // 默认:绿灯+行人红灯
  STATE_GREEN_TO_YELLOW, // 绿灯转黄灯
  STATE_YELLOW_TO_RED,   // 黄灯转红灯
  STATE_RED_WAIT,        // 红灯等待,切换行人绿灯
  STATE_PEDESTRIAN_GREEN,// 行人绿灯
  STATE_PEDESTRIAN_FLASH,// 行人红灯闪烁
  STATE_RED_FINAL_WAIT,  // 最后红灯等待
  STATE_RED_TO_GREEN     // 红灯转绿灯
};

TrafficState currentState = STATE_DEFAULT;
unsigned long stateStartTime = 0;
unsigned long eventLogTimer = 0;
const unsigned long LOG_INTERVAL = 500; // 事件记录间隔(ms)

// 记录上一次按钮状态,用于检测按下事件(消抖)
int lastButton1State = LOW;
int lastButton2State = LOW;

void setup() {
  pinMode(red, OUTPUT);
  pinMode(yellow, OUTPUT);
  pinMode(green, OUTPUT);
  pinMode(button1, INPUT);
  pinMode(p_red, OUTPUT);
  pinMode(p_green, OUTPUT);
  pinMode(button2, INPUT);
  Serial.begin(9600);
  
  // 初始化默认灯状态
  setDefaultLights();
}

void loop() {
  // 1. 实时检测按钮状态并记录按下事件
  checkButtons();
  
  // 2. 周期性记录当前状态
  logCurrentState();
  
  // 3. 非阻塞式处理交通灯状态切换
  handleTrafficState();
}

// 设置默认灯状态:交通绿灯+行人红灯
void setDefaultLights() {
  digitalWrite(green, HIGH);
  digitalWrite(yellow, LOW);
  digitalWrite(red, LOW);
  digitalWrite(p_green, LOW);
  digitalWrite(p_red, HIGH);
  
  // 记录状态变化
  Serial.println("[STATE] 切换到默认状态:绿灯亮,行人红灯亮");
}

// 检测按钮状态,记录按下事件(含简单消抖)
void checkButtons() {
  int currentButton1 = digitalRead(button1);
  int currentButton2 = digitalRead(button2);
  
  // 检测button1按下(上升沿)
  if (currentButton1 == HIGH && lastButton1State == LOW) {
    Serial.println("[EVENT] 按钮1被按下");
    // 如果当前是默认状态,触发切换序列
    if (currentState == STATE_DEFAULT) {
      currentState = STATE_GREEN_TO_YELLOW;
      stateStartTime = millis();
    }
  }
  
  // 检测button2按下(上升沿)
  if (currentButton2 == HIGH && lastButton2State == LOW) {
    Serial.println("[EVENT] 按钮2被按下");
    // 如果当前是默认状态,触发切换序列
    if (currentState == STATE_DEFAULT) {
      currentState = STATE_GREEN_TO_YELLOW;
      stateStartTime = millis();
    }
  }
  
  lastButton1State = currentButton1;
  lastButton2State = currentButton2;
}

// 周期性记录当前灯状态
void logCurrentState() {
  if (millis() - eventLogTimer >= LOG_INTERVAL) {
    eventLogTimer = millis();
    
    Serial.print("[LOG] 交通灯状态:红=");
    Serial.print(digitalRead(red) == HIGH ? "亮" : "灭");
    Serial.print(" 黄=");
    Serial.print(digitalRead(yellow) == HIGH ? "亮" : "灭");
    Serial.print(" 绿=");
    Serial.print(digitalRead(green) == HIGH ? "亮" : "灭");
    Serial.print(" | 行人灯:红=");
    Serial.print(digitalRead(p_red) == HIGH ? "亮" : "灭");
    Serial.print(" 绿=");
    Serial.println(digitalRead(p_green) == HIGH ? "亮" : "灭");
  }
}

// 非阻塞式处理交通灯各个状态
void handleTrafficState() {
  unsigned long currentMillis = millis();
  
  switch(currentState) {
    case STATE_DEFAULT:
      // 保持默认状态,等待按钮触发
      break;
      
    case STATE_GREEN_TO_YELLOW:
      if (currentMillis - stateStartTime >= 3000) { // 绿灯保持3秒
        digitalWrite(green, LOW);
        digitalWrite(yellow, HIGH);
        Serial.println("[STATE] 切换到黄灯亮");
        currentState = STATE_YELLOW_TO_RED;
        stateStartTime = currentMillis;
      }
      break;
      
    case STATE_YELLOW_TO_RED:
      if (currentMillis - stateStartTime >= 5000) { // 黄灯保持5秒
        digitalWrite(yellow, LOW);
        digitalWrite(red, HIGH);
        Serial.println("[STATE] 切换到红灯亮");
        currentState = STATE_RED_WAIT;
        stateStartTime = currentMillis;
      }
      break;
      
    case STATE_RED_WAIT:
      if (currentMillis - stateStartTime >= 6000) { // 红灯等待6秒后切换行人绿灯
        digitalWrite(p_red, LOW);
        digitalWrite(p_green, HIGH);
        Serial.println("[STATE] 切换到行人绿灯亮");
        currentState = STATE_PEDESTRIAN_GREEN;
        stateStartTime = currentMillis;
      }
      break;
      
    case STATE_PEDESTRIAN_GREEN:
      if (currentMillis - stateStartTime >= 1000) { // 行人绿灯保持1秒
        digitalWrite(p_green, LOW);
        Serial.println("[STATE] 行人绿灯熄灭,开始闪烁红灯");
        currentState = STATE_PEDESTRIAN_FLASH;
        stateStartTime = currentMillis;
      }
      break;
      
    case STATE_PEDESTRIAN_FLASH: {
      // 闪烁逻辑:每200ms切换一次,共5个周期(2秒)
      static unsigned long flashTimer = 0;
      static bool flashState = LOW;
      static int flashCount = 0;
      
      if (currentMillis - flashTimer >= 200) {
        flashTimer = currentMillis;
        flashState = !flashState;
        digitalWrite(p_red, flashState);
        
        if (flashState == HIGH) {
          flashCount++;
          if (flashCount >= 5) { // 完成5次闪烁
            digitalWrite(p_red, HIGH); // 最后保持红灯亮
            flashCount = 0;
            Serial.println("[STATE] 行人红灯闪烁结束,保持红灯亮");
            currentState = STATE_RED_FINAL_WAIT;
            stateStartTime = currentMillis;
          }
        }
      }
      break;
    }
      
    case STATE_RED_FINAL_WAIT:
      if (currentMillis - stateStartTime >= 2000) { // 红灯最后等待2秒
        digitalWrite(red, LOW);
        digitalWrite(green, HIGH);
        Serial.println("[STATE] 切换到绿灯亮,恢复默认状态");
        currentState = STATE_DEFAULT;
        stateStartTime = currentMillis;
      }
      break;
      
    default:
      currentState = STATE_DEFAULT;
      break;
  }
}

代码说明

  • 状态机:用enum定义所有交通灯状态,通过switch处理每个状态的切换逻辑,每个状态只判断是否到达时间阈值,不使用delay()。
  • 实时按钮检测:在loop()中每次读取按钮状态,通过对比上次状态检测上升沿,实现按钮按下事件的记录,同时避免重复触发。
  • 周期性事件记录:用millis()记录上次日志时间,每隔固定间隔输出当前灯的状态,不会被交通灯序列阻塞。
  • 非阻塞闪烁:行人红灯闪烁用静态变量记录闪烁时间和次数,避免用for+delay()阻塞程序。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 09:45:50