###### tags: `CPP` `Arduino` # 05 遞增運算與 for 迴圈--設計呼吸燈 在前一個教學中, 我們利用 `count = count + 1` 這樣的寫法來增加切換次數, 這種為變數增加數值的寫法很常用到, 因此在 C++ 中提供有一個簡便的寫法, 可以寫成 **`count += 1`**, 若是寫 `count += 3` 就等同於 `count = count + 3`。如果要加的數是 1, 還有另一種更簡單的寫法, 寫成 **`count++`**。 :::info 類似的運算有: |運算|範例|對等的結果|運算|範例|對等的運算| |---|---|---|---|---|---| |[+=](https://www.arduino.cc/reference/en/language/structure/compound-operators/compoundaddition/)| a += b | a = a + b|[-=](https://www.arduino.cc/reference/en/language/structure/compound-operators/compoundsubtraction/)| a -= b | a = a - b| |[\*=](https://www.arduino.cc/reference/en/language/structure/compound-operators/compoundmultiplication/)| a \*= b | a = a \* b|[/=](https://www.arduino.cc/reference/en/language/structure/compound-operators/compounddivision/)| a /= b | a = a / b| |[++](https://www.arduino.cc/reference/en/language/structure/compound-operators/increment/)| a++ | a = a + 1|[\-\-](https://www.arduino.cc/reference/en/language/structure/compound-operators/decrement/)| a\-\- | a = a - 1| ::: 我們可以將剛剛的程式改寫如下: ```cpp= int duration_on = 0; // 亮燈時間 int count = 0; // 計算測換次數 void setup() { pinMode(5, OUTPUT); } void loop() { if(count == 100) { duration_on = (duration_on + 1) % 101; count = 0; } digitalWrite(5, LOW); delayMicroseconds(duration_on); digitalWrite(5, HIGH); delayMicroseconds(100 - duration_on); count++; } ``` 第 17 行就改用 `++` 來遞增 `count` 變數的內容。 ## 使用 for 迴圈簡化流程 前面的範例會需要執行切換亮/熄 100 次, 對於這種有明確重複次數的執行方式, 在 C++ 中提供有 [`for`](https://www.arduino.cc/reference/en/language/structure/control-structure/for/) 可以更清楚的表達, 我們先來看相同功能, 但不同的寫法: ```cpp= int duration_on = 0; // 亮燈時間 void setup() { pinMode(5, OUTPUT); } void loop() { for(int count = 0; count < 100; count++) { digitalWrite(5, LOW); delayMicroseconds(duration_on); digitalWrite(5, HIGH); delayMicroseconds(100 - duration_on); } duration_on = (duration_on + 1) % 101; } ``` 你可以注意到幾個地方不一樣: 1. 程式開頭沒有宣告 `count` 變數。 2. 在第 8 行使用了 `for`, 它後面要跟著一對小括號, 再接著一個區塊。小括號內以**分號**區分為 3 個部分, 依序是**初始設定**、**判斷條件**、**改變動作**, 以本例來說, 就是: ``` for(int count = 0; count < 100; count++) { ------------- ----------- ------- 初始設定 判斷條件 改變動作 ... } ``` 整個 `for` 流程如下所示: ```flow init=>operation: 初始設定 int count = 0; cond=>condition: 判斷條件 count < 100 change=>operation: 改變動作 count++ block=>operation: 區塊 亮/熄 LED exit=>operation: for 之後的敘述 init->cond cond(no)->exit cond(yes, right)->block(right)->change(right)->cond ``` 3. `for` 會先執行**初始設定**, 以本例來說就是宣告並定義變數 `count` 為 0。 4. 執行**判斷條件**, 如果成立, 就執行 `for` **區塊**;否則就會跳到區塊之後執行。以本例來說就是檢查 `count` 是否小於 100?若是就執行 9~12 行, 否則就跳到第 14 行。 5. 每次執行 `for` 的區塊後會執行**改變動作**, 以本例來說就是把變數 `count` 的內容加 1。然後回到步驟 4.。 1. 根據上述, 8~13 行的 `for` 會讓 `count` 從 0 開始, 變化到 100 時跳離, 所以 `for` 區塊會執行 0~99 共 100 次, 實際的動作就是讓 9~12 行的亮/熄切換進行 100 次。 1. 第 14 行是在跳離 `for`, 也就是亮/熄切換 100 次後, 將亮燈時間增加 1us。第 15 行則是檢查並讓點亮時間超過 100us 時重置回 0us。 你可以重新上傳程式, 確認和上一個範例的結果完全相同。 :::info 像是 `for` 這樣用來重複執行動作的結構稱為**迴圈 (loop)**。 ::: ## 使用雙重迴圈再度簡化程式 了解 `for` 的用法後, 你可能已經想到點亮時間的變化也是一個從 0 到 100 的規律變化, 是不是也可以使用 `for` 來簡化呢?當然可以, 我們先來看簡化的版本: ```cpp= void setup() { pinMode(5, OUTPUT); } void loop() { for(int duration_on = 0; duration_on <= 100; duration_on++) { for(int count = 0;count < 100; count++) { digitalWrite(5, LOW); delayMicroseconds(duration_on); digitalWrite(5, HIGH); delayMicroseconds(100 - duration_on); } } } ``` 1. 原本程式開頭變數 `duration_on` 的宣告不見了。 2. 第 6 行多了一個 `for`, 讓 `duration_on` 從 0 變化到 100。 3. 比較特別的是這個 `for` 的區塊內又有一個 `for`, 也就是用來切換亮/熄的迴圈, 實際的效果就是內層的 `for` 一結束, 外層的 `for` 就會執行**改變動作**, 再執行**判斷條件**, 若成立, 就再執行內層的 `for`。 這個程式的效果還是一樣, 只是因為改用雙層迴圈, 所以程式從原本的 15 行再縮短到 14 行了。由於使用迴圈的關係, 不但程式可以變得更簡潔, 也讓『變數在某個數值區間內遞增變化』的意涵更清楚。 ## 加上反向迴圈設計呼吸燈 `for` 迴圈極具彈性, 你也可以反向控制, 像是剛剛的範例都是讓 LED 漸亮, 如果反向變化, 就可以讓 LED 漸暗, 底下就是把漸亮和漸暗組合在一起的程式: ```cpp= void setup() { pinMode(5, OUTPUT); } void loop() { for(int duration_on = 0; duration_on <= 100; duration_on++) { for(int count = 0;count < 100; count++) { digitalWrite(5, LOW); delayMicroseconds(duration_on); digitalWrite(5, HIGH); delayMicroseconds(100 - duration_on); } } for(int duration_on = 100; duration_on >= 0; duration_on--) { for(int count = 0;count < 100; count++) { digitalWrite(5, LOW); delayMicroseconds(duration_on); digitalWrite(5, HIGH); delayMicroseconds(100 - duration_on); } } } ``` 第 14~21 就和第 6~13 幾乎一樣, 唯一的不同是 `duration_on` 是從 100 開始, 判斷條件是大於等於 0, 而改變動作是讓 `duration_on` 減 1。也就是這個 `for` 是讓 LED 亮燈時間從 100us 變化到 0us, 漸漸變暗。 :::info 要特別說明的是, 在 `for` 的初始設定中所宣告的變數僅在 `for` 敘述中有效, 因此雖然上述程式在兩個 `for` 迴圈中都宣告了相同名稱的變數, 但是彼此各自獨立, 互不相干。 ::: 你現在就可以上傳程式看看效果, 由於是從不亮漸變亮, 再從最亮漸漸變暗, 如此反覆, 所以會有一種生命的韻律感, 這種效果也稱為**呼吸燈 (breathing light)**。