You are on page 1of 12

普物實驗講義

Arduino 的使

目的
認識 Arduino 微處理器,並能藉由程式設計透過微處理器偵測按鈕狀態及控制 LED 及壓電揚聲
器。
原理

Arduino 的基本介紹請參閱附錄"Arduino 的介紹"講義;程式運作原理請參閱附錄。


發光二極體(LED)簡介
LED 類似於燈泡,當有電流通過時,LED 就會產生亮光。不同於燈泡的是,LED 元件有方向
性, 電流方向必須由正極流向負極,LED 才能發光。如果電流的方向不對的話,LED 將無法
正常發光。圖 1 是 LED 外型及電路符號的示意圖。

負端這一側是 平的

圖 1 LED 零件圖 ( 上 ) 和電路符號 ( 下 )


判斷極性的方法有二,一是長腳為正;另一是塑膠封 裝 平面側為負。元件符號三角 的
形 向

一個 LED 有兩個端點接腳。一端是正極,另一端是負極。當你要把一個 LED 接到線路中,要特


別注意正極和負極是否正確的接上線路。在 LED 的電路符號中,三角形的底部表示正極;負極
則是一條線通過三角形的尖端。
仔細觀察圖 1,LED 的兩端導線長度並不一樣,長的一端是接正極,短的一端是接負極。此外,
當你再仔細的觀察實際的 LED,它塑膠殼的下緣,大部分是圓弧形的,但是在短導線的那一側
則是一個小小的平滑區域,這也是為了告訴我們那一端是負極。當你手邊的 LED,兩條導線的
長短並不是很明顯的時候,這個特點也可以幫助我們分辨正負極。使用 LED 時,必須串聯一個
適當大小的限流電阻。若沒接限流電阻,一下子就會把 LED 燒壞。要判斷 LED 是否良好,我們
可以透過三用電表的二極體檔位(有 符 號 )紅色接正端,黑色接負端,只要會亮就能判斷 LED
的好壞。
按鈕簡介
圖 2 是按鈕的電路符號和零件 。按鈕中的 pin 被分成兩組,一組中的 pin 互相連接。也就是說,
導線連接到 pin1 等效於連接到 pin4,pin2 和 pin3 也是相連通的。按鈕不設計兩個 pin 是因為
它需要穩定性,如果按鈕僅有兩個 pin 可能會因為壓力過大而彎曲甚至斷裂。

1
普物實驗講義

1,4 1 1,4 1,4


2,3 4 2,3 2,3
圖 2
圖 3
按鈕 (normallyeop n) 標準開放式按鈕
電路符號 ( 左和 ) 零件圖 未壓下 ( 左 ) 和 已壓下 ( 右 )
圖 3 表示一個 normally open 的按鈕。當按鈕未被壓下,1、4 端與 2、3 端之間會有間隔(斷
路), 所以 1、4 端與 2、3 端之間沒有電流。"normally open"表示按鈕的標準狀態(未被壓下)為
斷路。當按鈕被壓下,間隔會被導線接通,稱作"closed",電流可通過按鈕(短路)。
壓電揚聲器簡介
吉他弦的振動造成空氣中的壓力改
變,這樣的改變會被耳朵偵測到。當
改變的越快,聽到的音調越高;越慢
則音調越低。壓電揚聲器中有個塑膠
材質的壓電元件,當高低訊號傳到揚
聲器的正極時,壓電元件就會振動, 符
圖 4 壓電揚聲器的電路 號與零件圖注意,它是有 性的元件
就像吉他一樣造成空氣中壓力的改 極
變。耳朵可以偵測到它所造成的壓力改變,聽起來就像嗶聲或是一個音調。它的電路圖與零件示
意圖如圖 4 所示。請注意,它是有極性的元件,接電路時別弄錯了方向。
預習問題
1. 什麼是 Arduino?請簡單說明之。
2. 何謂流程圖?
3. 透過 arduino.cc 官網查詢 random()、tone()指令的語法。
4. 說明 while()及 do..while()的語法及差別。
儀器與裝置
有安裝 Arduino 編譯程式的電腦一台、Arduino 實驗平台一個、USB 連接線一條、三用電錶一台、
LED、按鈕、壓電揚聲器各一、電阻若干。
實驗步驟
※實驗開始前,先使用電表歐姆檔檢查所有杜邦線兩端是否短路(阻值接近零),兩邊動一動短
路狀況是否保持穩定(不穩定即線內接觸不良)。將這些斷路或接觸不良的杜邦線丟一般垃
圾。
一、簡單的 LED 電路
a. 拔出 USB 連接線(即切斷 UNO 的
電源)後接妥電路圖。

