owned this note
owned this note
Published
Linked with GitHub
# <u>10907 電資營 - 感應鬧鈴</u>
**大家手上都拿到一個實驗包,裡面主要包括一片 Arduino Uno 開發板,還有一塊麵包板上面插了許多元件**
![](https://i.imgur.com/UQyl9NB.png)
**總計大概有下面這些東西,今天我們的軟體操控實驗就是基於這些裝置**
![](https://i.imgur.com/jqX4wZa.jpg)
## 基本元件
1. 實驗麵包板
2. 按鈕開關
3. 發光二極體 LED
4. 無源蜂鳴器
5. 七段共陰極顯示器
6. 超音波模組
## 單晶片 (微控制器, MCU) ATMega 328P
在萬物聯網的時代裡,我們要透過單晶片來擷取資料並且操控這些電子設備,這個單晶片就是在 Arduino UNO 板子中央那顆小小黑色的晶片
- 8 位元
- 20MHZ (20MIPS)
- AVR, 內建快閃儲存的設計
## Arduino 開發版簡介
- Italy company
- 硬體、韌體開源設計,公開所有的設計資料,任何人或是廠商都可以基於這些資料修改或是設計新的電路
#### Uno
![](https://i.imgur.com/uzR7xVW.png)
- 單晶片
- USB 接頭
- 9V 電源接頭,降壓電路
- 時脈產生電路
- 強制重置按鍵
- 5V, 地線(GND)
- 類比輸入(Analog IN, A0~A5)
- 數位輸入/輸出(Digital, 0~13)
- 類比輸出(PWM, 3,5,6,9,10,11)
#### Mega
![](https://i.imgur.com/kqW9inF.jpg)
#### Due
![](https://i.imgur.com/Ri5XhLQ.jpg)
#### Nano
![](https://i.imgur.com/7ZsJkBj.png)
#### Pro mini
![](https://i.imgur.com/kqRYxB9.png)
### 有趣的應用
- [指尖陀螺機器人](https://www.youtube.com/embed/F-r3e8R0_eA?autoplay=1&start=374&end=420)
- [平衡機器人](https://www.youtube.com/embed/9ItEPmwfBqg?autoplay=1&start=11&end=25)
- [自動開合垃圾桶](https://www.youtube.com/embed/9ItEPmwfBqg?autoplay=1&start=95&end=117)
- [氣墊車](https://www.youtube.com/embed/9ItEPmwfBqg?autoplay=1&start=148&end=160)
- [平衡板](https://www.youtube.com/embed/9ItEPmwfBqg?autoplay=1&start=420&end=440)
- [四軸旋翼飛行器](https://www.youtube.com/embed/viSsssQZ-to?autoplay=1&start=408&end=470)
## 開發軟體
- Arduino IDE: [win10 1.8.13](https://www.arduino.cc/download_handler.php)
- [CH34x_Install_Windows_v3_4.zip](https://redmine.laas.fr/attachments/download/1257/CH34x_Install_Windows_v3_4.zip)
### 界面
![](https://i.imgur.com/TZuCPDR.png)
### 兩個重要按鈕:編譯(驗證)&上傳
![](https://i.imgur.com/Z8f1YDG.png)
## 簡單測試 Blink
### <u>實驗步驟:</u>
<u>
1. <b>開啟 Arduino IDE</b>
![](https://i.imgur.com/8D9051X.png)
2. **將 Arduino Uno 用 USB 連接線連上電腦**
![](https://i.imgur.com/IZjM8SY.png)
3. **選擇 工具/開發板/Arduino Uno**
![](https://i.imgur.com/VBGdADx.png)
4. **選擇 工具/序列埠/COM<font color=red><b>??</b></font>**
![](https://i.imgur.com/nJSMgjS.png)
5. **選取 檔案/範例/Basic/Blink**
![](https://i.imgur.com/1Pb0cce.png)
6. **驗證 (編譯)**
![](https://i.imgur.com/b7afuMr.png)
8. **上傳**
![](https://i.imgur.com/FpXLZlK.png)
**上傳有點慢,需要一點點時間,如果失敗的話,調整一下 COM??,如果設定都是對的,別擔心,有時候重新上傳一次就會成功了**
![](https://i.imgur.com/YaDwF5G.png)
9. **板子上除了有一個恆亮的 LED 之外,另一個 LED 一明一滅的,一秒鐘亮,一秒鐘暗,恭喜你成功了!! 整個程序到這裡如果一直出現問題,先找隊輔幫忙解決**</u>
```C=
void setup() { //這裡面要放最一開始執行的程式碼
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() { //此處程式碼會不斷地執行
digitalWrite(LED_BUILTIN, HIGH); // 點亮 LED(HIGH就是亮)
delay(1000); //讓它維持亮1秒(=1000毫秒)
digitalWrite(LED_BUILTIN, LOW); // LED暗了(LOW就是暗)
delay(1000); //讓它維持暗著1秒(=1000毫秒)
}
```
## 電路檢查
接下來很快地檢查麵包板上的電路,請先將超音波模組拔下,看到下面這張照片,從左邊開始很快地核對這些接線,最後再將超音波模組插回圖裡面紅色方格:
![](https://i.imgur.com/UUNfbBJ.png)
## 實驗麵包板
如下圖麵包板是不需要經由焊接過程,就可以將實驗所使用的電子元件以杜邦線連接,進而量測電路的特性,以驗證電路功能是否正常的實驗室工具。
![](https://i.imgur.com/4Iw1taL.png)
---
<font size=+1>接下來所有的實驗,我們都**不需要**把麵包板上的元件拔下來,只需要用杜邦線連接 Arduino Uno 和 麵包板上的元件。</font>
## 實驗 1: 按鈕開關與LED
### <u>實驗步驟:</u>
1. 拔掉 USB 與電腦的連線
1. 連接5V (下圖中紅線,由麵包板火線+接到UNO 5V)
2. 連接地線 (下圖中黑線,由麵包板地線-接到UNO GND)
3. 連接 LED 正極(麵包板12) 到 UNO輸出 13 (下圖中黃線)
4. 連接 按鈕開關(麵包板30) 到 UNO類比輸入 A0 (下圖中綠線)
![](https://i.imgur.com/I41cmYE.jpg)
5. 插上 USB 與電腦連線
6. IDE 中選取 檔案/新增,複製下列程式到視窗中
7. 點選 上傳 按鈕,存檔,上傳完畢
8. 程式已經開始執行,按下按鍵 LED 點亮,放開按鍵 LED 熄滅
```C=
void setup() {
pinMode(A0, INPUT);
pinMode(13, OUTPUT);
}
void loop() {
int switchStatus;
switchStatus = digitalRead(A0); // 讀取按鍵電壓
if (switchStatus==HIGH) // 按鍵被按下
digitalWrite(13, HIGH); // LED 點亮
else
digitalWrite(13, LOW);
}
```
### 當你按下按鍵,發生了什麼事?
#### UNO 輸入輸出腳位控制
- pinMode(設定腳位, 設定模式);
- pinMode(A0, INPUT); //腳位是A0,模式是輸入
- pinMode(13, OUTPUT);//腳位是13,模式是輸出
#### 認識 if 敘述
```
if (switchStatus==HIGH) // 如果讀取的按鍵狀態是高電位,按鍵被按下
digitalWrite(13, HIGH); // 點亮 LED
```
#### 想想看
怎樣修改程式功能變成「**按鍵放開時 LED 長亮,按鍵按下時 LED 熄滅**」
:::spoiler 程式碼
```C=
void setup() {
pinMode(A0, INPUT);
pinMode(13, OUTPUT);
}
void loop() {
int switchStatus;
switchStatus = digitalRead(A0);
if (switchStatus==HIGH)
digitalWrite(13, LOW);
else
digitalWrite(13, HIGH);
}
```
:::
### 資工領域特別強調軟體的操控模型
- BLINK 程式裡一個 LED 可以閃動,用 delay 來控制明亮兩種狀態的時間長度
- 兩個 LED 的話,當然可以同時用同樣頻率閃動;或是第一個先閃動,第二個接續閃動,這時候雖然可以用不同的頻率,但是第二個計時的時候,第一個的時間好像就凍結在那裡了,兩種都不是令人滿意的效果
- 想想看, <font color=red>怎樣可以讓兩個 LED **同時**用不同的頻率閃動?</font>
### <u>實驗步驟:</u>
1. 拔掉 USB 與電腦的連線
1. 將 綠色 LED 接到 UNO 輸出 12
2. 將 透明 LED 接到 UNO 輸出 11
5. 插上 USB 與電腦連線
6. IDE 中選取 檔案/新增,複製下列程式到視窗中
7. 點選 上傳 按鈕,存檔,上傳完畢
8. 程式已經開始執行,看到三個 LED 以相同頻率閃動,這樣子效果太瞎了
```CPP=
const int LEDYellow = 13;
const int LEDGreen = 12;
const int LEDRed = 11;
void setup() {
pinMode(LEDYellow, OUTPUT);
pinMode(LEDGreen, OUTPUT);
pinMode(LEDRed, OUTPUT);
}
void loop() {
digitalWrite(LEDYellow, HIGH);
digitalWrite(LEDGreen, HIGH);
digitalWrite(LEDRed, HIGH);
delay(1000);
digitalWrite(LEDYellow, LOW);
digitalWrite(LEDGreen, LOW);
digitalWrite(LEDRed, LOW);
delay(1000);
}
```
9. IDE 中選取 檔案/新增,複製下列程式到視窗中
10. 點選 上傳 按鈕,存檔,上傳完畢
11. 程式已經開始執行,看到三個 LED 以相同頻率閃動,這樣子效果更差
```CPP=
const int LEDYellow = 13;
const int LEDGreen = 12;
const int LEDRed = 11;
void setup() {
pinMode(LEDYellow, OUTPUT);
pinMode(LEDGreen, OUTPUT);
pinMode(LEDRed, OUTPUT);
}
void loop() {
digitalWrite(LEDYellow, HIGH);
delay(1000);
digitalWrite(LEDYellow, LOW);
delay(1000);
digitalWrite(LEDGreen, HIGH);
delay(1000);
digitalWrite(LEDGreen, LOW);
delay(1000);
digitalWrite(LEDRed, HIGH);
delay(1000);
digitalWrite(LEDRed, LOW);
delay(1000);
}
```
12. IDE 中選取 檔案/新增,複製下列程式到視窗中
13. 點選 上傳 按鈕,存檔,上傳完畢
14. 程式已經開始執行,**三個 LED 同時以不同頻率計時閃動**
```CPP=
const int LEDYellow = 13;
const int LEDGreen = 12;
const int LEDRed = 11;
void blinkingLED() {
int i;
const int duration[3] = {700, 351, 571},
onDuration[3]={345, 147, 367};
const int outputPin[3] = {LEDRed, LEDYellow, LEDGreen};
static unsigned long lastOnTime[3]={0,0,0};
unsigned long currentTime = millis();
for (i=0; i<3; i++)
if((currentTime - lastOnTime[i]) > duration[i]) {
digitalWrite(outputPin[i], HIGH);
lastOnTime[i] = currentTime;
}
else if ((currentTime - lastOnTime[i]) > onDuration[i]) {
digitalWrite(outputPin[i], LOW);
}
}
void setup() {
pinMode(LEDRed, OUTPUT);
pinMode(LEDYellow, OUTPUT);
pinMode(LEDGreen, OUTPUT);
}
void loop() {
blinkingLED();
delay(10);
}
```
:::spoiler 解釋與心得
關鍵在於「**不要再用 delay() 這種佔著資源卻躺著什麼事都不做的函式了**」,用 millis() 去檢查現在的時間,**時間到了該做什麼事就做什麼事**,像這個程式這樣子,單晶片雖然任何時間點只能做單一一件事情,但是我們運用程式設計的不同模型,讓人以為好多件工作同時進行中。
:::
---
## 實驗 2: 蜂鳴器
### <u>實驗步驟:</u>
1. 拔掉 USB 與電腦的連線
2. 拔掉 黃色 LED(麵包板11) 接到 UNO 輸出 13
3. 拔掉 綠色 LED(麵包板12) 接到 UNO 輸出 12
4. 拔掉 透明 LED(麵包板13) 接到 UNO 輸出 11
5. 把 蜂鳴器一端(麵包板22) 接到 UNO 輸出 13
6. 插上 USB 與電腦連線
6. IDE 中選取 檔案/新增,複製下列程式到視窗中
7. 點選 上傳 按鈕,存檔,上傳完畢
8. 程式已經開始執行,蜂鳴器長響三次後靜音
```C=
const int buzzer = 13;
int j=0;
void setup() {
pinMode(buzzer,OUTPUT);
}
void loop() {
int i;
for (i=0; i<10&&j<3; i++) {
tone(buzzer,1000);
delay(50);
tone(buzzer,500);
delay(50);
}
noTone(buzzer);
delay(2000);
j++;
}
```
### 無源蜂鳴器
無源蜂鳴器的優點是:
1. 便宜
2. 聲音頻率可控,可以做出音階 "Do Re Mi Fa Sol La Si" 的效果
3. 在一些特例中,可以和 LED 共用一個控制腳
### 常用操控蜂鳴器函式
**tone(腳位,頻率)**
ex: tone(13,1000); //透過13腳輸出頻率1000的聲音
**noTone(腳位)**
ex: noTone(13); //13腳無聲
```
int i;
for (i=0; i<10; i++) { (執行10次)
想要重複執行的程式敘述
}
```
### 音階
```
66, 131, 262, (523), 1046 // C Do
74, 147, 294, (587), 1175 // D Re
83, 165, 330, (659), 1318 // E Mi
88, 175, 349, (698), 1397 // F Fa
98, 196, 392, (784), 1568 // G So
110, 220, 440, (880), 1760 // A La
124, 247, 494, (988), 1976 // B Si
```
### 來個 Do
```
tone(13,523);
delay(50)
```
### Fa 呢? So 呢?
:::spoiler 程式片段
```
Fa:
tone(13,523);
delay(50);
So:
tone(13,523);
delay(50);
```
:::
### 寫一小段旋律吧
```C=
const int buzzer = 13;
int j = 0;
void setup() {
pinMode(buzzer,OUTPUT);
}
void loop() {
int i;
for (i=0; i<3&&j<2; i++) {
tone(buzzer,784); delay(333);
noTone(buzzer); delay(10);
tone(buzzer,659); delay(333);
noTone(buzzer); delay(10);
tone(buzzer,659); delay(333);
noTone(buzzer); delay(150);
tone(buzzer,698); delay(333);
noTone(buzzer); delay(10);
tone(buzzer,587); delay(333);
noTone(buzzer); delay(10);
tone(buzzer,587); delay(333);
noTone(buzzer); delay(150);
tone(buzzer,523); delay(333);
noTone(buzzer); delay(10);
tone(buzzer,587); delay(333);
noTone(buzzer); delay(10);
tone(buzzer,659); delay(333);
noTone(buzzer); delay(10);
tone(buzzer,698); delay(333);
noTone(buzzer); delay(10);
tone(buzzer,784); delay(333);
noTone(buzzer); delay(10);
tone(buzzer,784); delay(333);
noTone(buzzer); delay(10);
tone(buzzer,784); delay(333);
noTone(buzzer); delay(333);
}
noTone(buzzer);
delay(2000);
j++;
}
```
### 額、再一小段旋律???
:::spoiler 小祕密
哈哈,哪是祕密呀!!
[Google 大神](https://www.google.com/search?sxsrf=ALeKk0057682SUtW1NI60mObTI4lKAeg9Q%3A1594463310147&source=hp&ei=TpQJX8qTBvKUr7wPipK2-A4&q=arduino+buzzer+songs&oq=&gs_lcp=CgZwc3ktYWIQARgAMgcIIxDqAhAnMgcIIxDqAhAnMgcIIxDqAhAnMgcIIxDqAhAnMgcIIxDqAhAnMgcIIxDqAhAnMgcIIxDqAhAnMgcIIxDqAhAnMgcIIxDqAhAnMgcIIxDqAhAnUABYAGCDR2gBcAB4AIABAIgBAJIBAJgBAKoBB2d3cy13aXqwAQo&sclient=psy-ab)
<https://dragaosemchama.com/en/2019/02/songs-for-arduino/>
<https://github.com/robsoncouto/arduino-songs>
:::
---
## 實驗 3: 七段顯示器
### <u>實驗步驟:</u>
1. 拔掉 USB 與電腦的連線
2. 參考下圖,接上共陰極七段顯示器的排線到 UNO 輸出 2,3,4,5,6,7,8 腳
6. 插上 USB 與電腦連線
6. IDE 中選取 檔案/新增,複製下列程式到視窗中
7. 點選 上傳 按鈕,存檔,上傳完畢
9. 程式已經開始執行,七段顯示器顯示 9,8,7,6,5,4,3,2,1,0 休息四秒鐘重複
![](https://i.imgur.com/K6asgFz.png)
```C=
byte seven_seg_digits[10][7] = { { 1,1,1,1,1,1,0 }, // = 0
{ 0,1,1,0,0,0,0 }, // = 1
{ 1,1,0,1,1,0,1 }, // = 2
{ 1,1,1,1,0,0,1 }, // = 3
{ 0,1,1,0,0,1,1 }, // = 4
{ 1,0,1,1,0,1,1 }, // = 5
{ 1,0,1,1,1,1,1 }, // = 6
{ 1,1,1,0,0,0,0 }, // = 7
{ 1,1,1,1,1,1,1 }, // = 8
{ 1,1,1,0,0,1,1 } // = 9
};
void setup() {
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
digitalWrite(9, 0); // 關閉小數點
}
// 在七段顯示器上顯示指定的一個數字
void sevenSegWrite(byte digit) {
byte seg;
for (seg=0; seg<7; ++seg)
digitalWrite(seg+2, seven_seg_digits[digit][seg]);
}
void loop() {
byte digit;
for (digit=10; digit>0; --digit) {
sevenSegWrite(digit-1);
delay(1000);
}
delay(4000); // 暫停 4 秒鐘
}
```
### 七段顯示器
實際上只是 8 顆 LED 排列整齊
![](https://i.imgur.com/X5aKcvB.png)
### 重要控制函式
**sevenSegWrite(digit)**
例如:sevenSegWrite(8); // 顯示數字 8
### 陣列
**想要10個變數?**
byte x1, x2, x3, x4, x5, x6, x7, x8, x9, x10;
**你該試試「陣列」**
byte x[10]; // 一行搞定
**陣列有什麼功能特別強大嗎?**
```
void sevenSegWrite(byte digit) {
byte seg;
for (seg=0; seg<7; ++seg)
digitalWrite(seg+2, seven_seg_digits[digit][seg]);
}
```
**沒有陣列時**
![](https://i.imgur.com/ZQ5919c.png)
### 現在讓七段往上數
:::spoiler 程式片段
```
for (byte digit=0; digit<10; ++digit) {
sevenSegWrite(digit);
delay(1000);
}
```
:::
### 現在讓七段只數奇數
:::spoiler 程式片段
```
for (byte digit=10; digit>0; digit=digit-2) {
sevenSegWrite(digit-1);
delay(1000);
}
```
:::
---
## 實驗 4: 超音波感測器
#### 超音波感測模組 HC-SR04 操作原理
![](https://i.imgur.com/Qn1vkN8.png)
上面是發射器 (Transmitter),會發出 40 kHz 的聲波,由於這個聲波的頻率超過人類可聽見的 20 kHz,因此被稱為超音波。下面是接收器 (Receiver),可以接收超音波。它可以感測的距離為 2cm 到 400cm,感應角度為 15 度。
![](https://i.imgur.com/kLzbrfs.png)
如上圖,Trig 腳送進至少維持 **10 微秒**以上的高位準訊號,便能觸發模組中的超音波發射器送出 8 個連續的 40KHz 超音波脈衝,接收器收到反射波後便會在 Echo 腳輸出一個與量測距離成正比的高位準脈衝,此高位準脈衝上緣可以看成超音波開始發射時間;而下緣則是接收到反射波的時間,所以整個**高位準脈衝的寬度**就是超音波往返的總時間,要特別注意的地方是被測物體最好大於 0.5 平方公尺,否則反射的聲音沒有辦法接收到,而 Trig 時間間隔最好大於 60ms,避免 Trig 與 Echo 互相干擾。
#### 量測物體距離
### <u>實驗步驟:</u>
1. 拔掉 USB 與電腦的連線
3. 把超音波模組的 Echo(麵包板21) 接到 UNO 輸出 11
4. 把超音波模組的 Trig(麵包板20) 接到 UNO 輸出 12
6. 插上 USB 與電腦連線
6. IDE 中選取 檔案/新增,複製下列程式到視窗中
7. 點選 上傳 按鈕,存檔,上傳完畢
8. 程式已經開始執行,如下圖在 IDE 右上角點選序列埠監控視窗
9. 試試看拿一本書放在超音波感測器前方,看到監控視窗中的距離了嗎?
![](https://i.imgur.com/HF9uUvF.png)
``` C=
int trigPin = 12; // 超音波 Trig Pin
int echoPin = 11; // 超音波 Echo Pin
long duration, cm, inches;
void setup() {
Serial.begin(9600); // 設定序列埠監控視窗傳輸速率
pinMode(trigPin, OUTPUT); // 定義輸出腳位
pinMode(echoPin, INPUT); // 定義輸入腳位
}
void loop() {
digitalWrite(trigPin, LOW); // 輸出 Trig 低電位,持續 5 微秒
delayMicroseconds(5);
digitalWrite(trigPin, HIGH); // 輸出 Trig 高電位,持續 10 微秒
delayMicroseconds(10);
digitalWrite(trigPin, LOW); // 回復 Trig 低電位
pinMode(echoPin, INPUT); // 讀取 echo 的電位
duration = pulseIn(echoPin, HIGH); // 收到高電位時的時間
cm = (duration/2) / 29.1; // 將時間換算成距離 cm 或 inch
inches = (duration/2) / 74;
Serial.print("Distance : ");
Serial.print(inches);
Serial.print("in, ");
Serial.print(cm);
Serial.print("cm");
Serial.println();
delay(250);
}
```
#### 倒車雷達
試試看常用到的倒車雷達,越接近時蜂鳴器越急促
```C=
int buzzer = 13; // 蜂鳴器
int trigPin = 12; // 超音波 Trig Pin
int echoPin = 11; // 超音波 Echo Pin
long duration, cm, inches;
void setup() {
Serial.begin(9600); // 設定序列埠監控視窗傳輸速率
pinMode(echoPin, INPUT); // 定義輸入腳位
pinMode(trigPin, OUTPUT); // 定義輸出腳位
pinMode(buzzer, OUTPUT);
}
void loop() {
digitalWrite(trigPin, LOW); // 輸出 Trig 低電位,持續 5 微秒
delayMicroseconds(5);
digitalWrite(trigPin, HIGH); // 輸出 Trig 高電位,持續 10 微秒
delayMicroseconds(10);
digitalWrite(trigPin, LOW); // 回復 Trig 低電位
pinMode(echoPin, INPUT); // 讀取 echo 的電位
duration = pulseIn(echoPin, HIGH); // 收到高電位時的時間
cm = (duration/2) / 29.1; // 將時間換算成距離 cm 或 inch
inches = (duration/2) / 74;
Serial.print("Distance : ");
Serial.print(inches);
Serial.print("in, ");
Serial.print(cm);
Serial.println("cm");
tone(buzzer,500);
delay(200);
noTone(buzzer);
delay(cm*30);
}
```
#### 距離控制音階
讓我們用距離來控制蜂鳴器的頻率
``` C=
int buzzer = 13; // 蜂鳴器
int trigPin = 12; // 超音波 Trig
int echoPin = 11; // 超音波 Echo
long duration, cm, inches, step;
int freq[] = {523, 587, 659, 698, 784, 880, 988};
void setup() {
Serial.begin(9600); // 設定序列埠監控視窗傳輸速率
pinMode(echoPin, INPUT); // 定義輸入腳位
pinMode(trigPin, OUTPUT); // 定義輸出腳位
pinMode(buzzer, OUTPUT);
}
void loop() {
digitalWrite(trigPin, LOW); // 輸出 Trig 低電位,持續 5 微秒
delayMicroseconds(5);
digitalWrite(trigPin, HIGH); // 輸出 Trig 高電位,持續 10 微秒
delayMicroseconds(10);
digitalWrite(trigPin, LOW); // 回復 Trig 低電位
pinMode(echoPin, INPUT); // 讀取 echo 的電位
duration = pulseIn(echoPin, HIGH); // 收到高電位時的時間
cm = (duration/2) / 29.1; // 將時間換算成距離 cm 或 inch
inches = (duration/2) / 74;
Serial.print("Distance : ");
Serial.print(inches);
Serial.print("in, ");
Serial.print(cm);
Serial.println("cm");
step = 10;
for (int i=0; i<7; i++) {
if (cm/step==i)
tone(buzzer, freq[i]);
delay(250);
}
```
## 綜合實驗: 感應鬧鈴
這個實驗裡,你可以**按著按鍵來增加 LED 顯示的數字**,然後只要在超音波感測器前揮揮手,可以**開始倒數計時或是停下蜂鳴器的聲音**,試看看吧!
```C=
// buttons
// A0: increase the LED count
// A1: start the counting down / break the music
// note: cannot pause the counting down (single tasking)
// aa
// f b
// gg
// e c
// dd
// a b c d e f g
byte seven_seg_digits[10][7] = { { 1,1,1,1,1,1,0 }, // = 0
{ 0,1,1,0,0,0,0 }, // = 1
{ 1,1,0,1,1,0,1 }, // = 2
{ 1,1,1,1,0,0,1 }, // = 3
{ 0,1,1,0,0,1,1 }, // = 4
{ 1,0,1,1,0,1,1 }, // = 5
{ 1,0,1,1,1,1,1 }, // = 6
{ 1,1,1,0,0,0,0 }, // = 7
{ 1,1,1,1,1,1,1 }, // = 8
{ 1,1,1,0,0,1,1 } // = 9
};
const int buzzer = 13;
const int sevenSegA = 2; // seg a
int trigPin = 12; //Trig Pin
int echoPin = 11; //Echo Pin
long duration, cm, inches;
#define DEBOUNCE_DELAY 200
#define TEMPO 333
const int toneTable[7][5]={ // 5 octaves
{ 66, 131, 262, 523, 1046}, // C Do
{ 74, 147, 294, 587, 1175}, // D Re
{ 83, 165, 330, 659, 1318}, // E Mi
{ 88, 175, 349, 698, 1397}, // F Fa
{ 98, 196, 392, 784, 1568}, // G So
{110, 220, 440, 880, 1760}, // A La
{124, 247, 494, 988, 1976} // B Si
};
char beeTone[]="GEEFDDCDEFGGGGEEFDDCEGGEDDDDDEFEEEEEFGGEEFDDCEGGC";
char starTone[]="CCGGAAGFFEEDDCGGFFEEDGGFFEEDCCGGAAGFFEEDDC";
int beeBeat[]={
1,1,2, 1,1,2, 1,1,1,1,1,1,2,
1,1,2, 1,1,2, 1,1,1,1,4,
1,1,1,1,1,1,2, 1,1,1,1,1,1,2,
1,1,2, 1,1,2, 1,1,1,1,4
};
int starBeat[]={
1,1,1,1,1,1,2, 1,1,1,1,1,1,2,
1,1,1,1,1,1,2, 1,1,1,1,1,1,2,
1,1,1,1,1,1,2, 1,1,1,1,1,1,2
};
// (symbol-'C'+7)%7
int getTone(char symbol) {
static const char *toneName="CDEFGAB";
for (int i=0; i<7; i++) {
if (toneName[i]==symbol) {
return i;
}
}
return 0;
}
void measuringDistance(){
digitalWrite(trigPin, LOW);
delayMicroseconds(5);
digitalWrite(trigPin, HIGH); // 給 Trig 高電位,持續 10微秒
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
pinMode(echoPin, INPUT); // 讀取 echo 的電位
duration = pulseIn(echoPin, HIGH); // 收到高電位時的時間
cm = (duration/2) / 29.1; // 將時間換算成距離 cm 或 inch
inches = (duration/2) / 74;
Serial.print("Distance : ");
Serial.print(inches);
Serial.print("in, ");
Serial.print(cm);
Serial.print("cm");
Serial.println();
}
void playMusic() {
int i, length, toneNo, duration;
while(1)
{
// bee
length = sizeof(beeTone)-1;
for (i=0; i<length; i++) {
toneNo = getTone(beeTone[i]);
duration = beeBeat[i]*TEMPO;
tone(buzzer,toneTable[toneNo][3]);
delay(duration);
noTone(buzzer); // separate each sound
delay(15);
measuringDistance();
if(cm <= 10) return; // breaking the music
}
delay(2000);
if(cm <= 10) return;
// star
length = sizeof(starTone)-1;
for (i=0; i<length; i++) {
toneNo = getTone(starTone[i]);
duration = starBeat[i]*TEMPO;
tone(buzzer,toneTable[toneNo][3]);
delay(duration);
noTone(buzzer); // separate each sound
delay(15);
measuringDistance();
if(cm <= 10) return;
}
delay(2000);
if(cm <= 10) return;
}
}
// 在七段顯示器上顯示指定的 0~9 數字
void sevenSegWrite(int digit) {
int pin = sevenSegA;
for (int seg=0; seg<7; ++seg) {
digitalWrite(pin, seven_seg_digits[digit][seg]);
++pin;
}
}
static int ledCounter;
static int countDownActivated;
void debounceButtonInputA0() {
static unsigned long lastDebounceTime=0;
unsigned long currentTime = millis();
if((currentTime - lastDebounceTime) > DEBOUNCE_DELAY) {
lastDebounceTime = currentTime;
if(!countDownActivated) { // this is almost useless in single tasking
ledCounter++;
}
}
}
void debounceButtonInputA1() {
static unsigned long lastDebounceTime=0;
unsigned long currentTime = millis();
if((currentTime - lastDebounceTime) > DEBOUNCE_DELAY) {
lastDebounceTime = currentTime;
if (ledCounter>0)
countDownActivated = 1;
}
}
// executed @ powerup or every time the reset button is pressed
void setup() {
pinMode(buzzer, OUTPUT);
noTone(buzzer);
pinMode(A0, INPUT);
pinMode(A1, INPUT);
ledCounter = 0;
countDownActivated = 0;
for (int seg=0; seg<7; ++seg) {
pinMode(sevenSegA+seg, OUTPUT);
}
Serial.begin (9600); // Serial Port begin
pinMode(trigPin, OUTPUT); //Define inputs and outputs
pinMode(echoPin, INPUT);
// pinMode(buzzer,OUTPUT);
}
void loop() {
measuringDistance();
if(digitalRead(A0)) {
debounceButtonInputA0();
}
if(cm <= 10) {
debounceButtonInputA1();
}
if(countDownActivated==1) {
delay(1000);
ledCounter--;
if(ledCounter==-1) {
playMusic();
ledCounter = 0;
countDownActivated = 0;
}
}
if(ledCounter>9) {
ledCounter = 0;
}
sevenSegWrite(ledCounter);
delay(500);
}
```
### 微觀世界的彈跳現象
這裡的按鈕有點怪?
```
if (digitalRead(A0))
{
updateLed(0);
}
```
為什麼不寫成下面這樣就好?
```
digitalRead(A0);
```
原因就是**彈跳**現象
按鈕開關按下時會在極短時間內產生多次開合現象(稱為機械彈跳)
而使用控制板偵測腳位狀態時會讀取到多次的HIGH、LOW的切換 但實際上使用者只按下一次開關,因此會造成程式錯誤 所以設計者需加入「防彈跳」(Debounce)設計來避免此現象
![](https://i.imgur.com/dZ7rnfq.png)
```
unsigned long currentTime = millis();
if ((currentTime - lastDebounceTime) > DEBOUNCE_DELAY) {
//程式碼內容
lastDebounceTime = currentTime;
}
```
#### 如果你還會有機會使用 Arduino 的話,下面的連結和小抄整理是有用的
- [Arduino Programming Cheat Sheet](https://github.com/przygodyzkodem/Arduino-Cheat-Sheet/blob/master/Arduino_Cheat_Sheet_1-en.pdf)
- [Arduino Cheat Sheet](https://dlnmh9ip6v2uc.cloudfront.net/learn/materials/8/Arduino_Cheat_Sheet.pdf)
- [Extended Reference](http://arduino.cc/en/Reference/Extended)