多定时事件优化:行人红绿灯倒计时程序bug排查与代码优化
行人红绿灯倒计时功能Bug修复与代码优化
功能需求
进入倒计时阶段后需持续执行以下逻辑10秒,之后切换至其他任务:
- 红灯以亮0.8秒、灭0.8秒的频率闪烁;
- 蜂鸣器每1.6秒触发一次;
- 数码管从9开始每秒倒计时至0。
现有代码
unsigned long time_start = millis(); unsigned long time_over = millis() + 10001; unsigned long countdown_1sec = millis(); unsigned long countdown = millis(); unsigned long countdown_last = millis(); unsigned long countdown_last_1sec = millis(); int countdownNum = 9; bool atZeroRan = false; bool at800Ran = false; bool at1600Ran = false; while(countdown < time_over) { if (countdown - countdown_last == 0 && atZeroRan == false) { digitalWrite(pedR, HIGH); tone(buzzer, 973, 15); at1600Ran = false; atZeroRan = true; } if (countdown - countdown_last == 800 && at800Ran == false) { digitalWrite(pedR, LOW); atZeroRan = false; at800Ran = true; } if (countdown_1sec - countdown_last_1sec == 1000) { countdown_last_1sec = millis(); displayNumber(countdownNum); countdownNum = countdownNum - 1; } if (countdown - countdown_last == 1600 && at1600Ran == false) { countdown_last = millis(); at800Ran = false; at1600Ran = true; } countdown_1sec = millis(); countdown = millis(); } digitalWrite(pedR, HIGH);
问题现象
程序多数时候正常,但偶尔出现异常:数码管不更新倒计时、红灯不切换状态,或倒计时在5-8秒左右随机停止更新,已排除电路问题。
修复与优化方案
核心问题分析
现有代码的致命问题是精确判断时间差等于固定值,但millis()的更新是离散的,一旦程序执行有微小延迟,就会错过这个精确时间点,导致事件无法触发;同时多变量重复获取millis(),逻辑耦合度高,容易出现状态混乱。
具体优化措施
- 改用「时间差≥阈值」的判断逻辑:放弃精确匹配时间点,只要时间差达到设定值就触发事件,避免漏触发。
- 统一时间基准:循环内只获取一次当前
millis()值,避免多次调用导致的时间不一致。 - 简化状态管理:用单个布尔变量替代多个标记变量,减少逻辑冲突。
- 修正倒计时边界:确保数码管从9到0完整显示10次,对应10秒时长。
改进后的代码
unsigned long currentMillis = millis(); unsigned long phaseEndTime = currentMillis + 10000; // 10秒倒计时阶段 unsigned long lastBlinkTime = currentMillis; // 红灯闪烁/蜂鸣器基准时间 unsigned long lastCountdownTime = currentMillis; // 数码管倒计时基准时间 int countdownNum = 9; bool isRedLightOn = true; // 初始化状态 digitalWrite(pedR, HIGH); displayNumber(countdownNum); tone(buzzer, 973, 15); while (millis() < phaseEndTime) { currentMillis = millis(); // 处理红灯闪烁(0.8秒切换一次)和蜂鸣器(每1.6秒触发一次) if (currentMillis - lastBlinkTime >= 800) { isRedLightOn = !isRedLightOn; digitalWrite(pedR, isRedLightOn ? HIGH : LOW); // 红灯重新亮起时触发蜂鸣器(刚好每1.6秒一次) if (isRedLightOn) { tone(buzzer, 973, 15); } lastBlinkTime = currentMillis; } // 处理数码管每秒倒计时 if (currentMillis - lastCountdownTime >= 1000) { countdownNum--; if (countdownNum >= 0) { displayNumber(countdownNum); } lastCountdownTime = currentMillis; } } // 阶段结束后红灯保持常亮 digitalWrite(pedR, HIGH);
代码说明
- 容错性时间判断:用
>=替代==,确保即使程序有延迟,事件也能被触发,彻底解决漏触发问题。 - 统一时间变量:每次循环只获取一次
currentMillis,避免多次调用millis()产生的时间误差。 - 简化状态逻辑:用
isRedLightOn直接控制红灯状态,替代原有的多个标记变量,逻辑清晰不易出错。 - 蜂鸣器触发优化:利用红灯亮的时机(每1.6秒一次)触发蜂鸣器,无需额外计时,降低逻辑复杂度。
- 完整倒计时保证:确保
countdownNum从9递减到0,完整显示10个数字,严格对应10秒时长。
内容的提问来源于stack exchange,提问作者P. Z.
相关产品推荐
相关产品推荐

