Arduino案例實操 -- 蜂鳴器演奏歌曲(聖誕歌 Jingle Bells)

你們也知道,聖誕節就快到了,讓arduino完成一個比較應景的案例也會讓節日過得更有意思。
arduino可以做的放歌案例除了用語音模塊外,比較簡單成本最低的就是利用蜂鳴器了,只要控制好頻率和節拍,蜂鳴器也可以演奏音樂的,但你要準備的東西也會複雜一點。


一、聖誕歌簡譜

簡譜不理解是什麼??!!
很簡單,你小學時候的音樂課本上面的譜子就是了,下面這張是Jingle Bells的簡譜,歌詞是中文版不用太認真,調子是一樣的。
在這裏插入圖片描述
在這裏插入圖片描述
簡譜上除了中文,其他的應該都看不懂了吧

敲黑板,這個地方要好好理解一下,後面要用到!!!

  • 音符節奏分爲一拍、半拍、1/4拍、1/8拍,我們規定一拍音符的時間爲1,那麼半拍爲0.5,1/4拍爲0.25;1/8拍爲0.125……,爲每個音符賦予這樣的拍子播放出來,音樂的整個基調就出來了。
  • 確定簡譜的調,在簡譜歌名左下角(即整個簡譜的左上角)處,有個 1 = F 4/4 ,這裏的 F 表示這個曲子是 F 調的,下面會講到,而 4/4 表示曲子是四四拍的
  • 音符(簡譜中文字上面對應的每個數字)類型說明,舉個栗子,Jingle Bells簡譜第一小段音符 5 3 2 1 5 0 5 5
    1)這裏面5、3、2、1可以看到數字下面都帶着下劃線,這表示每個音符對應的節拍爲0.5拍,即音符5爲0.5拍,音符3爲0.5拍,音符2和音符1也是0.5拍,0也是0.5拍,仔細看最後兩個音符,兩個5下面都是2條下劃線,這表示0.25拍,這樣就好理解了,每個音符都看成1拍,每帶一條下劃線就當前拍數除以2,這樣一條下劃線是1/2=0.5拍,兩條下劃線是1/2/2=0.25拍,ok吧;然後把這一小段音符 5 3 2 1 5 0 5 5的節拍數加起來是0.5+0.5+0.5+0.5+1+0.5+0.25+0.25 = 4拍,這樣就跟簡譜標的四四拍是吻合的了,也說明你這一小段的節拍數沒算錯,記住Jingle Bells簡譜中每一小段都是4拍(這段話對照着簡譜去理解)
    2)聰明的你可能還發現有些音符還帶有小點點,當音符不帶點時,表示中音,例如第一小段中的音符3,表示中音音符3;當音符下面帶小點時,表示低音,例如第一小段中的音符5,表示低音音符5;同樣的,當音符上面帶小點時,表示高音音符,這在Jingle Bells簡譜中沒有出現高音
    3)音符後邊帶小數點的話,表示該音符節拍+0.5拍(音符帶下劃線的話,在計算完下劃線節拍的基礎上+0.5拍);音符後邊帶 - 的,音符+1拍(計算完下劃線節拍的基礎上+1拍)
    4)有的兩個連續的音符上面帶弧線,表示連音,可以稍微改下連音後面那個音的頻率,比如減少或增加一些數值(需自己調試),這樣表現會更流暢,其實不做處理,影響也不大

二、音符頻率對照表

簡單瞭解簡譜後,該怎麼把音符轉換成蜂鳴器對應頻率呢,看看音符頻率表
低音

音符→
音調↓
1# 2# 3# 4# 5# 6# 7#
A 221 248 278 294 330 371 416
B 248 278 294 330 371 416 467
C 131 147 165 175 196 221 248
D 147 165 175 196 221 248 278
E 165 175 196 221 248 278 312
F 175 196 221 234 262 294 330
G 196 221 234 262 294 330 371
中音
音符→
音調↓
1 2 3 4 5 6 7
A 441 495 556 589 661 742 833
B 495 556 624 661 742 833 935
C 262 294 330 350 393 441 495
D 294 330 350 393 441 495 556
E 330 350 393 441 495 556 624
F 350 393 441 495 556 624 661
G 393 441 495 556 624 661 742
高音
音符→
音調↓
1# 2# 3# 4# 5# 6# 7#
A 882 990 1112 1178 1322 1484 1665
B 990 1112 1178 1322 1484 1665 1869
C 525 589 661 700 786 882 990
D 589 661 700 786 882 990 1112
E 661 700 786 882 990 1112 1284
F 700 786 882 935 1049 1178 1322
G 786 882 990 1049 1178 1322 1484
如上的頻率表適用於arduino的各種蜂鳴器曲目,由於案例中Jingle Bells是F調的,所以在表中我把F調對應的音符都標紅了,可以看到表格中第一列是曲目音調,每首曲子基本都是一個調的,第一行是每個調對應的音符頻率1 到 7,也就是簡譜中我們看到的那些數字,最後是以高音、中音、低音把頻率分成了3個表,看懂了就可以開始編程了

