目錄
蜂鳴器介紹
蜂鳴器是一種將電信號轉換為聲音信號的器件,常用來產生設備的按鍵音、報警音等提示信號
蜂鳴器按驅動方式可分為有源蜂鳴器和無源蜂鳴器
有源蜂鳴器:內部自帶振盪源,將正負極接上直流電壓即可持續發聲,頻率固定
無源蜂鳴器:內部不帶振盪源,需要控制器提供振盪脈沖才可發聲,調整提供振盪脈沖的頻率,可發出不同頻率的聲音

這里顯然我們單片機上面的蜂鳴器是無源蜂鳴器,需要我們手動編寫代碼為其配置振盪脈沖的頻率,而使其發出不同的音調。
驅動電路
三極管驅動
左圖為高電平導通,右圖為低電平導通

集成電路驅動

我們單片機上的ULN2003D驅動芯片的OUT1~~OUT4是用來驅動電機的,自然OUT5是用來驅動蜂鳴器的(BEEP),最后OUT6,7沒有接線(之所以這樣都是為了節約引腳口而考慮)


音樂的相關知識
我了解的也不多,所以可能有錯的請大家見諒:
一個曲子簡單的是由音調和節拍決定的,音調是什么1,2,3,4,5,6,7這些數字,然后節拍是什么4分音符,8分音符。也就是音調持續的時間長短。
那么如何在蜂鳴器上模擬出各種各樣的音調呢,首先是要知道不同音調有不同的頻率,所以我們只要設法精確的將頻率的信號輸入到蜂鳴器就行
觀察規律我們發現:每個音符滿足12平分率
(前面一個音符的頻率)*2^(1/12)=(后面一個音符的頻率)
或者說(后面一個音符的頻率)/{2^(1/12)}=(前面一個音符的頻率)

那么頻率的公式為f=1/T,我們可以發現相鄰音調之間就差那么十幾赫茲,所以要求的精度還是比較高的,所以我們用定時器來計時,得到精確的頻率脈沖。
之前學過波的相關知識,一個波形要有波峰和波谷才算一個完整的周期,所以我們在音符頻率對應的周期內要將蜂鳴器的電壓翻轉2次。為了使定時器方便編碼(不能說計時一半還沒溢出就進行中斷,來進行翻轉),我們在以一個音調周期一半為一個單位進行計時並中斷來翻轉蜂鳴器,然后就實現了一個周期翻轉2次的目的。
音符與計時器重裝載值對應表
將低音L1為示例:T=1/f=1/262=0.0038167938931298,Tx1000000=3,816.793893129771
T/2=1,908.396946564885,取整1908,重裝載值=65536-T/2=63628

有了這個表以后就可以先將音符宏定義(例如高音用H開頭,低音用L開頭),然后創建一個數組將音符與對應的重裝載值對應即可;
將樂譜轉換為宏定義的音調譜
以天空之城簡譜的節選為例,簡單說明一下譜子里面包含的信息:

