程式設計目標及程式運行效果說明
程式設計目標:通過按下按鍵key1來控制音樂播放以及數碼管的歌詞顯示,
程式運行效果說明:按下按鍵key1,此時播放音樂《I can say ABC》并顯示歌詞;再次按下key1鍵可以暫停播放音樂,
程式相關電路及原理說明
1.原理說明
本實驗板采用的是無源蜂鳴器,相比與有源蜂鳴器,無源蜂鳴器的優點在于價格便宜,可以通過控制其振動頻率來改變發出的聲音,因此,無源蜂鳴器可以用于音樂的播放,而有源蜂鳴器的優點在于使用簡單,不需要撰寫“樂譜”,本實驗板使用的無源蜂鳴器是電磁式蜂鳴器,電磁式蜂鳴器由振蕩器、電磁線圈、磁鐵、振動膜片及外殼等組成,接通電源后,接收到的音頻信號電流通過電磁線圈,使電磁線圈產生磁場,振動膜片在電磁線圈和磁鐵的相互作用下,周期性地振動發聲,
本程式中,陣列music[]即是要播放的音樂,格式為音符,節拍,音符,節拍,如此回圈下去,音符為要發出的音調,而節拍則是聲音的持續時間,如圖,在陣列music[]中,音符表示的格式為:十位代表是低八度,中八度還是高八度,1代表高八度,2代表中八度,3代表高八度,個位代表簡譜的音符,例如,0x15代表低八度的S0,如圖即是低5S0,0x21代表中八度的DO,如圖即是中1 D0,音符中,0x00代表結束符,表示整首歌曲演唱完畢,而0xff代表休止符,表示要休止100ms,遇到這兩種情況,都應該重新執行回圈中的第一步,其余情況則是正常播放,程式燒入單片機后,需要按下按鍵key1才會進行演奏,相比基礎篇則添加了歌詞數碼管同步顯示功能,
注:因使用數碼管顯示歌詞著實有些勉強,有些字母很難在數碼管上面顯示,如“M”等,這個實驗主要的學習重點在于歌詞的同步顯示和歌曲的數字編排(即由簡譜變成音樂代碼), ABC英文歌字母數碼管顯示對應符號表(A-Z)已另外做成檔案,請見檔案“ABC英文歌字母數碼管顯示對應符號表(A-Z)”,
2.程式相關電路
(1)無源蜂鳴器電路原理圖

(2)按鍵控制電路