三、案例編程

看明白簡譜和頻率表就開始寫程序啦,首先宏定義曲目要用到的音符頻率

//中音NTF 0爲空拍
#define NTF0 -1
#define NTF1 350
#define NTF2 393	
#define NTF3 441
#define NTF4 495
#define NTF5 556
#define NTF6 624
#define NTF7 661

//高音NTFH
#define NTFH1 700
#define NTFH2 786
#define NTFH3 882
#define NTFH4 935
#define NTFH5 965
#define NTFH6 996
#define NTFH7 1023

//低音NTFL
#define NTFL1 175
#define NTFL2 196
#define NTFL3 221
#define NTFL4 234
#define NTFL5 262
#define NTFL6 294
#define NTFL7 330

按照Jingle Bells簡譜定義音符數組

//音符頻率數組 
int tune[]=
{
	NTF3,NTF3,NTF3,NTF3,NTF3,NTF3,
	NTF3,NTF5,NTF1,NTF2,NTF3,NTF0,
	NTF4,NTF4,NTF4,NTF4,NTF4,NTF3,NTF3,NTF3,NTF3,
	NTF5,NTF5,NTF4,NTF2,NTF1,NTF0,

	NTFL5,NTF3,NTF2,NTF1,NTFL5,NTF0,NTFL5,NTFL5,
	NTFL5,NTF3,NTF2,NTF1,NTFL6,NTF0,
	NTFL6,NTF4,NTF3,NTF2,NTFL7,NTF0,
	NTF5,NTF5,NTF4,NTF2,NTF3,NTF1,NTF0,

	NTFL5,NTF3,NTF2,NTF1,NTFL5,NTF0,
	NTFL5,NTF3,NTF2,NTF1,NTFL6,NTF0,NTFL6,
	NTFL6,NTF4,NTF3,NTF2,NTF5,NTF5,NTF5,NTF5,
	NTF6,NTF5,NTF4,NTF2,NTF1,NTF0
};

按簡譜定義音符節拍數組

//音符節拍數組
float durt[]=
{
	0.5,0.5,1,0.5,0.5,1,
	0.5,0.5,0.75,0.25,1.5,0.5,
	0.5,0.5,1,0.5,0.5,0.5,0.5,0.25,0.25,
	0.5,0.5,0.5,0.5,1.5,0.5,

	0.5,0.5,0.5,0.5,1,0.5,0.25,0.25,
	0.5,0.5,0.5,0.5,1,1,
	0.5,0.5,0.5,0.5,1,1,
	0.5,0.5,0.5,0.5,1,0.75,0.25,

	0.5,0.5,0.5,0.5,1,1,
	0.5,0.5,0.5,0.5,1,0.5,0.5,
	0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,
	0.5,0.5,0.5,0.5,0.75,0.25
};

接着是主題函數部分

//定義蜂鳴器引腳,音符長度變量
int buzzer_pin = 9;
int length;

//setup函數,初始化引腳,計算長度
void setup()
{
	pinMode(buzzer_pin, OUTPUT);
	length = sizeof(tune)/sizeof(tune[0]);
}

//loop函數
void loop()
{
	//for循環演奏曲子
	for(int x=0;x<length;x++)
	{
		tone(buzzer_pin, tune[x]);
		delay(500*durt[x]);					//這裏的500爲控制每個音符的時長來定曲子的節奏
		noTone(buzzer_pin);
	}
	delay(500);								//開始下一輪循環的時間間隔
}

程序對於蜂鳴器單獨演奏曲子來說簡短有效,對於在曲子演奏中間要一邊做點其他事情,可能就有些些麻煩了


四、案例擴展

聖誕節案例肯定不能少了聖誕樹不是嘛,這種聖誕樹小裝飾在某寶上十幾二十塊就能買到帶帶燈的,我手頭就搞了一個出來,店家給的蚊帳布一樣的原材料要自己動手,也是花了不少時間搞,主要是燈帶的固定很是頭疼,下面看一下效果圖,還是可以接受的
在這裏插入圖片描述
商家隨機給的顏色布料,粉色的,老夫也是無奈,還有圈上彩燈後的效果,還怪不錯的咧
在這裏插入圖片描述
是吧,節日一擺上那是很有氣氛了,還給了些聖誕鞋小掛飾什麼的,沒給弄上去,兩節5號電池供電,常亮狀態,爲了能達到彩燈控制效果,我把線剪了接到arduino控制的繼電器上面,通過繼電器控燈,程序也寫了,以最原始的方式,另外加了OLED屏幕的節日祝福顯示,具體代碼如下

#include <U8glib.h>
 