電阻是必須的。請記得使用限流電阻,
如果沒有它,會造成太多的電流流過線路。
將會造成許多零件的損壞,包括 UNO、電源 圖 5 UNO 控制 LED 線路
供應裝置甚至是 LED 等都可能會燒毀。
b. 鍵入 LedOnOff.ino 程式碼至 Arduino Editor。並設定好檔名另存新檔。

2
普物實驗講義

//LedOnOff.ino
// 開啟及關 LED 燈, 0.5s; 暗 0.5s 。
閉 亮
// 組別、組 員 姓名
// 雙斜線為 解指令,編譯程式會自
註 void setup(){ 動 忽略雙斜線之後的文字 , 主要目的是幫忙理解程式碼。
// 本函數只 行一次執
Serial.begin(9600);// 設定鮑率 9600
pinMode( 13,OUTPUT); // 設定接腳 1 3 為輸出模 式
}// 程式碼最好根據不同 的 程式區塊有不同的縮排 , 可以讓程式碼容易閱讀
void loop(){
k
Serial.println("The LED connected to Pin13 is blin ing!"); 窗

//Serial.println() 函數是把雙引號內的 串輸出到序列埠監控視
digitalWrite(13,HIGH);
//digitalWrite( 接腳名,輸出狀態 ) 指令是設定某輸 出 接腳的輸出狀態,其中 HIGH 為輸出高電壓 (5V) delay(500);//dela
delay(500); 間 秒

}//loop 函數執行結束會重新執

c. 重新連接你的 UNO 的電源、執行程式;點選工具列序列埠監控視窗,檢查是否出現設定
字串。
d. 執行特定次數的 for 迴圈_程式前面鍵入"int counter;"並把 void loop()函數內的程式改用下
列程式取代。 會
e. 檢查是否亮暗十次後停止?完成後請助教檢查。
Serial.println("The LED connected to Pin13 is blinking!");
二、按鈕測試電路 n
//程式由 counter=0 開始執行,每執行 一 次,counter 就+1,直到 counter=10 就 停 止 執 行 , 離 開 for 迴圈,共執行 10
次 1 5V
a. 拔出 USB 連接線(即切斷 P13
for(counter=0;counter<
W H0;counter=counter+1){ //可用 counter++取代 counter=counter+1
UNO 的電源)後接妥電路圖
digital rite(13,HIG );
6。
delay(500);
P11
b. 鍵入rite(13,LOW);
digital PushbuttonControlLed
delay(500); 圖 6
程式碼至 Arduino Editor。 路
Serial.println(counter,DEC) 按鈕迴路和 LED 電
; 並設定好檔名另存新檔。
} 執
// PushbuttonControlLed.ino
// Check pushbutton
h state 0 times per second and bli nk LED when pressed.
//組別、組員姓名 1
const byte SW = 11; //定義常數 SW 代表 11
const byte LED = 13; // 義接腳 13 為 led 控制端
void setup() {
Serial.begin(9600);//定義鮑率
pinMode(S W, INPUT);//設定接腳 1 1 為輸入模式
pinMode(LED,OUTPUT);//設定接腳 13 為輸出模式

3
普物實驗講義

}
void loop() {
boolean val = digitalRead(SW); //將
S W 接腳狀態讀入後存入
資 料型態為布林變數的
val if(val){ //val 是否為 HIGH
Serial.println("SW=1");//是若輸出"SW=1"字串,並閃爍 LED
digital
W rite(LED,HIGH);
delay(50);
digital rite(LED,LOW);
delay(50);}
else{//否W 輸出"SW=0"字

Serial.println("SW=0");
delay(100);
} 按 的
c. 重新連接你的 UNO 的電源、執行程式;點選工具列序列埠監控視窗,檢查是否出現設定字
串及測試結果是否符合預期?完成後請助教檢查。
三、揚聲器電路 P8
a. 拔出 USB 連接線(即切斷 UNO 的電
源) 後接妥電路圖 7。
b. 鍵入 ActionTones.ino 程式碼至
Arduino Editor。並設定好檔名另存新 圖 7
壓電揚聲器回 路的電路圖
檔。

//ActionTones.ino m
//Send a tone員to the piezo speaker using the tone() co mand.
//組別、組 P姓名
const byte S
_PIN=8; int
duration;
int frequency; 模
//設定函數 S
void setup() {//接腳輸出 式的設

pinMode( P_PIN,OUTPUT);
Serial.begin(9600);
}
//主程式 0 void loop() {
Serial.println("Alarm...");
delay(1000); 音 止
tone(SP_PIN, 1500); delay(500); noTone(SP_PIN); delay(500);//響 1.5kHz 聲 0.5 秒,停 0.5
秒 tone(SP_PIN, 1500); delay(500); noTone(SP_PIN); delay(500);
p
tone(SP_PIN, 1500); delay(500); noTone(SP_PIN); delay(500);
tone(SP_PIN, 1500); delay(500); noTone(SP_PIN); delay(500);

Serial.println("Robot
replay..."); delay(100);
tone(SP_PIN, 2800); delay(100); noTone(SP_PIN);
tone(SP_PIN, 2400); delay(200); noTone(SP_PIN);
tone(SP_PIN, 4200); delay(140); noTone(SP_PIN);
tone(SP_PIN, 2000); delay(30); noTone(SP_PIN);
delay(500);

Serial.println("Hyperspace...")
; delay(100);
for(duration=15;duration>=1;duration--){//duration--代表 duration=duration-1

4
普物實驗講義

tone(SP_PIN,frequency);
delay(duration);
noTone(SP_PIN);
}
}
Serial.println("Done");
for(;;);
}
c. 重新連接你的 UNO 的電源、執行程式;點選工具列序列埠監控視窗,檢查是否出現設定字
串及測試結果是否符合預期?完成後請助教檢查。
四、設計_反應計時電路
假如你已經是遊戲公司的嵌入系統工程師,市場部門要你藉由電路來測試玩家使用遊戲把手的反應
時間,你的下一個工作是開發一個反應計時電路。這個實驗程序中,你要建立並且測試你的解決
方法,是這一個設定程式並解決問題的例子。
a. 拔出 USB 連接線(即切斷 UNO 的電源)後接妥電路圖。

