Try   HackMD

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組

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
圖中藍色的是紅外線發射 LED、透明的是紅外線接收 LED。
請注意,各廠牌的發射器及接收器顏色不一定相同,購買前最好先向店家確認。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
自製的LED支架

使用類比輸入測試

我們所用的紅外線 LED 發射及接收器需要對準才能讀到較強的訊號,因此下圖只是示意圖,請參考以下的接法但另外用較長的線將發射、接收器拉出來。我自己測試的結果,當發射、接收器對準時且距離5cm,讀數可達1000,若中間有障礙物時約為300。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
線路圖

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
實際的裝置照片

/* 實驗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); }

使用數位輸入測試

由於我想要把紅外線發射、接收器當成光電閘使用,只要能讀出兩者間是否有東西經過即可,因此需要用數位輸入測試看看。除了紅外線發射、接收器,另外再接上一顆普通的 LED ,當光電閘之間沒有障礙物時點亮 LED,有障礙物時熄滅 LED。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
線路圖

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
實際的裝置照片

/* 實驗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); }

迴圈

迴圈(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 的語法

while (條件) {
    迴圈內容......;
    通常會有遞增或遞減條件;
}

當條件成立時會執行迴圈內容,當條件不成立時結束迴圈。另一個結束迴圈的狀況則是在迴圈中遇到 break,一旦遇到 break 會立即跳出迴圈,位在迴圈內但在 break 之後的程式碼不會被執行。

do while 的語法

do {
    迴圈內容......;
    通常會有遞增或遞減條件;
} while (條件);

用法與 while 迴圈幾乎一樣,差別在於 do while 迴圈會先執行一次迴圈內容,執行完後再檢查條件是否成立,當條件成立時會再次執行迴圈內容,當條件不成立時結束迴圈。一樣可以用 break 跳出迴圈。

for 的語法

for (起始狀態; 條件; 遞增或遞減) {
    迴圈內容......;
}

for 迴圈通常會用在已知執行次數的狀況,例如 1+2+3++10 = ? 這類的計算,這會寫成

int sum = 0;

for (int i = 1; i <= 10; i++) {
    sum += i;
}

一開始設定了變數 i 並將其值指定為1,接著檢查 i 是否小於、等於10,若條件成立則執行迴圈內容,執行完後將 i 的值加1;若條件不成立則結束迴圈。在這個例子當中有一些不太符合數學運算規則的寫法

i++        i += 1        i = i + 1

這三種寫法的功能相同,將 i 的值加1後再存到 i 當中,若 i 原為1,運算後變為2。

sum += i        sum = sum + i

這兩種寫法的功能相同,將 sum 的值加上 i 的值後再存到 sum 當中,若 sum 原為45、i 原為 10,運算後 sum 變為55。for 迴圈一樣可以用 break 跳出迴圈。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
三種迴圈的流程圖

使用1組光電閘設次計時

線路圖與裝置請參考使用數位輸入測試,接法完全相同。程式碼如下:

/* 實驗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); }

程式的流程大致可分為

  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. 印出測量結果

在這個程式當中應用了一個技巧,利用 while 迴圈及 if 使程式停在某個部分,直到指定的條件發生為止。

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;
    }
}

while(1) 其實是個無窮迴圈,因為條件設定為常數1,條件一定成立,迴圈內的程式碼會不斷地被重複執行,直到 if(updateSensor(SENSOR) == true) 成立為止,因為在 if 當中最後一行會碰到 break 跳出迴圈。

第2個 while 迴圈只是單純地計算次數,直到光電閘被擋住的次數到達設定次數為止,其中 Serial.println 的部分會將次數及時間印出來,方便除錯。當第2個 while 迴圈跑完後會印出 t1、t2 及時間差 dt ,如果光電閘沒有再被擋住,程式就會又停在第1個 while 迴圈。

使用2組光電閘測量物體通過所用的時間

線路圖與裝置如下,只是發射、接收器增加為2組。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
線路圖

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
實際的裝置照片

程式碼如下:

/* 實驗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); }

程式的流程大致可分為

  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. 印出測量結果


tags:Arduino