不要用 delay
讓板子同時做兩件事。 · ⏱ 約 35 分鐘
你會學到三件事
delay 的真正代價:那段時間什麼都做不了
millis() 是「開機到現在幾毫秒」
- 「看時間,而不是等時間」,這是嵌入式最重要的一個轉念
開始之前
想同時做兩件事:一顆燈每 1 秒閃、另一顆每 0.3 秒閃。
用 delay 寫寫看,你會發現寫不出來。
那不是你不夠聰明,是 delay 這個工具做不到。
🟢 先把編輯器打開,跟著下面一起做:跟著做
一、delay 的代價
delay 這一行的期間,這支程式什麼都不能做:
🔎 這一段是試一下——打了看結果就好,不要留在程式裡。
這一行的意思不是「一秒後做下一件事」,
而是「接下來一秒,這塊板子什麼都不做」。
按鈕按了?沒讀到。感測器變了?沒看到。序列埠來了資料?漏掉了。
delay 不是「等一下」,是「停機一下」。
二、millis()
millis() 回的是「開機到現在幾毫秒」:
🔎 這一段是試一下——打了看結果就好,不要留在程式裡。
1unsigned long t = millis();
millis() 回傳「從開機到現在過了幾毫秒」。它一直在跑,不需要你等。
⚠️ 型態一定要 unsigned long。用 int 的話 32 秒就溢位了。
積木盤上它寫著「開機到現在的毫秒數」;
而下一節那行 last = millis(); 是「把 … 設成 …」。
三、看時間,而不是等時間
照著打上這一段——它一樣每秒閃一次,而中間沒有停下來:
1unsigned long last = 0;2 3void loop() {4 if (millis() - last >= 1000) {5 last = millis();6 // ← 這裡放「每秒要做一次」的事7 }8}
讀法:「現在的時間減掉上次做的時間,如果超過 1000 毫秒,就做一次。」
loop 還是每秒跑幾萬次,而那一段每秒只進去一次。
其餘幾萬次它是空跑的,所以它有空做別的事。
用 delay ███████等待███████ 做一下 ███████等待███████
用 millis ·······················做一下·····················
↑ 這些點都是空的,可以塞別的事
四、狀態
要讓燈交替,得記住「現在是亮還是暗」:
int state = LOW;
...
if (state == LOW) state = HIGH;
else state = LOW;
digitalWrite(LED_BUILTIN, state);
⚠️ state 和 last 都要宣告在 loop 外面。
宣告在裡面的話,每次 loop 重來它們都會重置——
而 loop 一秒重來幾萬次,等於它們永遠不會改變。
這是「狀態」這個概念第一次真的有必要:
有些東西必須活得比一次 loop 更久。
五、⚠️ 為什麼是 millis() - last 而不是 millis() > last + 1000
millis() 大約 49 天會轉回 0(溢位)。
寫成 millis() - last 的話,即使剛好轉回去,減法的結果還是對的
(因為 unsigned 的減法會繞回來)。寫成 > 那種就會卡死。
這種寫法叫「慣用法」:它看起來只是換個寫法,而它是對的那一個。
完成的樣子
這一課的參考做法——程式碼每一行的號碼,就印在對應的那塊積木上。
1unsigned long last = 0;2int state = LOW;3 4void setup() {5 pinMode(LED_BUILTIN, OUTPUT);6}7 8void loop() {9 if (millis() - last >= 1000) {10 last = millis();11 if (state == LOW) state = HIGH;12 else state = LOW;13 digitalWrite(LED_BUILTIN, state);14 }15}
換你了
加第二顆燈,讓它每 300 毫秒閃一次,用另一組 last2 / state2。
兩顆燈會各閃各的,互不干擾。用 delay 做不到這件事。
再回頭做第 4 課那個「按一下切換」——現在你有工具了。
到編輯器做這一題:練習:加第二顆燈,讓兩顆各閃各的
做一個
三顆燈,週期分別是 0.3 秒、0.5 秒、0.7 秒,各閃各的,互不干擾。
🔴 用 delay 做不到這一題,先試一次,體會一下卡在哪裡。
⚠️ 每一顆燈要記住自己上次換的時間,三顆燈就是三個變數。
做完想一下:如果是十顆燈呢?
到編輯器做這一題:做一個:三顆燈,三個節奏
這一課你做了什麼
- 你知道
delay 期間板子什麼都做不了
- 你用
millis() 改成「看時間」而不是「等時間」
- 你第一次為了跨越
loop 而把變數宣告在外面
如果卡住了
| 你看到 |
多半是因為 |
| 燈完全不閃 |
last / state 宣告在 loop 裡面了 |
| 大約 32 秒後行為亂掉 |
millis() 存進 int 了。要 unsigned long |
| 兩顆燈互相干擾 |
共用了同一個 last |
| 還是很卡 |
某個地方還留著 delay |
在編輯器打開這一課 →