该程序是否需要添加delay,对cpu是否全占用
4 2025-12-04 10:37
/*
加湿器间歇工作 20 分钟
工作 3s → 休息 3s → 循环
串口波特率 9600,调试信息一目了然
*/
// ================= 硬件配置 =================
const uint8_t RELAY_PIN = 7; // 继电器控制引脚
const uint32_t WORK_MS = 3000; // 单次工作时长
const uint32_t REST_MS = 3000; // 单次休息时长
const uint32_t TOTAL_RUN_MS = 20UL * 60 * 1000; // 20 分钟
// 若继电器低电平触发,把下面两行互换
#define RELAY_ON HIGH
#define RELAY_OFF LOW
// ================= 状态变量 =================
uint32_t totalStartMs = 0; // 总计时起点
uint32_t cycleStartMs = 0; // 当前周期起点
bool flagWorking = false; // true=工作, false=休息
bool flagFinish = false; // true=20 分钟已到,永久停止
// ================= 工具:安全写继电器 ================
void relayWrite(bool on) {
digitalWrite(RELAY_PIN, on ? RELAY_ON : RELAY_OFF);
}
// ================= SETUP =================
void setup() {
Serial.begin(9600);
pinMode(RELAY_PIN, OUTPUT);
relayWrite(false); // 上电先关闭
totalStartMs = millis();
cycleStartMs = totalStartMs;
Serial.println(F("\n==== 加湿器程序启动 ===="));
Serial.print(F("总运行时间=")); Serial.print(TOTAL_RUN_MS / 1000);
Serial.println(F("s, 工作/休息=3s/3s"));
}
// ================= LOOP =================
void loop() {
if (flagFinish) return; // 20 分钟已到,永久停在这
uint32_t now = millis();
// ---------- 1. 总时间到? ----------
if (now - totalStartMs >= TOTAL_RUN_MS) {
relayWrite(false);
flagFinish = true;
Serial.println(F("[TIMEUP] 20 分钟已到,关闭并停止"));
return;
}
// ---------- 2. 周期控制 ----------
if (flagWorking) { // 当前处于“工作”状态
if (now - cycleStartMs >= WORK_MS) {
relayWrite(false);
flagWorking = false;
cycleStartMs = now;
Serial.println(F("[REST ] 工作 3s 到,进入休息"));
}
} else { // 当前处于“休息”状态
if (now - cycleStartMs >= REST_MS) {
relayWrite(true);
flagWorking = true;
cycleStartMs = now;
Serial.println(F("[WORK ] 休息 3s 到,进入工作"));
}
}
}
不需要
delay(),也不会 100 % 占 CPU。原因
-
主循环里只有纯计算+几个 if——没有 busy-wait,没有
delay(),每次循环 < 10 µs 就能跑完。 -
millis()读取的是单片机内部 64 位计数器,读操作仅一条指令,不会等待。 -
当状态未满足切换条件时,代码立即退出本次循环,CPU 实际 99 % 时间在空转(idle),功耗最低。
如果你想再“优雅”一点
可以把
loop() 最后加一行cpp
复制
void loop() {
...前面代码...
yield(); // 对于 ESP 系列会切到 Wi-Fi 任务;AVR 里就是空函数,无影响
}
或干脆保持原样——对 AVR/ESP 来说,空的 loop() 就是自动反复进入
__WFI/yield() 的低功耗状态,不会忙等。结论
-
不需要
delay; -
不会全占 CPU;
-
功耗最低、响应最快——这就是状态机+非阻塞设计的优点。
我有用到状态机吗?
严格来说:
没有显式状态机,但代码结构已经带“状态机雏形”——只是还缺一张“状态表”。
没有显式状态机,但代码结构已经带“状态机雏形”——只是还缺一张“状态表”。
你现在写的
用两个布尔变量
isRunning / isWorking 把行为切成两段:表格
复制
| isRunning | isWorking | 行为 |
|---|---|---|
| false | × | 停机 |
| true | false | 休息 3 s |
| true | true | 工作 3 s |
切换条件写在
if 里,状态隐含在变量组合里——这就是“if-else 状态机”,也叫“隐式状态机”。真正的“显式状态机”长什么样
把状态枚举出来,再用
switch(state) 跳转,以后加状态、加条件都只要改表,不用继续堆 if。cpp
复制
enum State { ST_STOP, ST_REST, ST_WORK };
State state = ST_REST;
void loop() {
uint32_t now = millis();
switch (state) {
case ST_REST:
if (now - cycleStartMs >= REST_MS) {
relayWrite(true);
cycleStartMs = now;
state = ST_WORK; // 显式迁移
}
break;
case ST_WORK:
if (now - cycleStartMs >= WORK_MS) {
relayWrite(false);
cycleStartMs = now;
state = ST_REST;
}
break;
case ST_STOP: // 20 min 到
default:
return; // 永久停
}
if (!flagFinish && now - totalStartMs >= TOTAL_RUN_MS) {
relayWrite(false);
flagFinish = true;
state = ST_STOP; // 进入终止态
}
}
一句话总结
-
你现在:隐式状态机(用布尔变量 + if),功能完全 OK。
-
想再清晰、易扩展 → 改成显式枚举状态机即可,不是必须,但推荐。
全部评论