You are on page 1of 12

普物實驗講義

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

Arduinno的基本介介紹請參閱附附錄"Arduinno的介紹"講講義;程式式運作原理請請參閱附錄錄。
發光二極體 體(LED)簡介介
LED 類似於
於燈泡,當 當有電流通過過時,LED 就
就會產生亮
亮光。不同於
於燈泡的是,LED 元件
件有方向性,
電流方向必 必須由正極 極流向負極,
,LED 才能
能發光。如果
果電流的方方向不對的話
話,LED 將
將無法正常發

光。圖 1 是 LED 外型
型及電路符號號的示意圖圖。


負端這一側是
是平的

圖 1 LED 零件
件圖(上)和電路
路符號(下)
判斷
斷極性的方法 法有二,一是長長腳為正;另一一是塑膠封裝
裝平面側為負。
元件
件符號三角形 形的方向是指電電流由正流向 向負的方向。

一個 LED 有兩個端點 點接腳。一端 端是正極,另一端是負 負極。當你 你要把一個 L


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

需要穩定性 性,如果按 個 pin 可能會
按鈕僅有兩個 會因為壓力 力過大而彎曲曲甚至斷裂裂。

1
普物實驗講義

1,4 1 1,4 1,4


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

路狀況是
是否保持穩
穩定(不穩定
定即線內接
接觸不良)。將這些斷
斷路或接觸不
不良的杜邦線線丟一般垃

圾。
一、簡單的 的LED電路 路
a.拔出
出 USB 連接 斷 UNO 的電
接線(即切斷 電
源)後接妥電 電路圖。

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

2
普物實驗講義

//LedOnOff.inno
//開啟及關閉 閉 LED 燈,亮 亮 0.5s;暗 0.5s。
//組別、組員 員姓名
//雙斜線為註 註解指令,編譯 譯程式會自動 動忽略雙斜線之 之後的文字, ,主要目的是幫
是幫忙理解程式 式碼。
void setup(){ //本函數只執 執行一次
Serial.begiin(9600); //設定鮑率 96600
pinMode(113,OUTPUT); //設定接腳 113 為輸出模式 式
}//程式碼最好 好根據不同的 的程式區塊有不 不同的縮排,可以讓程式碼 碼容易閱讀
void loop(){
Serial.printtln("The LED
D connected to Pin13 is blinkking!");
//Serial.printlln()函數是把雙 雙引號內的字 字串輸出到序列 列埠監控視窗 窗
digitalWritte(13,HIGH);
//digitalWritee(接腳名,輸 輸出狀態)指令是 是設定某輸出 出接腳的輸出狀 狀態,其中 H
HIGH 為輸出
出高電壓(5V)
delay(500));//delay(數字))指令是指暫停 停指定的時間 間,其中數字的 的單位為毫秒
秒(ms)
digitalWritte(13,LOW);///接腳 13 輸出 出低電壓(0V)
delay(500));
} //loopp 函數執行結束 束會重新執行 行
c.重新
新連接你的 U
UNO 的電源 源、執行程程式;點選工 工具列序列埠 列埠監控視窗 窗,檢查是否否出現設定

字串
串。
d.執行 的 for 迴圈__程式前面鍵
行特定次數的 鍵入"int couunter;"並把 vvoid loop()函
函數內的程
程式改用下列

