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
相关产品推荐
相关产品推荐