//引腳定義
int buzzer_0 = 5;
int relay_0 = 12;
U8GLIB_SSD1306_128X64 u8g_0(U8G_I2C_OPT_NONE);

//setup函數
void setup() {
    pinMode(buzzer_0, OUTPUT);
    pinMode(relay_0, OUTPUT);

	//OLED屏幕顯示 Merry Christmas
    u8g_0.firstPage();
    do {
        u8g_0.setFont(u8g_font_9x18);
        u8g_0.drawStr(5, 30, "Merry");
        u8g_0.drawStr(40, 55, "Christmas");
    } while (u8g_0.nextPage());
}

//loop函數
void loop() {
    tone(buzzer_0, 441, 0);		// T1-1
    delay(0);
    delay(250);
    noTone(buzzer_0);
    tone(buzzer_0, 441, 0);
    delay(0);
    delay(250);
    noTone(buzzer_0);
    tone(buzzer_0, 441, 0);
    delay(0);
    delay(480);
    noTone(buzzer_0);
    digitalWrite(relay_0, HIGH);
    tone(buzzer_0, 441, 0);
    delay(0);
    delay(250);
    noTone(buzzer_0);
    tone(buzzer_0, 441, 0);
    delay(0);
    delay(250);
    noTone(buzzer_0);
    tone(buzzer_0, 441, 0);
    delay(0);
    delay(230);
    noTone(buzzer_0);
    digitalWrite(relay_0, LOW); 
    tone(buzzer_0, 441, 0);			// T1-2
    delay(0);
    delay(250);
    noTone(buzzer_0);
    tone(buzzer_0, 556, 0);
    delay(0);
    delay(250);
    noTone(buzzer_0);
    tone(buzzer_0, 350, 0);
    delay(0);
    delay(355);
    noTone(buzzer_0);
    digitalWrite(relay_0, HIGH);
    tone(buzzer_0, 393, 0);
    delay(0);
    delay(125);
    noTone(buzzer_0);
    tone(buzzer_0, 441, 0);
    delay(0);
    delay(750);
    noTone(buzzer_0);
    tone(buzzer_0, -1, 0);
    delay(0);
    delay(230);
    noTone(buzzer_0);
    digitalWrite(relay_0, LOW); 
    tone(buzzer_0, 495, 0);			// T1-3
    delay(0);
    delay(250);
    noTone(buzzer_0);
    tone(buzzer_0, 495, 0);
    delay(0);
    delay(250);
    noTone(buzzer_0);
    tone(buzzer_0, 495, 0);
    delay(0);
    delay(500);
    noTone(buzzer_0);
    tone(buzzer_0, 495, 0);
    delay(0);
    delay(230);
    noTone(buzzer_0);
    digitalWrite(relay_0, HIGH);
    tone(buzzer_0, 495, 0);
    delay(0);
    delay(250);
    noTone(buzzer_0);
    tone(buzzer_0, 441, 0);
    delay(0);
    delay(250);
    noTone(buzzer_0);
    tone(buzzer_0, 441, 0);
    delay(0);
    delay(250);
    noTone(buzzer_0);
    tone(buzzer_0, 441, 0);
    delay(0);
    delay(125);
    noTone(buzzer_0);
    tone(buzzer_0, 441, 0);
    delay(0);
    delay(105);
    noTone(buzzer_0);
    digitalWrite(relay_0, LOW); 
    tone(buzzer_0, 556, 0);			// T1-4
    delay(0);
    delay(250);
    noTone(buzzer_0);
    tone(buzzer_0, 556, 0);
    delay(0);
    delay(250);
    noTone(buzzer_0);
    tone(buzzer_0, 495, 0);
    delay(0);
    delay(230);
    noTone(buzzer_0);
    digitalWrite(relay_0, HIGH);
    tone(buzzer_0, 393, 0);
    delay(0);
    delay(250);
    noTone(buzzer_0);
    tone(buzzer_0, 350, 0);
    delay(0);
    delay(750);
    noTone(buzzer_0);
    tone(buzzer_0, -1, 0);
    delay(0);
    delay(230);
    noTone(buzzer_0);
	...						//此處省略200+行重複代碼
	delay(1500);			//循環演奏的時間間隔
}

上述代碼只取了完整曲子的1/3段,因爲每個音符都是以tone函數,delay函數,noTone函數的形式在循環,4個小段加起來30個音符左右,還有OLED顯示和繼電器控制的程序前前後後140行代碼左右,全曲82個音符總370行代碼,也是敲得心累,上面把後邊200+行的重複函數代碼省略了,只是頻率跟延時不同。


案例擴展實現了簡介控制聖誕樹燈光閃爍,其實個人覺得接PWM引腳做成呼吸燈效果會更棒,也不用聽繼電器在滴答滴答叫,這個就看你們個人的idea了,分享到這,下回見~

發佈了40 篇原創文章 · 獲贊 85 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章