Ver. 1:2016/11/20
Ver. 2:2018/3/29
所需元件
- 紅外線發射 LED 2 個
- 紅外線接收 LED 2 個
- 3 mm 或 5 mm LED 1 個
- 220 Ω 電阻 1 個(串聯普通 LED)
- 560 Ω 電阻 2 個(串聯紅外線發射 LED)
- 1 MΩ 電阻 2 個(串聯紅外線接收 LED)
- 麵包板 1 塊
- Arduino Uno 開發板 1 塊
- 麵包板連接線數條
- 自製的 LED 支架 2 組
圖中藍色的是紅外線發射 LED、透明的是紅外線接收 LED。
請注意,各廠牌的發射器及接收器顏色不一定相同,購買前最好先向店家確認。
自製的LED支架
使用類比輸入測試
我們所用的紅外線 LED 發射及接收器需要對準才能讀到較強的訊號,因此下圖只是示意圖,請參考以下的接法但另外用較長的線將發射、接收器拉出來。我自己測試的結果,當發射、接收器對準時且距離 5 cm,讀數可達 1000,若中間有障礙物時約為 300。
線路圖
實際的裝置照片
/* 實驗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。
線路圖
實際的裝置照片
/* 實驗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網站對於這三種迴圈的說明如下:
- while: https://www.arduino.cc/en/Reference/While
- do while: https://www.arduino.cc/en/Reference/DoWhile
- 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 跳出迴圈。
三種迴圈的流程圖
使用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);
}
程式的流程大致可分為
- 設定接腳編號、鮑率、計時次數、全域變數
- 設定自定函式updateSensor、blinkLed
- 在 void setup() 中設定接腳模式、開啟連接埠
- 在 void loop()
- 設定區域變數counts、t1、t2,讀取sensorPin的狀態
- 在第1個 while loop 中,直到接收器第1次被擋住時記錄時間t1、次數+1、使LED閃爍1次
- 在第2個 while loop 中,當接收器被擋住時記錄時間t2、次數+1、使LED閃爍1次,到達設定的次數時停止
- 印出測量結果
在這個程式當中應用了一個技巧,利用 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組。
線路圖
實際的裝置照片
程式碼如下:
/* 實驗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);
}
程式的流程大致可分為
- 設定接腳編號、鮑率、全域變數
- 設定自定函式updateSensor、blinkLed
- 在 void setup() 中設定接腳模式、開啟連接埠
- 在 void loop()
- 設定區域變數t1、t2
- 讀取啟動組接腳狀態
- 在第1個 while 迴圈中,直到啟動組第1次被擋住時記錄時間t1、使LED閃爍1次
- 讀取停止組接腳的狀態
- 在第2個 while 迴圈中,直到停止組第1次被擋住時記錄時間t2、使LED閃爍1次
- 印出測量結果
HackMD 版本連結:https://hackmd.io/@yizhewang/S16-Ba6V4
請問第一步若讀取值有遮蔽物時並沒有差很多,有可能是什麼樣的問題呢?
回覆刪除電阻的瓦數會有差別嗎?
1. 我猜你的讀數是一直都很小吧?這可能是因為發射器和接收器沒有對準,也有可能是因為 LED 接反,最好能看到裝置的照片和讀數,這樣會比較好判斷。
回覆刪除2. 我們使用開發板上的 5V 電源,電阻器的電阻值也很高,如果計算電功率 P = V^2 / R,對 560 Ω 的電阻器而言,功率約為 0.045 W,我測試時使用 1/4 W 的電阻器,不會燒起來的。
不好意思,我問一下紅外線發射的led燈會一直亮著
回覆刪除在遮蔽的時候無法熄滅,求解有沒有led熄滅的方法
是按照文章裡的電路圖測試嗎?這裡的發射端一直保持在高電位的發射狀態,並不會熄滅。當發射、接收端兩者之間有物體擋住時,接收端無法收到紅外線,進而改變電阻值,再利用分壓電路偵測到電壓變化。
回覆刪除