# Arduino 教學 10:紅外線發射接收器 > 作者:王一哲 > Ver. 1:2016/11/20 > Ver. 2:2018/3/29 ## 所需元件 1. 紅外線發射 LED 2個 2. 紅外線接收 LED 2個 3. 3mm或5mm LED 1個 4. 220Ω電阻1個(串聯普通LED) 5. 560Ω電阻2個(串聯紅外線發射LED) 6. 1MΩ電阻2個(串聯紅外線接收LED) 7. 麵包板1塊 8. Arduino Uno開發板1塊 9. 麵包板連接線數條 10. 自製的LED支架2組 </br> <img height="40%" width="40%" src="https://i.imgur.com/QRSY6ok.jpg" style="display: block; margin-left: auto; margin-right: auto;"/> <div style="text-align:center">圖中藍色的是紅外線發射 LED、透明的是紅外線接收 LED。</div> <div style="text-align:center;color:red;font-weight:bold">請注意,各廠牌的發射器及接收器顏色不一定相同,購買前最好先向店家確認。</div> </br> <img height="60%" width="60%" src="https://i.imgur.com/s0rylwr.jpg" style="display: block; margin-left: auto; margin-right: auto;"/> <div style="text-align:center">自製的LED支架</div> </br> ## 使用類比輸入測試 我們所用的紅外線 LED 發射及接收器需要對準才能讀到較強的訊號,因此下圖只是示意圖,請參考以下的接法但另外用較長的線將發射、接收器拉出來。我自己測試的結果,當發射、接收器對準時且距離5cm,讀數可達1000,若中間有障礙物時約為300。 <img height="100%" width="100%" src="https://i.imgur.com/OY19GP3.png" style="display: block; margin-left: auto; margin-right: auto;"/> <div style="text-align:center">線路圖</div> </br> <img height="100%" width="100%" src="https://i.imgur.com/Fiq5W1B.jpg" style="display: block; margin-left: auto; margin-right: auto;"/> <div style="text-align:center">實際的裝置照片</div> </br> ```c= /* 實驗7-1: 紅外線發射、接收器 * * 用類比輸入讀取,檢測讀數範圍 * * 對準時距離5cm讀數可達1000,隔絕時約300 * * Ver. 1: Sep. 16, 2016 * * Ver. 2: Mar. 20, 2018 * * 作者: 王一哲 */ #define SENSOR A0 #define BAUDRATE 9600 void setup() { pinMode(SENSOR, INPUT); Serial.begin(BAUDRATE); } void loop(){ int state = analogRead(SENSOR); Serial.println(state); delay(1000); } ``` </br> ## 使用數位輸入測試 由於我想要把紅外線發射、接收器當成光電閘使用,只要能讀出兩者間是否有東西經過即可,因此需要用數位輸入測試看看。除了紅外線發射、接收器,另外再接上一顆普通的 LED ,當光電閘之間沒有障礙物時點亮 LED,有障礙物時熄滅 LED。 <img height="100%" width="100%" src="https://i.imgur.com/beFhCzi.png" style="display: block; margin-left: auto; margin-right: auto;"/> <div style="text-align:center">線路圖</div> </br> <img height="100%" width="100%" src="https://i.imgur.com/FtgDUGm.jpg" style="display: block; margin-left: auto; margin-right: auto;"/> <img height="100%" width="100%" src="https://i.imgur.com/3UWSSVr.jpg" style="display: block; margin-left: auto; margin-right: auto;"/> <div style="text-align:center">實際的裝置照片</div> </br> ```c= /* 實驗7-2: 紅外線發射、接收器 * * 用數位輸入讀取,檢測讀數是否正常 * * Ver. 1: Sep. 16, 2016 * * Ver. 2:Mar. 20, 2018 * * 作者: 王一哲 */ #define LED 13 #define SENSOR 2 #define BAUDRATE 9600 void setup() { pinMode(LED, OUTPUT); pinMode(SENSOR, INPUT); Serial.begin(BAUDRATE); } void loop(){ int state = digitalRead(SENSOR); Serial.println(state); if(state == HIGH) { digitalWrite(LED, HIGH); } else { digitalWrite(LED, LOW); } delay(100); } ``` </br> ## 迴圈 迴圈(loop)是用來重複執行某段程式碼的工具,在C、C++、Arduino中的迴圈有while、do while、for等三種,Arduino網站對於這三種迴圈的說明如下: 1. while: https://www.arduino.cc/en/Reference/While 2. do while: https://www.arduino.cc/en/Reference/DoWhile 3. for: https://www.arduino.cc/en/Reference/For ### while 的語法 ```c while (條件) { 迴圈內容......; 通常會有遞增或遞減條件; } ``` 當條件成立時會執行迴圈內容,當條件不成立時結束迴圈。另一個結束迴圈的狀況則是在迴圈中遇到 break,一旦遇到 break 會立即跳出迴圈,位在迴圈內但在 break 之後的程式碼不會被執行。 </br> ### do while 的語法 ```c do { 迴圈內容......; 通常會有遞增或遞減條件; } while (條件); ``` 用法與 while 迴圈幾乎一樣,差別在於 do while 迴圈會先執行一次迴圈內容,執行完後再檢查條件是否成立,當條件成立時會再次執行迴圈內容,當條件不成立時結束迴圈。一樣可以用 break 跳出迴圈。 </br> ### for 的語法 ```c for (起始狀態; 條件; 遞增或遞減) { 迴圈內容......; } ``` for 迴圈通常會用在已知執行次數的狀況,例如 1+2+3+...+10 = ? 這類的計算,這會寫成 ```c int sum = 0; for (int i = 1; i <= 10; i++) { sum += i; } ``` 一開始設定了變數 i 並將其值指定為1,接著檢查 i 是否小於、等於10,若條件成立則執行迴圈內容,執行完後將 i 的值加1;若條件不成立則結束迴圈。在這個例子當中有一些不太符合數學運算規則的寫法 ```c i++ i += 1 i = i + 1 ``` 這三種寫法的功能相同,將 i 的值加1後再存到 i 當中,若 i 原為1,運算後變為2。 ```c sum += i sum = sum + i ``` 這兩種寫法的功能相同,將 sum 的值加上 i 的值後再存到 sum 當中,若 sum 原為45、i 原為 10,運算後 sum 變為55。for 迴圈一樣可以用 break 跳出迴圈。 </br> <img height="100%" width="100%" src="https://i.imgur.com/xO8AfyC.png" style="display: block; margin-left: auto; margin-right: auto;"/> <div style="text-align:center">三種迴圈的流程圖</div> </br> ## 使用1組光電閘設次計時 線路圖與裝置請參考**使用數位輸入測試**,接法完全相同。程式碼如下: ```c= /* 實驗7-3: 紅外線發射、接收器 * * 用數位輸入讀取,設次計時 * * Ver. 1: Sep. 16, 2016 * * Ver. 2: Mar. 20, 2018 * * 作者: 王一哲 */ #define LED 13 //設定LED接腳 #define SENSOR 2 //設定IR LED接收器接腳 #define BAUDRATE 9600 //設定鮑率 const int times = 21; //設定計時次數 int lastState, currentState; bool blocked = false; //更新接收器狀態函式 bool updateSensor(int pin){ currentState = digitalRead(pin); if (currentState == LOW && lastState == HIGH) { lastState = currentState; blocked = true; } else if (currentState == HIGH && lastState == LOW) { lastState = currentState; blocked = false; } else { blocked = false; } return blocked; } //使LED閃爍1下 void blinkLed(int pin) { digitalWrite(pin, HIGH); delay(50); digitalWrite(pin, LOW); delay(50); digitalWrite(pin, HIGH); } void setup() { pinMode(LED, OUTPUT); pinMode(SENSOR, INPUT); digitalWrite(LED, HIGH); Serial.begin(BAUDRATE); } void loop(){ int counts = 0; unsigned long t1, t2; lastState = digitalRead(SENSOR); //直到接收器第1次被擋住時記錄時間t1、次數+1、使LED閃爍1次 while(1){ if(updateSensor(SENSOR) == true) { t1 = millis(); counts += 1; blinkLed(LED); Serial.print("counts: "); Serial.println(counts); Serial.print("t1: "); Serial.println(t1); break; } } //當接收器被擋住時記錄時間t2、次數+1、使LED閃爍1次,到達設定的次數時停止 while(counts < times){ if(updateSensor(SENSOR) == true) { t2 = millis(); counts += 1; blinkLed(LED); Serial.print("counts: "); Serial.println(counts); Serial.print("t2: "); Serial.println(t2); } } //印出測量結果 unsigned long dt = t2 - t1; Serial.print("t1 = "); Serial.println(t1); Serial.print("t2 = "); Serial.println(t2); Serial.print("dt = "); Serial.println(dt); } ``` </br> 程式的流程大致可分為 1. 設定接腳編號、鮑率、計時次數、全域變數 2. 設定自定函式updateSensor、blinkLed 3. 在 void setup() 中設定接腳模式、開啟連接埠 4. 在 void loop() a. 設定區域變數counts、t1、t2,讀取sensorPin的狀態 b. 在第1個 while 迴圈中,直到接收器第1次被擋住時記錄時間t1、次數+1、使LED閃爍1次 c. 在第2個 while 迴圈中,當接收器被擋住時記錄時間t2、次數+1、使LED閃爍1次,到達設定的次數時停止 d. 印出測量結果 </br> 在這個程式當中應用了一個技巧,利用 while 迴圈及 if 使程式停在某個部分,直到指定的條件發生為止。 ```c while(1) { if(updateSensor(SENSOR) == true) { t1 = millis(); counts += 1; blinkLed(LED); Serial.print("counts: "); Serial.println(counts); Serial.print("t1: "); Serial.println(t1); break; } } ``` </br> while(1) 其實是個無窮迴圈,因為條件設定為常數1,條件一定成立,迴圈內的程式碼會不斷地被重複執行,直到 **if(updateSensor(SENSOR) == true) 成立為止**,因為在 if 當中最後一行會碰到 break 跳出迴圈。 第2個 while 迴圈只是單純地計算次數,直到光電閘被擋住的次數到達設定次數為止,其中 Serial.println 的部分會將次數及時間印出來,方便除錯。當第2個 while 迴圈跑完後會印出 t1、t2 及時間差 dt ,如果光電閘沒有再被擋住,程式就會又停在第1個 while 迴圈。 </br> ## 使用2組光電閘測量物體通過所用的時間 線路圖與裝置如下,只是發射、接收器增加為2組。 <img height="100%" width="100%" src="https://i.imgur.com/drINdj2.png" style="display: block; margin-left: auto; margin-right: auto;"/> <div style="text-align:center">線路圖</div> </br> <img height="100%" width="100%" src="https://i.imgur.com/dzibVv2.jpg" style="display: block; margin-left: auto; margin-right: auto;"/> <img height="100%" width="100%" src="https://i.imgur.com/XCgdpcC.jpg" style="display: block; margin-left: auto; margin-right: auto;"/> <div style="text-align:center">實際的裝置照片</div> </br> 程式碼如下: ```c= /* 實驗7-4: 紅外線發射、接收器作為光電閘 * * 用數位輸入讀取,測量通量2組光電閘的時差 * * Ver. 1: Sep. 16, 2016 * * Ver. 2: Mar. 20, 2018 * * 作者: 王一哲 */ #define LED 13 //設定LED接腳 #define START 2 //設定IR LED接收器1接腳 #define STOP 3 //設定IR LED接收器2接腳 #define BAUDRATE 9600 //設定鮑率 int lastState, currentState; bool blocked = false; //更新接收器狀態函式 bool updateSensor(int pin) { currentState = digitalRead(pin); if (currentState == LOW && lastState == HIGH) { lastState = currentState; blocked = true; } else if (currentState == HIGH && lastState == LOW) { lastState = currentState; blocked = false; } else { blocked = false; } return blocked; } //使LED閃爍1下 void blinkLed(int pin) { digitalWrite(pin, HIGH); delay(50); digitalWrite(pin, LOW); delay(50); digitalWrite(pin, HIGH); } void setup() { pinMode(LED, OUTPUT); pinMode(START, INPUT); pinMode(STOP, INPUT); digitalWrite(LED, HIGH); Serial.begin(BAUDRATE); } void loop(){ unsigned long t1, t2; lastState = digitalRead(START); while(1) { if(updateSensor(START) == true) { t1 = millis(); blinkLed(LED); break; } } lastState = digitalRead(STOP); while(1) { if(updateSensor(STOP) == true) { t2 = millis(); blinkLed(LED); break; } } unsigned long dt = t2 - t1; Serial.print("t1 = "); Serial.println(t1); Serial.print("t2 = "); Serial.println(t2); Serial.print("dt = "); Serial.println(dt); } ``` </br> 程式的流程大致可分為 1. 設定接腳編號、鮑率、全域變數 2. 設定自定函式updateSensor、blinkLed 3. 在 void setup() 中設定接腳模式、開啟連接埠 4. 在 void loop() a. 設定區域變數t1、t2 b. 讀取啟動組接腳狀態 c. 在第1個 while 迴圈中,直到啟動組第1次被擋住時記錄時間t1、使LED閃爍1次 d. 讀取停止組接腳的狀態 e. 在第2個 while 迴圈中,直到停止組第1次被擋住時記錄時間t2、使LED閃爍1次 f. 印出測量結果 </br> --- ###### tags:`Arduino`