C音調與譜曲控制對應表
音調 低1(低音“dao”) 低2 低3 低4 低5 低6 低7
頻率 262 294 330 349 392 440 494
音調 中1(中音“dao”) 中2 中3 中4 中5 中6 中7
頻率 523 587 659 698 784 880 988
音調 高1(高音“dao”) 高2 高3 高4 高5 高6 高7
頻率 1046 1175 1318 1397 1568 1760 1967
實現方法
1.音頻控制
要讓蜂鳴器發出某音調的聲音,只要給蜂鳴器輸送該音調頻率的電平信號就可以了。
由于單片機I/O口的輸出只有高電平“1”和低電平“0”兩種狀態,因此給蜂鳴器輸送的電平信號實際上就是該音頻的方波。
例如:中音“1”的頻率為523HZ,它的周期為1/523s,即1.91ms,因此,只要給蜂鳴器輸送周期為1.91ms的脈沖方波電平信號就能發出
523HZ的音調,該方波的半周期為1.91/2=0.955ms.為此,需要利用定時器的中斷,讓輸送給蜂鳴器的電平信號每0.995ms取反一次即可,
單片機晶振頻率為11.0952MHZ,它的一個機器周期為12*(1/11.0592)us,所以需要的機器周期總數為
955us/1.085us=880
即定時器的定時常數應取為880,由上述分析,發出頻率為f的音頻時,定時常數C計算公式為
C=(10^6 / 2f)/1.085us = 460830/f
因為T0工作方式0,其最大計數值為8192,完全可以滿足各音頻定時常數設置的需要?梢宰C明,在已知定時常數C的條件下,13位計數器
的高8位和低5位的初值可由以下公式設定:
TH0=(8192-C)/32 //13位計數器高8位的賦初值方法
TL0=(8192-C)%32 //13位計數器低5位的賦初值方法
2.節拍控制
每分鐘72拍,每個節拍所需時間:(1000*60/72)ms=833ms // 具體見單片機扉頁
3.音調與節拍的存儲
可以將簡譜中所有音調的頻率及節拍分別存儲于兩個數組,然后依次從數組中讀出頻率,再根據頻率和定時器延時常數的計算公式
即可由定時器中斷控制發出該音調的音頻,其發聲時間可由節拍控制(1-4個延時單位)。
4.音調的宏定義
直接將頻率存入數組,顯然不如以“dao,rei,mi,fa,sao……”的形式存儲方便,但是為了讓單片機認識這些,需要在程序開頭處對各音調
的頻率進行宏定義。例如,低音6的頻率為440Hz,中音6的頻率為880Hz,高音6的頻率為1760Hz。所以可以對這三個頻率進行如下宏定義
#define l_la 440 //將“l_la”宏定義為低音“6”的頻率440Hz,以此類推
#define la 880
#define h_la 1760
程序設計
#include<reg52.h>
sbit deep=P2^3;
uint C;
#define uint unsigned int
#define uchar unsigned char
#define l_dao 262
#define l_re 286
#define l_mi 311
#define l_fa 349
#define l_sao 392
#define l_la 440
#define l_xi 494
#define dao 523
#define re 587
#define mi 659
#define fa 698
#define sao 784
#define la 880
#define xi 987
#define h_dao 1046
#define h_re 1174
#define h_mi 1318
#define h_fa 1396
#define h_sao 1567
#define h_la 1760
#define h_xi 1975
void delay(uint xms)
{
uchar i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
void main()
{
uchar i.j;
uint code f[]={re,mi,re,dao,l_la,dao,l_la,
l_sao,l_mi,l_sao,l_la,dao,
l_la,dao,sao,la,mi,sao,re,mi,re,mi,sao,mi,
l_sao,l_mi,l_sao,l_la,dao,
l_la,l_la,dao,l_la,l_sao,l_re,l_mi,
l_sao,re,re,sao,la,sao,
fa,mi,sao,mi,la,sao,mi,re,mi,l_la,dao,
re,mi,re,mi,sao,mi,l_sao,l_mi,l_sao,l_la,dao,
l_la,dao,re,l_la,dao,re,mi,re,l_la,dao,re,l_la,dao,
re,mi,re,l_la,dao,re,l_la,dao,re,mi,re,0xff};
uchar code JP[]={4,1,1,4,1,1,2, //每行對應一小節音調的節拍
2,2,2,2,8,
4,2,3,1,2,2,
10,
4,2,2,4,4,
2,2,2,2,4,
2,2,2,2,2,2,2,
10,
4,4,4,2,2,
4,2,4,4,
4,2,2,2,2,2,2,
10,
4,2,2,4,4,
2,2,2,2,6,
4,2,2,4,1,1,4,
10,
4,2,2,4,1,1,4,
10};
EA=1; //開總中斷
ET0=1; //定時器T0中斷允許
TMOD=0X00; //使用定時器T0的模式0(13位計數器)
while(1) //無限循環
{
i=0;
while(1)
{
i=0; //從第一個音調f[0]開始播放
while(f[i]!=0xff) //只要沒有讀到結束標志就繼續播放
{
C=460830/f[i];
TH0=(8192-C)/32;
TL0=(8192-C)%32;
TR0=1;
for(j=0;j<JP[i];j++) //控制節拍數
delay(); //延時一個節拍單位
TR0=0; //關閉定時器T0
i++; //播放下一個音調
}
}
}
void Time() interrupt 1
{
deep=!deep;
TH0=(8192-C)/32;
TL0=(8192-C)%32;
}
|