该程序是否需要添加delay,对cpu是否全占用

16 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
原因
  1. 主循环里只有纯计算+几个 if——没有 busy-wait,没有 delay()每次循环 < 10 µs 就能跑完。
  2. millis() 读取的是单片机内部 64 位计数器读操作仅一条指令,不会等待。
  3. 当状态未满足切换条件时,代码立即退出本次循环,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
  • 想再清晰、易扩展 → 改成显式枚举状态机即可,不是必须,但推荐

 

 

 

 

 

 

 

 

全部评论

·