下面我來寫天空之城的第一行的音符,我以一個一分音符為時間基准,那么一節有4拍就是四個四分音符16
(將空音符定義為P,高音用H開頭,低音用L開頭,中央音符用M開頭)
//第一小節
P, 4,
P, 4,
P, 4,
M6, 2,
M7, 2,
//第二小節
H1, 4+2,
M7, 2,
H1, 4,
H3, 4,
//第三小節
M7, 4+4+4,
M3, 2,
M3, 2,
這樣一來將樂譜建立一個數組,那么音調與節拍就交替存在了
注意:還有一點就是,那個不同音調之間要有停頓感,為了實現這一目的所以在實際操作的時候,每個音符演奏以后,要將定時器延時5~10ms再進行下一個音符的演奏
實際代碼演示:
音樂《卡農》片段
主函數:
#include <REGX52.H>
#include "Delay.h"
#include "Timer0.h"
//蜂鳴器端口定義
sbit Buzzer=P2^5;
//播放速度,將一個四分音符的時長設置為600(ms),並以四分音符的時長為基准
#define SPEED 600
//音符與索引對應表,P:休止符,L:低音,M:中音,H:高音,下划線:升半音符號#
#define P 0
#define L1 1
#define L1_ 2
#define L2 3
#define L2_ 4
#define L3 5
#define L4 6
#define L4_ 7
#define L5 8
#define L5_ 9
#define L6 10
#define L6_ 11
#define L7 12
#define M1 13
#define M1_ 14
#define M2 15
#define M2_ 16
#define M3 17
#define M4 18
#define M4_ 19
#define M5 20
#define M5_ 21
#define M6 22
#define M6_ 23
#define M7 24
#define H1 25
#define H1_ 26
#define H2 27
#define H2_ 28
#define H3 29
#define H4 30
#define H4_ 31
#define H5 32
#define H5_ 33
#define H6 34
#define H6_ 35
#define H7 36
//索引與頻率對照表
unsigned int FreqTable[]={
0,
63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,
64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283,
};
//樂譜
unsigned int code Music[]={//樂譜較長加上關鍵字code將其儲存在ROM(flash)
//音符,時值,
//1
H5,2,
H3,1,
H4,1,
H5,2,
H3,1,
H4,1,
H5,1,
M7,1,
M6,1,
M7,1,
H1,1,
H2,1,
H3,1,
H4,1,
//2
H3,2,
H1,1,
H2,1,
H3,2,
M3,1,
M4,1,
M5,1,
M6,1,
M5,1,
M4,1,
M5,1,
H1,1,
M7,1,
H1,1,
//3
M6,2,
H1,1,
M7,1,
M6,2,
M5,1,
M4,1,
M5,1,
M4,1,
M3,1,
M4,1,
M5,1,
M6,1,
M7,1,
H1,1,
//4
M6,2,
H1,1,
M7,1,
H1,2,
M7,1,
H1,1,
M7,1,
M6,1,
M7,1,
H1,1,
H2,1,
H3,1,
H4,1,
H5,1,
//5
H5,2,
H3,1,
H4,1,
H5,2,
H3,1,
H4,1,
H5,1,
M7,1,
M6,1,
M7,1,
H1,1,
H2,1,
H3,1,
H4,1,
//6
H3,2,
H1,1,
H2,1,
H3,2,
M3,1,
M4,1,
M5,1,
M6,1,
M5,1,
M4,1,
M5,1,
H1,1,
M7,1,
H1,1,
0xFF //終止標志
};
unsigned int FreqSelect,MusicSelect;//MusicSelect為樂譜數組下標,FreqSelect音調宏定義
void main()
{
Timer0Init();
while(1)
{
if(Music[MusicSelect]!=0xFF) //如果不是停止標志位
{
FreqSelect=Music[MusicSelect]; //選擇音符對應的頻率
MusicSelect++;
Delay(SPEED/4*Music[MusicSelect]); //選擇音符對應的時長
MusicSelect++;
TR0=0;//不同音符間短暫停頓,利用延時開關定時器實現
Delay(5);
TR0=1;
}
else //如果是停止標志位
{
TR0=0; //關閉定時器
while(1);
}
}
}
void Timer0_Routine() interrupt 1
{
if(FreqTable[FreqSelect]) //如果是休止符(0),那么不播放聲音,只進行延時
{
//取對應頻率值的重裝載值到定時器(確認音高)FreqSelect=Music[MusicSelect]
TL0 = FreqTable[FreqSelect]%256; //設置低位定時初值
TH0 = FreqTable[FreqSelect]/256; //設置高位定時初值
Buzzer=!Buzzer; //翻轉蜂鳴器IO口(注意這里的重裝值是周期的一半,故僅進行一次蜂鳴器的翻轉)
}
}
定時器:
void Timer0Init(void)
{
TMOD &= 0xF0; //設置定時器模式
TMOD |= 0x01; //設置定時器模式
TL0 = 0x18; //設置定時初值
TH0 = 0xFC; //設置定時初值
TF0 = 0; //清除TF0標志
TR0 = 1; //定時器0開始計時
ET0=1;
EA=1;
PT0=0;
}
延時函數Delay()
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
啰啰嗦嗦這么多,到這里就算寫完了,若有不當之處,懇請指正!
對了,還有什么想用單片機聽的音樂,可以在評論區留言哦,會盡快更新的,