5V

P8
P11
圖 8
反 應計時電路

b. 鍵入 ReactionTimer.ino 程式碼至 Arduino Editor。並設定好檔名另存新檔。


// ReactionTimer.ino
// 反應計時電路
//組別、組 姓名
//宣告變數 員 稱
int 名
timeCounter=0;
int counter=0;
boolean val=0;
const byteu Button =
11; const byte S
_PIN =8;P const byte
LED =13;
// - void setup() {
Serial.begin(9600);
pinMode(Button,INPU T );
pinMode( P_PIN,OUTPUT);
S
pinMode(LED,OUTPUT);
Serial.println("When the ringtone is over, press the button");//顯示提示字
串 Serial.println("go as fast as you can.");
}
// - void loop() {
tone(SP_PIN, 1500); delay(500);
noTone(SP_PIN);delay(500); tone(SP_PIN, 1500);
delay(500); noTone(SP_PIN);delay(500); tone(SP_PIN,
1500); delay(500); noTone(SP_PIN);delay(500);
tone(SP_PIN, 3000); delay(1000); noTone(SP_PIN);
do{ //計時函數
delay(1);
timeCo nter+
+; o

5
普物實驗講義

digitalWrite(LED,HIGH);//LED 亮,計時結
束 delay(1000); //Delay 1 second.

Serial.print("\n Your time was "); //Display time measurement.


Serial.print(timeCounter,DEC);
Serial.println(" ms.\n"); //"\n"為換行指令
Serial.println("To play again, press the button down again."); //Play again instructions.
val=0; //為什麼 val 回復為 0?
while(!val){ //等待按鈕,重啟遊
戲 val = digitalRead(Button);
}
timeCounter=0;
digitalWrite(LED,LOW);
delay(1000);
c. 重新連接你的 UNO 的電源、執行程式;點選工具列序列埠監控視窗,檢查是否出現設定
字串及測試結果是否符合預期?
修正這個設計
市場部門將你的設計交給其他遊戲測試者,完成測試後,市場部門回報你 3 個問題,你必須改良
到可以應用並裝入遊戲控制器中。
1. 當玩家持續約 33 秒後才壓住按鈕,它的成績卻出現負的秒數。
2. 玩家很快地發現,第四聲時間是 1 秒,在玩過幾次之後,他們開始很會預測時間,所以成績
不再是準確的反應時間。
3. 若玩家在聲音結束前都沒有放開按鈕,會得到不可置信的好成績(1ms),你的程式必須察覺
作弊的行為。
d. 修正上述的問題。完成後請助教檢查。
記錄
請把完成的程式加上註解 mail 給助教。並說明電路及程式工作流程。
思考問題
1.程式設計有很多不同的方式可以達到正確的結果,除了正確的結果外,還有那些方面可以努力
改進,讓程式執行上可以更
附錄_程式說明  電阻是必須的。請記得使用限流電阻,如果
LedOnOff.ino 如何運作 沒有它,會造成太多的電流流過線路。將會造成許多零件的損壞
指令 Serial.begin(9600);設定鮑率(Baud Rate)為
9600,它是一個數值告訴 UNO 傳送資料要多
快,必須與軟體的設定要一致。其中要特別注意的是,Serial 通訊會連動到 P0(RX)和 P1(TX)這
兩支接腳,因此若這兩支接腳另外用途可能會造成動作的異常。指令 Serial.println("The LED connected
to Pin13 is blinking!");會讓這一段話出現在序列
埠監控視窗(功能列/工具或 Ctrl+Shift+M)。指  程式碼的縮排是很重要的程式碼編排格式。如 void setup() 啟始
令 digitalWrite(13,HIGH);會讓 UNO 將 P13 接至
5V,讓 LED 亮起。
指令 delay(500);會讓 UNO 於 LED 開啟狀態時
的 0.5 秒(500ms)內不做任何事。數字 500 告
訴 delay 指令等待 500 毫秒。delay 括弧內的
數字我們稱為時間參數。如果你從 Arduino 網

6
普物實驗講義

頁(https://www.arduino.cc/en/Reference/ HomePage)中查詢 delay()指令,你會發現這個參數用來表


示 delay 指令停止"一段時間",且以毫秒(ms)當單位來計算。
指令 digitalWrite(13,LOW); 會讓 P13 連接至 GND。這將使 LED 關閉。因為這個指令之後還有另一
個指令 delay(500); ,所以 LED 會維持關閉 0.5 秒。
這個程式會一次又一次的重複執行是因為它是建構在 Arduino 的關鍵字(keyword)「void loop()」
內。藉由將 LED 開啟又關閉的程式碼放於 void loop()大括弧{}之間,它會告訴 UNO 重複執行大
括弧{}內的程式。這個結果會讓 LED 重複開啟和關閉的動作。它會持續的閃燈熄滅直到你將電
源移除或是壓下 Reset 按扭,再者就是等電池用盡。
執行特定次數的 for 迴圈如何運
 程式的註解:在程式碼左邊加
作這是 Arduino 的宣告
上一個 // 雙斜線,會將該行程式碼轉變為註解。當你
int counter;

告訴 Arduino Editor 你的程式會用到一個叫做 counter 的


變數用來儲存一個整數的資訊。
選擇變數的名字有許多規則
1. 名字不能是 Arduino 已經定義的字。這些字叫做關鍵
字(keyword),其實你已經接觸過一些例子如:
delay、HIGH、LOW、for 和 LOOP。
2. 名字中間不能有空格。
3. 名字只能包含英文字母、數字或是底線。但它必須要
以字母為起頭。
4. 名字不能有全形字。
5. 最好以變數所代表的意義來取名,可以降低閱讀及除錯的困難度,但應避免過長。
6. 大小寫有別,因此 led 與 LED 視為不同變數。

設定初始

 不同的變數形態,可以儲存不同的數字
範圍。當然,儲存的數字範圍越大,所需要的儲存記憶體空間
boolean : true or fasle 布林變數
byte : 0~255 位元組
不符合 char : 'a', 'b', 'c' 字元 int : -32768 ~ 32767 整數
設定執
脫離迴圈 long : -2147483648 ~ 2147483647 長整數
行條件

符合條件

主要程式

設定變數
變化量 圖 9 for() 流程圖

7
普物實驗講義

Serial.println(counter,DEC);指令中的 DEC 是一個顯示格式,告訴電腦要用十進位的方式顯示 counter


變數值在序列埠監控視窗上。
for 格式語法如下所示:
for(設定變數初始值;設定執行條件;設定變數變化量){主要程式}

for()函數內有三個參數(分別以;分號隔開),第一個參數為設定變數初始值,其中變數需事先宣
告,本例中變數名稱為 counter,設定為 0 即 counter=0。第二個參數為設定執行條件,其中只要符
合條件就繼續執行 for()函數,不符合就離開 for()函數。本例中為 counter<10,只要 counter 變數小
於 10 就執行大括弧內程式。第三個變數為設定變數變化量,只要 for()函數內的程式執行完後即重
新設定變數後再依據第二個參數作判斷是否離開迴圈。本例為 counter=counter+1 代表每次將
counter 變數內的數值取出加 1 後再儲存回 counter,即 counter 變數加 1 的意思(可簡化為 counter+
+)。因此執行 for(counter=0;counter<10;counter=counter+1)告訴 UNO 要先設定 counter 的值為 0 然後根據條
件判 斷是否要執行主要程式。若符合條件將執行程式後把 counter 的值加 1,接著再檢查 counter
的值有沒有小於 10。如果沒有,它會再重複程序。當 counter 最後到達 10 時,程式會自動跳出
for()迴圈, 並且繼續執行之後的指令。流程圖如圖 9 所示。
for(counter=0;counter<10;counter=counter+1){
digitalWrite(13,HIGH);
delay(500);
digitalWrite(13,LOW);
delay(500);
Serial.println(counter,DEC);
}

在 for()函數之後的指令為
Serial.println("All done!");
for(;;);//停止執行

這個指令只是要表示程式已經完成十次的 for()迴圈。最後它會停在 for(;;);指令上。若無此行指令


程式將會重新重頭執行。
最後我們來說明 for(;;);指令,當我們了解了 for()函數的語法後,就知道這個指令內並無第二個參
數,即設定執行的條件,因此一旦進入函數內後,就會無條件執行迴圈,不會再跳脫迴圈,因此
可以暫時充當停止程式的指令。
PushbuttonControlLed.ino 如何運作
在說明之前,我們先討論 if()指令的語法。當程式中有需要因應
不同的條件,執行不同程式時,就可以用 if-else 敘述處理,當 圖 10 if()流程圖
判斷條件成立時,則執行 if 後程式 A,判斷條件不成立,則
執行 else 後面程式 B。流程圖如圖 10 所示。if-else 語法如下
判斷條件
if( 判斷條件 )
{
程式 A; // 若判斷條件成立,則執行此部份 true false
}
else 程式 A 程式 B
{
程式 B; // 若判斷條件不成立,則執行此部份
}

8
普物實驗講義

透過 void loop(),指令 boolean val =digitalRead(SW);用 boolean 資料形態臨時宣告一個 val 變數後同時


將 SW(P11)的狀態存入 val 變數裡,再透過 if()判斷將對應字串送到序列埠監控視窗。接腳 11 的
數值表示執行 digitalRead(SW)指令時當下感應 P11 的狀態。當 if 的判斷條件是正確的(val=1),就
接著執行程式 A 敘述,執行到 else 敘述時程式會忽略程式 B 敘述而持續運作。當 if()後的條件是
錯誤時(val
= 0),就會執行 else 敘述後的程式 B。
編輯程式控制揚聲器
tone()指令是一個好用的指令用來傳送高低訊號到揚聲器產生聲音。Arduino 英文網址介紹它的語
法如下:
tone(pin, frequency, duration)
tone(pin, frequency) //參考網址:https://www.arduino.cc/en/Reference/Tone
noTone(pin)
如同書中大多數的指令,pin 是一個數值選擇使用哪一個 UNO 輸入輸出端。Duration 參數是一個
數值,用來告知 tone()指令音調必須持續幾毫秒(ms)。如同第二行程式所述,duration 也可以省
略不輸入,但若不指定時間,則 tone()將持續出聲直到執行到 noTone()為止。frequency 參數設定
音調的頻率。
下行程式碼為使用範例,顯示如何傳送音調到輸入輸出端 P9,並使它產生頻率 2kHz 的音調持續
1.5 秒。
tone(9, 2000, 1500);

動作音效
許多玩具裝有 microcontroller 以產生動作音效,揚聲器也可以產生頻率快速改變的動作音效。
你也可以使用 tone()指令中的參數,來產生許多有趣的效果。
動作與音效的使用包含三個部份:
1. delay 2.duration 3.Frequency
在音調之間的暫停,可以使用 noTone()+delay()指令來產生停頓。frequency 則決定音調的高低,
頻率越高音調就越高,頻率越低音調則越低,這是由 tone()指令中的 frequency 參數來決定
的。duration 是指讓音調持續時間,你可以使用 tone()指令中的 duration 參數做設定。但是
duration 參數是「播放時間」的說法只是網頁的說明,實際上執行 tone()之後會緊接著執行下一個
指令。所以若 tone()之後如果沒有用 delay()設定會出現無法預期的聲音。因此,通常 duration
參數不用會比較好設計特定音調。
ActionTones.ino 如何運作
"Alarm"的這組音調聽起來應該要像是電子鬧鈴,音調固定在頻率 1.5kHz 持續 0.5 秒,音調之間
間隔 0.5 秒。很像科幻電影中機器人說話聲音的"Robot replay",這組音調使用了多種的頻率,都
非常短暫。"Hyperspace"包含多種的頻率與持續時間。藉由使用 for()廻圈快速地改變頻率與持續
時間,可以產生許多有趣的音效。當一個 for()迴圈在另一個 for()迴圈中被執行,就稱為巢狀迴
圈。以下說明巢狀的 for()迴圈如何運作。duration 變數一開始為 15,frequency 迴圈開始進行並
且傳送頻率到壓電揚聲器,從頻率 2000、2020、2040 直到 2500。當 frequency 迴圈結
束,duration 迴圈重複它接下來的 15 個程序,所以把計算 duration 的 for()迴圈之數值減少 1,然
後繼續 frequency 迴圈

9
普物實驗講義

for(duration=15;duration>=1;duration--){//duration--代表 duration=duration-1
for(frequency=2000;frequency<=2500;frequency=frequency+20){
tone(SP_PIN,frequency);
delay(duration);
noTone(SP_PIN);
}
}

ReactionTimer.ino 如何運作
因為程式必須追蹤下壓前所累積的時間,所以宣告一個 timeCounter 變數。另外也宣告一個布林
變數 val 來儲存按鈕的按壓情形。最後為了提高程式碼的可讀性,同時也分定義了三個接腳名稱。
int timeCounter=0;
int counter=0;
boolean val=0;
const byte Button =
11; const byte SP_PIN
=8; const byte LED
=13;
這些字串是序列埠監控視窗給玩家的指示
Serial.println("When the ringtone is over, press the button");//顯示提示字串
Serial.println("go as fast as you can.");
程式開始執行後,首先出現幾聲音效,模擬賽車出發前的提示,提醒玩家於最後一聲結束時,必
須按下按鈕。
tone(SP_PIN, 1500); delay(500); noTone(SP_PIN);delay(500);
tone(SP_PIN, 1500); delay(500); noTone(SP_PIN);delay(500);
tone(SP_PIN, 1500); delay(500); noTone(SP_PIN);delay(500);
tone(SP_PIN, 3000); delay(1000); noTone(SP_PIN);

一旦提示音效結束,就開始計時玩家多久才按下按鍵。timeCounter 變數初始值設為 0,do_while()


函數開始重複運作,直到玩家按下按鍵(val =1)。每次運作這個迴圈,Arduino 會因為使用 delay(1);
而延遲 1ms,而它會同時將 1 加入 timeCounter 變數。
do{ //計時函數
delay(1);
timeCounter++;
val=digitalRead(Button);
}while(!val);//按下按鈕,脫離迴圈

void loop()敘述裡面再加一個 do_while()loop ,這是個巢狀結構。


void loop() { //Begin main loop.
do { }while(!val)
}

裡 面 的 do_while()函數需要特別的看看。do_while()能用一個條件,決定是否跳出迴圈,繼續執
行後面的指令(見圖 11 程式流程圖)。範例中的 do_while()會重複執行,直到按鍵壓下,即
val=1。while()函數裡的括弧裡敍述為判斷的條件,true 即執行函數內的主程式;false 即跳脫函
數。本例中,若按鍵不按,則 val=0 但 val 前有一個驚嘆號(! boolean operator)布林運算子,將
val 取 not(反相,即 true 變 false;false 變 true),因此程式會在函數內,重覆執行函數內主體程
式。直到按鍵壓下後程式才會脫離 do_while()函數。

1
普物實驗講義

while()
do while()

設定 設定
迴圈 迴圈

圖 11
迴圈 while()及 do while()程式流程圖
false
兩者的差別在於 while()先檢查
判斷 離開 主體 條件再執行迴圈主體,而 do
條件 迴圈 while()則至少執行一次迴圈主
體再檢查條件。
改變
true
條件
迴圈
主體
true false


離開
改變 條 迴圈
條件 件

離 開 do_whil()函數之後,會繼續執行之後的程式敘述。點亮 LED,提醒玩家計時結束,延遲 1
秒後再出現提示字串。
digitalWrite(LED,HIGH);//LED 亮,計時結束
delay(1000); //Delay 1 second.
序列埠監控視窗中會展現出結果
Serial.print("\n Your time was "); //Display time measurement.
Serial.print(timeCounter,DEC);
Serial.println(" ms.\n"); //"\n"為換行指令
Serial.println("To play again, press the button down again."); //Play again instructions.

結束後,有一個 while()迴圈。迴圈內程式主體只是讀取按鈕狀態。離開迴圈的條件就是按下按
鈕。目的是就等待玩家準備重啟遊戲。一旦離開迴圈會將一些變數回復預設值,關閉 LED 及延
遲 1s 後繼續往下執行程式。
val=0; //為什麼 val 要重設為 0?
while(!val){ //等待按鈕,重啟遊戲
val = digitalRead(Button);
}
timeCounter=0;
digitalWrite(LED,LOW); delay(1000);

程式的最後一個敘述是右大括弧},它讓程式回到最開始的 void loop()敘述。


換你試試看 - 修正這個設計 ReactionTimer.ino
1. 當玩家持續約 33 秒後才壓住按鈕,它的成績卻出現負的秒數。
變數 timeCounter 是以整數(int)宣告,範圍為-32768~32767。因此約 33 秒後,就超出變數設定
範圍,產生奇怪的數字。我們可以改用無號整數宣告(unsigned int),可以把範圍改為 0~65535。
這樣可以計時延長至 65 秒。這樣應該夠用了,沒有人會這樣遲鈍吧。否則就改用無號長整數(unsigned

1
普物實驗講義

long)。你可以查一下它的範圍有多大?
unsigned int timeCounter; //修正這個

除了擴大變數範圍外,另一種方式是限制最大時間。例如限制 10 秒,之後就不再計數,統一以
>10 方式呈現結果。
boolean over=0; //再宣告一個 over 變數來記錄 timeCounter 是否大於 10 的狀態。
在程式末的 timeCounter=0;之後再加上 over=0;記得每次都要先歸零,不然只有第一次正常。
在 timeCounter++;之後再加上
if(timeCounter==10000){//判斷是否計數到 10s,其中雙等號"=="是判斷是否相等的運算子
val=1; //設定將結束迴圈
over=1;//記錄已超出 10 的狀態
}

將 Serial.println(timeCounter,DEC);改為
if(over){//若超出 10s 則
Serial.print(">10000");//直接呈現>10s 結果
}
else{//否則就呈現實際結果
Serial.print(timeCounter,DEC);
}

2. 玩家很快地發現,第三聲時間是 1 秒,在玩過幾次之後,他們開始很會預測時間,所以成績不
再是準確的反應時間。
Arduino 有一個 random()指令,以下是如何使用 random( ) 指令修正程式:
a. 在程式的一開始,宣告一個新的變數 value,將它初始值設定在 0。
int timeCounter=0;
int value=0; //加入這個

b. 在第四聲 tone()指令指令之前,使用 random 指令將隨機亂數傳給 value


value=random(300); //加入這個
Serial.print( "Delay time= ");
Serial.println(1000 + value, DEC); //加入這個 random 語法 (Syntax) random(max);
c. 修正 delay()指令,將時間參數由原本的 1000ms 再加上 random 例 :value = random(300);
(300)的值 //value 變動範圍為 0-299 。 random(min, m
例 :value = random(10,20);
delay(1000 + value); //修正這個 value 變動範圍為 10-19 。
3. 若玩家在聲音結束前都沒有放開按鈕,會得到不可思議的好成績
(1ms),你的程式必須察覺作弊的行為。
我們可以改良 if…else 敘述,解決此問題。
a. 若 timeCounter 的值等於 1,顯示訊息告知玩家,不能作弊。
b. 其他(若 timeCounter 的數值大於 1),則顯示 timeCounter 的數值。
用這個方式改良你的程式,克服作弊的問題。或者你也可以用自己的方式解決。

You might also like