芯片相關引腳:Beep——P3^4;
代碼如下:
#include "STC15F2K60S2.H"
#define uint unsigned int //宏定義
#define uchar unsigned char
sbit led_sel=P2^3; //數碼管與發光二極管選通引腳
sbit sel0=P2^0; //SEL0、SEL1、SEL2組合為位選資訊,0-7
sbit sel1=P2^1;
sbit sel2=P2^2;
sbit beep=P3^4; //蜂鳴器引腳
sbit key1=P3^2; //定義按鍵1
sbit key2=P3^3; //定義按鍵2
uchar timeh,timel; //定義定時器的重裝值
uchar flag; //所選擇點亮的數碼管0-7標志位
bit zanting; //播放或暫圖示志位
uchar jindu=0; //music陣列中指向的位置
uchar geci[]={0x77,0x7f,0x39,0x00,0x6f,0x7b}; //顯示開機畫面的數碼管段碼 ABC ge
uchar geci1[]={0x77,0x7f,0x39,0x3f,0x79,0x71,0x3d,0x40}; //顯示A-G的數碼管段碼 ABCDEFG-
uchar geci2[]={0x76,0x06,0x1e,0xf6,0x38,0x4f,0x37,0x40}; //顯示H-N的數碼管段碼 HIJKLMN-
uchar geci3[]={0x3f,0x73,0xbf,0x40,0xf7,0x6d,0x07,0x40}; //顯示O-T的數碼管段碼 OPQ-RST-
uchar geci4[]={0x62,0x3e,0xf9,0x40,0xf6,0xe6,0xdb,0x40}; //顯示U-Z的數碼管段碼 UVW-XYZ-
uchar geci5[]={0xf6,0xe6,0xdb,0x80,0x37,0x3f,0xf9,0x00}; //XYZ,Now
uchar geci51[]={0xe6,0x3f,0x62,0x00,0x6d,0x79,0x79,0x00}; //you see
uchar geci6[]={0x0f,0x39,0x77,0x37,0x00,0x6d,0x77,0xe6}; //I can say
uchar geci61[]={0x77,0x7f,0x39,0x00,0x00,0x00,0x00,0x00}; // ABC
uchar weixuan[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07}; //位選碼
uchar code music[] ={ //音樂代碼,歌曲為《I can say ABC》,格式為: 音符, 節拍, 音符, 節拍,
0x11,0x10,0x21,0x10, //音符的十位代表是低八度,中八度還是高八度,1代表低八度,2代表中八度,3代表高八度
0x25,0x10,0x25,0x10, //個位代表簡譜的音符,例如0x15代表低八度的S0,0x21代表中八度的DO,
0x26,0x10,0x26,0x10, //節拍則是代表音長,例如:0x10代表一拍,0x20代表兩拍,0x05代表1/2拍
0x25,0x10,0x25,0x10, //16
0x24,0x10,0x14,0x10,
0x23,0x10,0x13,0x10,
0x22,0x10,0x22,0x10,
0x21,0x10,0x21,0x10, //32
0x25,0x10,0x25,0x10,
0x24,0x10,0x24,0x20,
0x23,0x10,0x23,0x10,
0x22,0x10,0x22,0x10, //48
0x25,0x10,0x25,0x10,
0x24,0x10,0x24,0x20,
0x23,0x10,0x23,0x10,
0x22,0x10,0x22,0x10, //64
0x21,0x10,0x21,0x10,
0x25,0x10,0x25,0x10, //72
0x26,0x10,0x26,0x10,
0x25,0x10,0x25,0x10, //80
0x24,0x10,0x24,0x10,
0x23,0x10,0x23,0x10, //88
0x22,0x10,0x22,0x10,
0x21,0x10,0x21,0x20, //96
0x00,0x00
};
uchar code quzi[] ={ //此陣列資料為各個音符在定時器中的重裝值,第一列是高位,第二列是低位
0xf8,0x8c, //低八度,低1
0xf9,0x5b,
0xfa,0x15, //低3
0xfa,0x67,
0xfb,0x04, //低5
0xfb,0x90,
0xfc,0x0c, //低7
0xfc,0x44, //中央C調
0xfc,0xac, //中2
0xfd,0x09,
0xfd,0x34, //中4
0xfd,0x82,
0xfd,0xc8, //中6
0xfe,0x06,
0xfe,0x22, //高八度,高1
0xfe,0x56,
0xfe,0x6e, //高3
0xfe,0x9a,
0xfe,0xc1, //高5
0xfe,0xe4,
0xff,0x03 //高7
};
void delay(unsigned int xms)
{
uint i,j;
for(i=xms;i>0;i--)
for(j=124;j>0;j--);
}
//功能描述:在quzi陣列中,找到music陣列定義的簡譜音符的重裝值,并回傳其在quzi陣列中的位置
//入口引數:tem:music陣列中定義的簡譜音符
//出口引數:回傳的是tem音符在quzi陣列中的位置值
uchar quyin(uchar tem)
{
uchar qudiao,jp,weizhi; //定義曲調,音符和位置
qudiao=tem/16; //高4位是曲調值
jp=tem%16; //低4位是音符
if(qudiao==1) //當曲調值為1時,即是低八度,低八度在quzi陣列中基址為0
qudiao=0;
else if(qudiao==2) //當曲調值為2時,即是中八度,中八度在quzi陣列中基址為14
qudiao=14;
else if(qudiao==3) //當曲調值為3時,即是高八度,高八度在quzi陣列中,基址為28
qudiao=28;
weizhi=qudiao+(jp-1)*2; //通過基址加上音符作為偏移量,即可定位此音符在quzi陣列中的位置
return weizhi; //回傳這一個位置值
}
void playmusic()//播放音樂
{
uchar p,m,tem; //m為節拍
while(1)
{
if(zanting==1) //播放或暫停控制
{
p=music[jindu];
if(p==0x00) //如果碰到結束符,延時1秒,回到開始再來一遍
{
jindu=0;
delay(1000);
break;
}
else if(p==0xff) //若碰到休止符,延時100ms,繼續取下一音符
{
jindu++;
delay(100);
TR0=0; //關定時器0
break;
}
else //正常情況下取音符和節拍
{
tem=quyin(music[jindu]); //取出當前音符在quzi陣列中的位置值
timeh=quzi[tem]; //把音符相應的計時器重裝載值賦予timeh和timel
timel=quzi[tem+1];
jindu++;
TH0=timeh; //把timeh和timel賦予計時器
TL0=timel;
m=music[jindu]; //取得節拍
jindu++;
}
TR0=1; //開定時器0
delay(m*180); //等待節拍完成, 通過P3^4口輸出音頻
TR0=0; //關定時器0
beep=0; //將beep置0,以保護蜂鳴器
}
}
}
//系統初始化,功能是配置IO口
void init_sys()
{
P0M0=0xff; //設定推挽模式
P0M1=0x00;
P2M0=0x08;
P2M1=0x00;
P3M0=0x10;
P3M1=0x00;
}
//定時器初始化
void init()
{
TMOD=0x01; //設定定時器0,作業方式1,16位手動重裝初值;設定定時器1,作業方式0,16位自動重裝初值
TH0=0xD8; //設定定時器0初值
TL0=0xEF;
TH1=(65536-1000)/256; //設定定時器1初值
TL1=(65536-1000)%256;
EA=1; //打開總中斷
ET0=1; //打開定時器0中斷
ET1=1; //打開定時器1中斷
TR0=0; //定時器0暫時不啟動
TR1=1; //啟動定時器1
EX0=1; //啟動外部中斷0
zanting=0; //播放或暫圖示志位賦初值
beep=0; //將beep置0,以保護蜂鳴器
}
void main()
{
init_sys(); //系統初始化
init();
P0=0x00;
key1=1;
flag=0;
while(1)
playmusic(); //進入播放音樂函式
}
//定時器0中斷處理,重新裝值,并把beep值取反,產生方波
void tim1()interrupt 1 //計時器控制頻率
{
TH0=timeh; //中斷后重賦初值
TL0=timel;
beep=~beep; //中斷beep翻轉產生方波,使得蜂鳴器發聲
}
//按下按鍵1的外部中斷,對標志位取反,功能是暫停和播放音樂
void exint0()interrupt 0
{
if(key1==0)
{
delay(10);
if(key1==0)
zanting=~zanting;
}
}
//定時器1中斷處理,用作數碼管顯示歌詞,
void timer1()interrupt 3 //把顯示程式提到定時器1中斷服務程式
{
flag++;
if(flag==8)
flag=0;
P0=0;
P2=weixuan[flag];
if(jindu==0) //通過判斷播放時的jindu進而控制此時的歌詞顯示
{ //此時為零時,顯示“ABC ge”
switch(flag)
{
case 0:P0=geci[0];break;
case 1:P0=geci[1];break;
case 2:P0=geci[2];break;
case 4:P0=geci[4];break;
case 5:P0=geci[5];break;
default:P0=geci[3];break;
}
}
else if(jindu<16)
{ //此時進度為0-15之間,顯示“ABCDEFG-”
switch(flag)
{
case 0:P0=geci1[0];break;
case 1:P0=geci1[1];break;
case 2:P0=geci1[2];break;
case 3:P0=geci1[3];break;
case 4:P0=geci1[4];break;
case 5:P0=geci1[5];break;
case 6:P0=geci1[6];break;
default:P0=geci1[7];break;
}
}
else if(jindu<32)
{ //此時進度為16-31之間,顯示“HIJKLMN-”
switch(flag)
{
case 0:P0=geci2[0];break;
case 1:P0=geci2[1];break;
case 2:P0=geci2[2];break;
case 3:P0=geci2[3];break;
case 4:P0=geci2[4];break;
case 5:P0=geci2[5];break;
case 6:P0=geci2[6];break;
default:P0=geci2[7];break;
}
}
else if(jindu<48)
{ //此時進度為32-47之間,顯示“OPQ- RST-”
switch(flag)
{
case 0:P0=geci3[0];break;
case 1:P0=geci3[1];break;
case 2:P0=geci3[2];break;
case 3:P0=geci3[3];break;
case 4:P0=geci3[4];break;
case 5:P0=geci3[5];break;
case 6:P0=geci3[6];break;
default:P0=geci3[7];break;
}
}
else if(jindu<64)
{ //此時進度為48-63之間,顯示“UVW- XYZ-”
switch(flag)
{
case 0:P0=geci4[0];break;
case 1:P0=geci4[1];break;
case 2:P0=geci4[2];break;
case 3:P0=geci4[3];break;
case 4:P0=geci4[4];break;
case 5:P0=geci4[5];break;
case 6:P0=geci4[6];break;
default:P0=geci4[7];break;
}
}
else if(jindu<72)
{ //此時進度為64-71之間,顯示“XYZ- NOW”
switch(flag)
{
case 0:P0=geci5[0];break;
case 1:P0=geci5[1];break;
case 2:P0=geci5[2];break;
case 3:P0=geci5[3];break;
case 4:P0=geci5[4];break;
case 5:P0=geci5[5];break;
case 6:P0=geci5[6];break;
default:P0=geci5[7];break;
}
}
else if(jindu<80)
{ //此時進度為72-79之間,顯示“YOU SEE ”
switch(flag)
{
case 0:P0=geci51[0];break;
case 1:P0=geci51[1];break;
case 2:P0=geci51[2];break;
case 3:P0=geci51[3];break;
case 4:P0=geci51[4];break;
case 5:P0=geci51[5];break;
case 6:P0=geci51[6];break;
default:P0=geci51[7];break;
}
}
else if(jindu<88)
{ //此時進度為80-87之間,顯示“ICAN SAY”
switch(flag)
{
case 0:P0=geci6[0];break;
case 1:P0=geci6[1];break;
case 2:P0=geci6[2];break;
case 3:P0=geci6[3];break;
case 4:P0=geci6[4];break;
case 5:P0=geci6[5];break;
case 6:P0=geci6[6];break;
default:P0=geci6[7];break;
}
}
else if(jindu<96)
{ //此時進度為88-95之間,顯示“ABC”
switch(flag)
{
case 0:P0=geci61[0];break;
case 1:P0=geci61[1];break;
case 2:P0=geci61[2];break;
case 3:P0=geci61[3];break;
case 4:P0=geci61[4];break;
case 5:P0=geci61[5];break;
case 6:P0=geci61[6];break;
default:P0=geci61[7];break;
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/295264.html
標籤:其他