程式
式取代。
Serial.println("The LED coonnected to Pinn13 is blinkinng!");
//程式由counnter=0開始執行 行,每執行一 一次,counter就 就+1,直到ccounter=10就停
停止執行,離 離開for迴圈,共
共執行10次
for(counterr=0;counter<110;counter=counter+1){ ///可用counter+++取代counterr=counter+1
digitalW
Write(13,HIGH H);
delay(5000);
digitalW
Write(13,LOW));
delay(5000);
Serial.prrintln(counter,,DEC);
}
Serial.printtln("All done!");
for(;;);//參數
數沒有任何設
設定,因此執
執行到此函數時
時,程式就會
會卡在這裡,可
可當作停止執
執行的指令。
e.檢查 十次後停止?完成後請助
查是否亮暗十 助教檢查。
二、按鈕測 測試電路
5V
a.拔出出 USB 連接線 線(即切斷 P13
UNO
O 的電源)後 後接妥電路圖 圖
6。 P11
b.鍵入入 PushbuttonnControlLedd 圖6
程式碼碼至 Arduiino Editor。 和 LED 電路
按鈕迴路和 路
並設定定好檔名另 另存新檔。
// PushbuttonnControlLed.inno
// Check pushhbutton state 10
1 times per seecond and blinnk LED when pressed.
//組別、組員 員姓名
const byte SWW = 11; //定 定義常數 SW 代 代表 11
const byte LEED = 13; //定 定義接腳 13 為 led 控制端
void setup() {
Serial.begiin(9600);//定義義鮑率
pinMode(S SW, INPUT);///設定接腳 111 為輸入模式
pinMode(L LED,OUTPUT T);//設定接腳 腳 13 為輸出模 模式

3
普物實驗講義

}
void loop() {
boolean vaal = digitalReaad(SW); //將 S
SW 接腳狀態讀
讀入後存入資 布林變數的 val
資料型態為布
if(val){ //val 是否為 HIGH
Serial.prrintln("SW=1"");//是若輸出"SW=1"字串,並閃爍 LED
D
digitalW
Write(LED,HIG GH);
delay(500);
digitalW
Write(LED,LOW W);
delay(500);}
else{//否若 若輸出"SW=0""字串
Serial.prrintln("SW=0"");
delay(1000);
}
}//本程式設定 定為當按下按 按鈕時,LED 將以 10Hz 的頻率閃爍。

c.重新
新連接你的 U
UNO 的電源
源、執行程程式;點選工具
具列序列埠
埠監控視窗,檢查是否
否出現設定字

串及
及測試結果
果是否符合預 預期?完成後
後請助教檢查查。
三、揚聲器 器電路 P8
a.拔出出 USB 連接線 線(即切斷 UNO 的電源 源)
後接妥 妥電路圖 77。
b.鍵入入 ActionTonnes.ino 程式
式碼至 Arduino 圖7
Editoor。並設定好 好檔名另存 存新檔。 壓
壓電揚聲器回
回路的電路圖

//ActionTonees.ino
//Send a tone to the piezo speaker
s using tthe tone() com
mmand.
//組別、組員 員姓名
const byte SPP_PIN=8;
int duration;
int frequencyy;
//設定函數------------------------------
void setup() {{//接腳輸出模 模式的設定
pinMode(S SP_PIN,OUTP PUT);
Serial.begiin(9600);
}
//主程式--------------------------------
void loop() {
Serial.printtln("Alarm...");
delay(10000);
tone(SP_PIN, 1500); dellay(500); noToone(SP_PIN);; delay(500);///響1.5kHz聲音
音0.5秒,停止
止0.5秒
tone(SP_PIN, 1500); dellay(500); noToone(SP_PIN);; delay(500);
tone(SP_PIN, 1500); dellay(500); noToone(SP_PIN);; delay(500);
tone(SP_PIN, 1500); dellay(500); noToone(SP_PIN);; delay(500);

Serial.printtln("Robot repplay...");
delay(100));
tone(SP_PIN, 2800); delay(100); noTone(SP_P
PIN);
tone(SP_PIN, 2400); delay(200); noTone(SP_P
PIN);
tone(SP_PIN, 4200); delay(140); noTone(SP_P
PIN);
tone(SP_PIN, 2000); delay(30); noTone(SP_P
PIN);
delay(500));

Serial.printtln("Hyperspaace...");
delay(100));
for(duratioon=15;durationn>=1;durationn--){//duration--代表duration=duration-1
for(frequuency=2000;fr
frequency<=25500;frequencyy=frequency+220){

4
普物實驗講義

tone(S
SP_PIN,frequeency);
delay((duration);
noTonne(SP_PIN);
}
}
Serial.printtln("Done");
for(;;);
}
c.重新
新連接你的 U
UNO 的電源
源、執行程程式;點選工具
具列序列埠
埠監控視窗,檢查是否
否出現設定字

串及
及測試結果
果是否符合預 預期?完成後
後請助教檢查查。
四、設計__反應計時電 電路
假如你已經 經是遊戲公 公司的嵌入系系統工程師,市場部門要 要你藉由電電路來測試玩
玩家使用遊
遊戲把手的反

應時間,你你的下一個工 工作是開發發一個反應計 計時電路。這個實驗程程序中,你要
要建立並且
且測試你的解

決方法,是 是這一個設 設定程式並解解決問題的例 例子。
a.拔出
出 USB 連接線
線(即切斷 UNO 的電源
源)後接妥電
電路圖。

5V

P8
P11
圖8
反應
應計時電路

b.鍵入
入 ReactionTimer.ino 程
程式碼至 Ardduino Editorr。並設定好
好檔名另存
存新檔。
// ReactionTimer.ino
// 反應計時電 電路
//組別、組員 員姓名
//宣告變數名 名稱------------------------------------
int timeCounter=0;
int counter=00;
boolean val=00;
const byte Buutton = 11;
const byte SP P_PIN =8;
const byte LE ED =13;
//-----------------------------------
void setup() {
Serial.begiin(9600);
pinMode(B Button,INPUT T);
pinMode(S SP_PIN,OUTP PUT);
pinMode(L LED,OUTPUT T);
Serial.printtln("When thee ringtone is ovver, press the button");//顯示
示提示字串
Serial.printtln("go as fastt as you can."));
}
//-----------------------------------
void loop() {
tone(SP_PIN, 1500); dellay(500); noToone(SP_PIN);;delay(500);
tone(SP_PIN, 1500); dellay(500); noToone(SP_PIN);;delay(500);
tone(SP_PIN, 1500); dellay(500); noToone(SP_PIN);;delay(500);
tone(SP_PIN, 3000); dellay(1000); noT Tone(SP_PIN);
do{ //計時 時函數
delay(1));
timeCouunter++;
val=digiitalRead(Buttoon);
}while(!vaal);//按下按鈕 鈕,脫離迴圈

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)為 成許多零件的損壞,包括UNO或電源供應裝置
都會燒毀。
9600,它是一個數值告訴 UNO 傳送資料要多
快,必須與軟體的設定要一致。其中要特別注意的是,Serial 通訊會連動到 P0(RX)和 P1(TX)這
兩支接腳,因此若這兩支接腳另外用途可能會造成動作的異常。指令 Serial.println("The LED connected
to Pin13 is blinking!");會讓這一段話出現在序列
埠監控視窗(功能列/工具或 Ctrl+Shift+M)。指 程式碼的縮排是很重要的程式碼編排格式。如void
令 digitalWrite(13,HIGH);會讓 UNO 將 P13 接至 setup()啟始,而結束於右大括弧}。因此它們是同一排。
而大括弧內的程式碼則後縮排兩個空格。如此一來對應
5V,讓 LED 亮起。
關係就會很清楚,左右括弧也不容易搞錯,同時也增加
指令 delay(500);會讓 UNO 於 LED 開啟狀態時 了程式的易讀性。我們再舉例void loop()與其對應的}同
的 0.5 秒(500ms)內不做任何事。數字 500 告 排;而其它程式碼則內縮兩個空格。for()大括弧內的程式
訴 delay 指令等待 500 毫秒。delay 括弧內的 碼是屬於for內部的程式碼,因此比起for的縮排再內縮兩
數字我們稱為時間參數。如果你從 Arduino 網 格空白,共4格空白。以上縮排後的程式碼是不是比起
全部切齊的格式要來的清楚好懂!

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視為不同變數。

設定初始

不同的變數形態,可以儲存不同的數字
範圍。當然,儲存的數字範圍越大,所需要
不符合 的儲存記憶體空間也越大。因此選取適當的
設定執
行條件
脫離迴圈 變數形態是很重要的。Arduino有很多不同形
態的變數。每一種可以儲存不同範圍的數
符合條件 字。除了數字之外,尚有字元及字串的變數
形態。
boolean:true or fasle 布林變數
主要程式 byte:0~255 位元組
char: 'a', 'b', 'c' 字元
int:-32768 ~ 32767 整數
unsigned int:0 ~ 65535 無號整數
設定變數 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()函數。

10
普物實驗講義

while() do while()

設定 設定
迴圈 迴圈

圖 11
false 迴圈 while()及 do while()程式流程圖
判斷 離開 兩者的差別在於 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

11
普物實驗講義

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

12

You might also like