Python可以這樣玩(15):蜂鳴器與音樂
蜂鳴器是一種一體化結構的電子發聲器,採用直流電壓供電,廣泛應用於電腦、印表機、影印機、報警器、電子玩具、汽車電子設備、電話機、計時器等電子產品中作發聲器件。
工作原理
蜂鳴器的連線
蜂鳴器模擬救護車警笛聲音實驗
讓常見的蜂鳴器唱歌
tone(pin, frequency, duration)
noTone (pin):
C程式碼說明
使用 Python 演奏音樂
影片展示如下:
按其驅動方式的不同,可分為:有源蜂鳴器(內含驅動線路)和無源蜂鳴器(外部驅動)。教你區分有源蜂鳴器和無源蜂鳴器,現在市場上出售的一種小型蜂鳴器,因其體積小(直徑只有 11 mm)、重量輕、價格低、結構牢靠,而廣泛地應用在各種需要發聲的電器設備、電子製作和單片機等電路中。有源蜂鳴器和無源蜂鳴器的外觀好像一樣,但仔細看,兩者的高度略有區別,有源蜂鳴器,高度為 9 mm,而無源蜂鳴器的高度為 8mm。如將兩種蜂鳴器的引腳朝上放置時,可以看出有綠色電路板的一種是無源蜂鳴器,沒有電路板而用黑膠封閉的一種是有源蜂鳴器。
工作原理
蜂鳴器發聲原理是電流通過電磁線圈,使電磁線圈產生磁場來驅動振動膜發聲的,因此需要一定的電流才能驅動它,本實驗用的蜂鳴器內部帶有驅動電路,所以可以直接使用。當蜂鳴器連接的引腳為高電壓時,內部驅動電路導通,蜂鳴器發出聲音;當蜂鳴器連接的引腳為低電壓,內部驅動電路截止,蜂鳴器不發出聲音。
蜂鳴器的連線
本實驗用的蜂鳴器內部帶有驅動電路,所以可以直接將蜂鳴器的正極連接到數位口,蜂鳴器的負極連接到 GND 插口中。如下圖 (實際接線的時候,我並沒有把上個範例中的LED拆除,因此我把蜂鳴器的正極接到 PIN 10):
實際連線的時候,建議使用編號 ZK-1205S 的蜂鳴器,它會麵包板的格子剛好契合。注意上面的正負極。
蜂鳴器模擬救護車警笛聲音實驗
實驗原理:蜂鳴器發出聲音的時間間隔不同,頻率就不同,所以發出的聲音就不同。根據返一原理我們通過改發蜂鳴器發出聲音的時間間隔,來發出不同種聲音,來模擬各種聲音。本程式首先讓蜂鳴器間隔 1ms 發出一種頻率的聲音,迴圈 80 次;接著讓蜂鳴器間隔 2ms 發出另一種頻率的聲音,迴圈 100 次。程式我改寫如下,原本蜂鳴器執行的動作是放在 loop() 裡面的,但是由於執行起來停不了太吵了,所以我把主程式放在 setup() 裡面,只會執行三次。當然,如果你要保持原來的程式,讓它一直在loop()裡面執行,建議加一個開關,按下的時候才會響,開關的線路要如何加?思考一下。
int buzzer=10;
void setup()
{
pinMode(buzzer,OUTPUT);
unsigned char
i,j,times=0;
while(times<3)
{
for(i=0;i<80;i++)
{
digitalWrite(buzzer,HIGH);
delay(1);
digitalWrite(buzzer,LOW);
delay(1);
}
for(i=0;i<100;i++)
{
digitalWrite(buzzer,HIGH);
delay(2);
digitalWrite(buzzer,LOW);
delay(2);
}
times+=1;
}
}
void loop()
{
}
接下來請直接按上傳。不過這裡位遇到問題,如果之前正在使用 Python 控制 Arduino,可能就會報錯,COM3被占用,請關閉
Python並按下UNO上面的RESET按鈕,或移除USB重新插入一次。
讓常見的蜂鳴器唱歌
讓蜂鳴器亂叫的程式很簡單,如果能讓它唱歌就更好了。還記得我們在前一個章節中實做出讓多個LED燈按照音階閃爍,如果我們把這兩種電路結合在一起,效果勢必不錯。
要讓蜂鳴器唱歌就是讓蜂鳴器可以產生不同頻率的聲音, Arduino 上要用兩個指令:tone() / noTone() 來達到,我修改了網路上其他網友分享的範例,搭配我們特別設計的電路板,如下圖,做出可以放音樂並且同時亮起各個音階上的LED燈的功能:
如果對線路不清,可以放大此照片。因為蜂鳴器真的很吵,我特別多加了一個開關按鈕在蜂鳴器旁邊,想聽歌的時候,按下去即可。
指令的用法如下:
tone(pin, frequency, duration)
pin:輸出方波訊號的 digital pin
frequency (unsigned int):讓蜂鳴器發出的聲音的對應頻率
duration (unsigned long):發出聲音的長短
使用 tone() 的時候後面依然要搭配
delay (duration) 才能夠讓執行續等待聲音放完。
noTone (pin):
將指定 pin 的聲音停止,蜂鳴器一次只能撥放一個音階,如果要做和絃的效果,必須使用兩個蜂鳴器。先不要想那麼多,我們先做單音。
接下來要了解每個音階的頻率,請參考下面的表格,我們直接把數字用在我們的程式裡面就好:
最後,我們要來編寫程式了,C程式內容如下:
int speakerPin = 10;
// 依照數字簡譜1-7,高音1用8代替,0代表休止符
// 每個音階的拍子,寫在一起方便對照
int notes[] = {1,1,5,5,6,6,5,4,4,3,3,2,2,1,5,5,4,4,3,3,2,5,5,4,4,3,3,2,1,1,5,5,6,6,5,4,4,3,3,2,2,1,0};
int beats[] =
{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,8};
// 利用 sizeof(),算出總共要多少音符
int length = sizeof(notes)/sizeof(notes[0]);
// 決定一拍多長,這邊一拍 300 ms
int tempo = 300;
void setup() {
pinMode(speakerPin, OUTPUT);
pinMode(2,OUTPUT); //燈號Do
pinMode(3,OUTPUT); //燈號Re
pinMode(4,OUTPUT); //燈號Mi
pinMode(5,OUTPUT); //燈號Fa
pinMode(6,OUTPUT); //燈號So
pinMode(7,OUTPUT); //燈號La
pinMode(8,OUTPUT); //燈號Si
pinMode(9,OUTPUT); //燈號Do
}
void loop() {
// 利用
for 來播放我們設定的歌曲,一個音一個音撥放
for (int i = 0;
i < length; i++) {
// 如果是0的話,不撥放音樂
if (notes[i]
== 0) {
delay(beats[i] * tempo); // rest
} else {
// 呼叫
palyNote() 這個 function,將音符轉換成訊號讓蜂鳴器發聲
playNote(speakerPin,notes[i], beats[i] * tempo);
}
// 讓每個音符之間停頓一下
delay(tempo/10);
}
}
void playNote(int OutputPin, int note, int duration) {
// 音符字元與對應的頻率由兩個矩陣表示
int names[] = {
1, 2, 3, 4, 5, 6, 7, 8 };
int tones[] = {
261, 294, 330, 349, 392, 440, 494, 523 };
int leds[] = {
2, 3, 4, 5, 6, 7, 8, 9 };
// 播放音符對應的頻率
for (int i = 0;
i < 8; i++) {
if (names[i]
== note) {
tone(OutputPin,tones[i], duration);
//讓燈號亮起來
digitalWrite(leds[i], HIGH);
//下方的
delay() 及 noTone (),一定要有這兩行
delay(duration);
noTone(OutputPin);
digitalWrite(leds[i], LOW);
}
}
}
C程式碼說明
首先,為了同時控制音階與節拍,我用了兩個陣列來儲存,請看下面兩行:
int notes[] = {1,1,5,5,6,6,5,4,4,3,3,2,2,1,5,5,4,4,3,3,2,5,5,4,4,3,3,2,1,1,5,5,6,6,5,4,4,3,3,2,2,1,0};
int beats[] = {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,8};
這種方式有一個好處,就是你可以上下比對,一眼看出音階對應的節拍,比較直觀。接著,我們利用 playNote(speakerPin,notes[i], beats[i] * tempo) 這個副程式來撥放音樂,把 notes 與 beats 傳入,就知道每一個音要響多久。最後,在副程式的 for 迴圈裡面利用下面幾個動作來完成控制:
if (names[i]
== note) {
tone(OutputPin,tones[i], duration);
//讓燈號亮起來
digitalWrite(leds[i], HIGH);
//下方的
delay() 及 noTone (),一定要有這兩行
delay(duration);
noTone(OutputPin);
digitalWrite(leds[i],
LOW);
所有動作都是相對應的,亮燈、響聲、停頓、滅燈、止聲。這樣音樂就會一直循環的撥放。如果要樣它停止,請拔掉USB。
使用 Python 演奏音樂
現在讓我們用 Python 來改寫這段C語言程式,同樣的,請先上傳 Standard Firmata,你可以從 [檔案] [開啟最近] 裡面找到之前開啟過的 Standard Firmata 草稿,然後請按下 [上傳]。
最後你會看到紅色的訊息 “avardude done: Thank you.”,就代表上傳成功。
開啟 Python IDLE 馬上就會發現,Firmata 並不支援 tone() 與 noTone() 這兩個函數,也就是說,我們必須自己設計 tone() 的功能,好讓蜂鳴器發出音樂。所以,一切必須從頭開始。
我們回到一開始的救護車警示聲的程式:
for(i=0;i<80;i++)
{
digitalWrite(buzzer,HIGH);
delay(1);
digitalWrite(buzzer,LOW);
delay(1);
}
for(i=0;i<100;i++)
{
digitalWrite(buzzer,HIGH);
delay(2);
digitalWrite(buzzer,LOW);
delay(2);
}
要讓蜂鳴器發出聲音很簡單,輸入高電壓1ms 然後再輸入低電壓1ms,就是一個音頻,輸入高電壓2ms 再輸入低電壓2ms,又是另一個音頻。而頻率 Hz的定義,是指每秒鐘震動的次數。以第一個音頻為例,兩個 1ms 組合就等於震動一次需要 2ms,而 1 sec = 1000 ms,因此,1000 ms/2 ms =
500 Hz 就是第一個音的頻率,1000 ms/4 ms = 250 Hz 就是第二個音的頻率,這兩個音就組成救護車警笛聲。
有了這樣的概念就好辦了,我們知道 Do 到高音都的頻率如下:
from pyfirmata import Arduino
from time import sleep
# 定義八個音
notes = [ 261, 294, 330, 349, 392, 440, 494, 523 ]
把上面的公式反過來運算,1000 ms / 2 X ms = 261,X = 500 ms / 261 = 1.915 ms,也就是我們只要把 HIGH 和 LOW 的 delay 時間都設定成 1.915
ms ,在 Python 用 sleep(0.001915) 就可以發出 Do 的聲音了。
以上的理論是沒有問題的,但是卻遇到精度的問題,還記得在C語言程式裡面使用的 delay(1) 嗎?在這裡 delay() 的最小單位是毫秒(ms),並不支援 1.915 或是 1.7 毫秒,而在 Python 中所使用的 sleep() 的單位是秒,就更不可能達成,所以聽到的聲音都是一樣的。
結論是,如果想要用蜂鳴器撥放音樂,還是要回到 Arduino IDE 使用 tone()、noTone()來寫程式才能達成,至於
Python 如何玩音樂,我們會放到後面討論 Pymata 以及 Pygame 的章節再行討論。
影片展示如下:
留言
張貼留言