《基于51單片機(jī)的音樂(lè)游戲.doc》由會(huì)員分享,可在線閱讀,更多相關(guān)《基于51單片機(jī)的音樂(lè)游戲.doc(14頁(yè)珍藏版)》請(qǐng)?jiān)谘b配圖網(wǎng)上搜索。
一、課程設(shè)計(jì)內(nèi)容
利用89c52單片機(jī)設(shè)計(jì)一款音樂(lè)游戲。
游戲說(shuō)明:游戲基于單片機(jī)開發(fā)箱實(shí)現(xiàn)。游戲開始時(shí),會(huì)播放一首音樂(lè),同時(shí)每隔一個(gè)音符,LED點(diǎn)陣中會(huì)從最上方下落一個(gè)小點(diǎn)。玩家需要根據(jù)音樂(lè)的節(jié)奏,在小點(diǎn)下降到LED點(diǎn)陣的底部時(shí),按下對(duì)應(yīng)的按鍵。其中,矩陣鍵盤第一行從左到右的四個(gè)鍵分別對(duì)應(yīng)LED點(diǎn)陣的1、3、5、7列。根據(jù)每個(gè)按鍵按下的時(shí)間,會(huì)有不同的得分,通過(guò)LED模塊的D1-D5亮起來(lái)代表獲得1-5分。如果沒(méi)有LED燈亮則代表得0分。當(dāng)整首歌播放完后,會(huì)在LED點(diǎn)陣上顯示整首歌的得分,得分為0-99分。
二、設(shè)計(jì)思路和方案選擇
在決定制作音樂(lè)游戲后,利用開發(fā)箱的哪幾個(gè)模塊來(lái)制作也是需要考慮的一個(gè)問(wèn)題,最終我們決定利用現(xiàn)在的這個(gè)方案。其主要原因是LED點(diǎn)陣和蜂鳴器是游戲中的主體部分,因此無(wú)可替代。獨(dú)立鍵盤還是矩陣鍵盤的選擇上,經(jīng)過(guò)試驗(yàn)發(fā)現(xiàn)獨(dú)立鍵盤由于離LED點(diǎn)陣較遠(yuǎn),在實(shí)際游戲中經(jīng)常會(huì)有反應(yīng)不過(guò)來(lái)的情況。而矩陣鍵盤就在點(diǎn)陣的正下方,因此在操作上較為方便,所以選擇了矩陣鍵盤。而游戲得分的顯示,最初是想利用數(shù)碼管來(lái)實(shí)現(xiàn),后來(lái)發(fā)現(xiàn)數(shù)碼管和LED點(diǎn)陣都用到了單片機(jī)的P0口。在LED點(diǎn)陣無(wú)法被取代的情況下,只能退而求其次,選擇用LED模塊顯示得分。
程序的具體設(shè)計(jì)思路如下:
1. 實(shí)現(xiàn)音樂(lè)的播放效果
2. 實(shí)現(xiàn)LED點(diǎn)陣的顯示下落效果
3. 實(shí)現(xiàn)音樂(lè)的節(jié)奏和LED點(diǎn)的下落同步
4. 實(shí)現(xiàn)按鍵得分功能
5. 實(shí)現(xiàn)顯示得分效果
三、硬件設(shè)計(jì)
本游戲的實(shí)現(xiàn)基于單片機(jī)開發(fā)試驗(yàn)儀,其主要用到的硬件電路有
89C52CPU模塊,LED點(diǎn)陣模塊,74HC595模塊,LED模塊,矩陣鍵盤模塊和蜂鳴器模塊。
四、軟件框圖及設(shè)計(jì)
程序流程圖:
開始
LED與SOUND指針指向第一個(gè)音符
在頂部顯示音符對(duì)應(yīng)的LED燈
按鍵是否按下
計(jì)算并顯示上個(gè)音符的得分
顯示時(shí)間達(dá)到一個(gè)16分音符
LED燈下降一行
有LED燈下降到底部
發(fā)下一個(gè)音
SOUND指針指向下個(gè)音符
定時(shí)器1延時(shí)30ms
LED燈顯示時(shí)間達(dá)到規(guī)定長(zhǎng)度
LED指針指向下個(gè)音符
計(jì)算并顯示整首歌的得分
LED指針指向結(jié)束符號(hào)
結(jié)束
是
否
否
是
否
是
否
否
是
是
C語(yǔ)言程序:
#include
#include
#define COMMONPORTS P0 //定義輸出端口
sbit BeepIO=P1^5; //定義輸出管腳
sbit SRCLK=P3^6;
sbit RCLK1=P3^5;
sbit SER=P3^4;
sbit k1=P1^3; //定義按鍵
sbit k2=P1^2;
sbit k3=P1^1;
sbit k4=P1^0;
sbit button=P1^7;
unsigned char code music_tab[]={
33,16,4,33,8,2,34,8,2,39,16,4,26,8,2,29,8,2,33,8,2,33,8,2,33,8,2,34,8,2,
39,16,4,29,8,2,33,8,2,34,16,4,34,8,2,33,8,2,26,16,4,34,8,2,33,8,2,34,16,4,
11,8,2,26,8,2,29,16,4,26,8,2,29,8,2,33,16,4,33,8,2,34,8,2,39,16,4,44,8,2,
49,8,2,104,8,2,104,8,2,33,8,2,34,8,2,39,16,4,29,8,2,33,8,2,34,8,2,33,8,2,
34,8,2,104,8,2,104,16,4,29,8,2,33,8,2,34,8,2,33,8,2,34,8,2,117,8,2,
117,16,4,104,8,2,49,8,2,104,26,4,117,8,2,132,8,2,117,8,2,104,8,2,49,8,2,
104,26,4,104,8,2,49,8,2,39,8,2,39,8,2,26,8,2,44,26,4,44,8,2,39,8,2,26,8,2,
26,8,2,33,8,2,29,26,4,33,4,1,34,4,1,34,8,2,39,8,2,104,8,2,49,8,2,104,26,4,
117,8,2,132,8,2,117,8,2,104,8,2,49,8,2,104,26,4,104,8,2,49,8,2,39,8,2,
39,8,2,26,8,2,44,26,4,44,8,2,39,8,2,39,8,2,104,8,2,49,8,2,104,76,4,44,8,2,
44,8,2,44,8,2,49,8,2,49,8,2,44,8,2,39,8,2,39,16,4,33,8,2,26,5,5,26,8,2,
29,8,2,33,8,2,34,8,2,33,4,1,34,5,0,34,8,2,33,4,1,34,5,0,34,26,4,33,8,2,
34,8,2,33,8,2,34,8,2,117,8,2,117,16,4,104,8,2,49,8,2,104,26,4,117,8,2,
132,8,2,117,8,2,104,8,2,49,8,2,104,26,4,104,4,1,49,5,0,49,8,2,39,8,2,
39,8,2,26,8,2,44,26,4,44,8,2,39,8,2,26,8,2,26,8,2,33,8,2,29,26,4,33,4,1,
34,4,1,34,8,2,39,8,2,104,8,2,49,8,2,104,26,4,117,8,2,132,8,2,117,8,2,
104,8,2,49,8,2,104,26,4,104,8,2,49,8,2,39,8,2,39,8,2,26,8,2,44,26,4,
44,8,2,39,8,2,39,8,2,104,8,2,49,8,2,104,76,4,34,16,4,34,8,2,44,8,2,
39,10,0,26,8,2,26,16,4,34,26,4,29,16,4,33,16,4,26,76,4,0,0,0
}; //歌曲格式為{音調(diào),發(fā)音長(zhǎng)度,不發(fā)音長(zhǎng)度,...,0,0,0}
unsigned char code P0TAB[8] =
{0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe}; //分別為選中點(diǎn)陣1-8列
unsigned char code DIG_CODE[10][4]=
{
{0xf8,0x88,0xf8,0x00}, //0
{0x00,0x00,0xf8,0x00}, //1
{0xb8,0xa8,0xe8,0x00}, //2
{0xa8,0xa8,0xf8,0x00}, //3
{0xe0,0x20,0xf8,0x00}, //4
{0xe8,0xa8,0xb8,0x00}, //5
{0xf8,0xa8,0xb8,0x00}, //6
{0x80,0x80,0xf8,0x00}, //7
{0xf8,0xa8,0xf8,0x00}, //8
{0xe8,0xa8,0xf8,0x00}, //9
};
unsigned int Sound_Temp_TH0,Sound_Temp_TL0; //音符定時(shí)器初值暫存
unsigned int Sound_Temp_TH1,Sound_Temp_TL1; //音長(zhǎng)定時(shí)器初值暫存
void InitialSound(void);
void Hc595SendByte(unsigned char dat);
void Delay(unsigned int n);
void Play(unsigned char *Sound);
void ShowScore(unsigned int sum);
unsigned int sum;
void InitialSound(void)
{
button = 0;
BeepIO = 0;
TMOD |= 0x11;
Sound_Temp_TH1 = (65535-30000)/256; //計(jì)算TL1應(yīng)裝入的初值
Sound_Temp_TL1 = (65535-30000)%256; //計(jì)算TH1應(yīng)裝入的初值
ET0= 1;
ET1= 0;
TR0= 0;
TR1= 0;
EA= 1;
}
void ShowScore(unsigned int sum) //顯示得分
{
unsigned int tab,n;
while(1)
{
for(tab=0;tab<8;tab++)
{
Hc595SendByte(0x00); //消隱
COMMONPORTS = P0TAB[tab]; //輸出字碼
if(tab<4)
{
n=sum/10;
Hc595SendByte(DIG_CODE[n][tab]);
}
else
{
n=sum%10;
Hc595SendByte(DIG_CODE[n][tab-4]);
}
Delay(1);
}
}
}
void Delay(unsigned int n) //延時(shí)50us
{
unsigned char a,b;
for(;n>0;n--)
{
for(b=1;b>0;b--)
for(a=22;a>0;a--);
}
}
void BeepTimer0(void) interrupt 1 //音符發(fā)生中斷
{
BeepIO = !BeepIO;
TH0 = Sound_Temp_TH0;
TL0 = Sound_Temp_TL0;
}
void Hc595SendByte(unsigned char dat) //向74H595發(fā)送一個(gè)字節(jié)的數(shù)據(jù)
{
unsigned char a;
SRCLK=0;
RCLK1=0;
for(a=0;a<8;a++)
{
SER=dat>>7;
dat<<=1;
SRCLK=1;
_nop_();
_nop_();
SRCLK=0;
}
RCLK1=1;
_nop_();
_nop_();
RCLK1=0;
}
void Play(unsigned char *Sound) //演奏子程序
{
unsigned int HC595TAB[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
unsigned char i;
unsigned int tab,j,k,Point_led,Point_sound,LDiv,LDiv1,LDiv2,LDiv16,
LDiv16_temp,CurrentFre,LDiv1_sound,LDiv2_sound,LDiv1_temp,
CurrentFre_sound,Fre_Temp,Temp_T,SoundLength,score,count;
unsigned int temp[4];
SoundLength = 0;
LDiv16=5; //16分音符的長(zhǎng)度
while(Sound[SoundLength] != 0x00) //計(jì)算歌曲總長(zhǎng)度
{
SoundLength+=3;
}
LDiv1_temp=0;
LDiv16_temp=0;
Point_led=0;
Point_sound=0;
TR0=0;
TR1=1;
score=0;
sum=0;
j=1;
while(Point_sound < SoundLength) //一個(gè)音符一個(gè)音符的來(lái)
{
CurrentFre = Sound[Point_led];
LDiv1 = Sound[Point_led+1];
LDiv1_sound = Sound[Point_sound+1]; //發(fā)音長(zhǎng)度
LDiv2 = Sound[Point_led+2];
if(Sound[Point_led]!=0)
LDiv=LDiv1+LDiv2; //兩個(gè)LED燈之間的間隔長(zhǎng)度
else
LDiv=5;
if (Point_led!=0)
{
if (Fre_Temp>CurrentFre) //頻率比上個(gè)音符大,LED顯示的點(diǎn)右移兩行,比上個(gè)音符小則左移兩行,移到最左或最右循環(huán)
{
if(k==0) k=6;
else k=k-2;
}
else if (Fre_Temp0;i--) //間隔規(guī)定長(zhǎng)度顯示下一個(gè)led燈
{
LDiv16_temp++;
count++;
if(j!=1) //判斷得分
{
if(temp[j/2]!=0)
{
if(count==1){ score=5;P2=239;}
else if(count==2){score=4;P2=247;}
else if(count==3){score=3;P2=251;}
else if(count==4){score=2;P2=253;}
else if(count==5){score=1;P2=254;}
else{score=0;P2=255;}
sum=sum+score;
j=1;
}
}
if(LDiv16_temp==LDiv16) //每隔1個(gè)16分音符的時(shí)間
{
for(tab=0;tab<8;tab++)
{
HC595TAB[tab]>>=1; //led燈下降1格
if(HC595TAB[tab]&1 == 1) //判斷l(xiāng)ed燈是否下降到底部
{
CurrentFre_sound = Sound[Point_sound];
LDiv1_sound = Sound[Point_sound+1];
LDiv2_sound = Sound[Point_sound+2];
LDiv1_temp=0;
Temp_T = 65536-(5000/CurrentFre_sound)*10; //計(jì)算計(jì)數(shù)器初值
Sound_Temp_TH0 = Temp_T/256;
Sound_Temp_TL0 = Temp_T%256;
TH0 = Sound_Temp_TH0;
TL0 = Sound_Temp_TL0 + 12;
TR0=1;
temp[0]=temp[1]=temp[2]=temp[3]=0;
count=0;
P2=255;
j=tab;
Point_sound+=3; //發(fā)下一個(gè)音
if(Sound[Point_sound]==0)
i=LDiv1_sound+LDiv2_sound;
}
}
LDiv16_temp=0;
}
while(TF1==0)
{
for(tab=0;tab<8;tab++)
{
Hc595SendByte(0x00); //消隱 COMMONPORTS = P0TAB[tab]; //輸出字碼
Hc595SendByte(HC595TAB[tab]);
Delay(1);
if(k1==0) temp[0]=1; //判斷按鍵是否按下
else if(k2==0) temp[1]=1;
else if(k3==0) temp[2]=1;
else if(k4==0) temp[3]=1;
}
}
TH1 = Sound_Temp_TH1;
TL1 = Sound_Temp_TL1;
TF1=0;
LDiv1_temp++;
if (LDiv1_temp==LDiv1_sound)
TR0=0;
}
if(Sound[Point_led]!=0)
Point_led+=3; //顯示下個(gè)led燈
}
P2=255; //最后一個(gè)音結(jié)束熄滅led燈
}
main()
{
InitialSound();
Play(music_tab);
ShowScore(sum/9);
}
五、實(shí)物照片及調(diào)試結(jié)果
游戲時(shí)照片:
結(jié)束顯示分?jǐn)?shù)照片:
六、心得體會(huì)
這次單片機(jī)的課程設(shè)計(jì),也是我們第一次自己完成一個(gè)由自己構(gòu)思并實(shí)現(xiàn)的作品。在選擇方案的時(shí)候,我們首先決定的是自己編寫一款游戲。在聽完老師講解蜂鳴器播放音樂(lè)和LED點(diǎn)陣的原理后,我想到了以前玩過(guò)的一款名為節(jié)奏大師的音樂(lè)游戲,我發(fā)現(xiàn)利用LED點(diǎn)陣和蜂鳴器結(jié)合,完全可以在單片機(jī)上實(shí)現(xiàn)它的主要框架。再配合按鍵的功能,就能用單片機(jī)來(lái)玩這一簡(jiǎn)易的音樂(lè)游戲了。于是,我們便初步?jīng)Q定了這個(gè)題目。
然而,在制作的過(guò)程中,我們遇到了許多事先沒(méi)有料想到的困難。比如LED顯示點(diǎn)的出現(xiàn)和對(duì)應(yīng)的音符的不同步,按鍵的不同得分判定難以實(shí)現(xiàn),用于顯示分?jǐn)?shù)的數(shù)碼管和LED點(diǎn)陣共用了一個(gè)端口等等。這些困難都給我們編寫程序造成了很大的困擾,甚至令我們一度想要放棄。但在我們的努力下,最終還是一個(gè)個(gè)解決了這些困難,實(shí)現(xiàn)了我們?cè)镜臉?gòu)想。這也讓我體會(huì)到實(shí)踐才是檢驗(yàn)問(wèn)題最好的辦法。
通過(guò)這次課程設(shè)計(jì),我也對(duì)單片機(jī)的軟件、硬件有了進(jìn)一步的認(rèn)識(shí)。完成這一課程設(shè)計(jì),需要能熟練地運(yùn)用51單片機(jī)的C語(yǔ)言編程,以及單片機(jī)內(nèi)部的定時(shí)器、中斷等功能,也需要對(duì)LED、按鍵、蜂鳴器、數(shù)碼管等的原理進(jìn)行一定的了解。更重要的是,能夠?qū)⑦@兩者結(jié)合,才能讓各個(gè)模塊都能受我們控制,達(dá)到我們想要的效果。
這一次的實(shí)驗(yàn)作品的完成,讓我們充分體會(huì)到了學(xué)習(xí)是一個(gè)循序漸進(jìn)的過(guò)程。在課程開始之初,我們對(duì)于單片機(jī)一無(wú)所知,老師給與我們的開發(fā)箱也只不過(guò)是一個(gè)上課依葫蘆畫瓢,展現(xiàn)一下黑板上程序的工具。然而隨著課程的深入,我們可以不斷的運(yùn)用課本上新學(xué)到的知識(shí)已經(jīng)實(shí)驗(yàn)之中運(yùn)用過(guò)的程序和結(jié)構(gòu),來(lái)實(shí)現(xiàn)更多的功能以及想法。直到最后,我們?cè)陂_發(fā)箱上,依靠著這門課程給予我們的知識(shí)和技巧,完成了一個(gè)簡(jiǎn)易的音樂(lè)游戲。雖然游戲非常的簡(jiǎn)單,但是能夠設(shè)計(jì)出這樣一個(gè)游戲是我們?cè)陂_始完全沒(méi)有預(yù)想到的。
通過(guò)這門課程,我們第一次實(shí)際感受到了知識(shí)是如何運(yùn)用于實(shí)際的。在別的課程之中考核基本都是通過(guò)書面的考試,而這一次是讓我們自己在開發(fā)箱上創(chuàng)造自己的作品,讓我們受益匪淺。我們不僅僅需要用這一門課程上的知識(shí),許多之前學(xué)過(guò)的知識(shí)也再一次被我們從課本中找了回來(lái),運(yùn)用于其中。在這一次的期末作業(yè)考核之中,我們真正認(rèn)識(shí)到了之前學(xué)過(guò)的許多知識(shí)的作用,例如微機(jī)原理、C語(yǔ)言等等,這些東西不再是課本上的知識(shí),而變成了我們的一種能力,這是我們?cè)陂_發(fā)箱創(chuàng)造作品的過(guò)程之中最大的收獲。
另外,通過(guò)這次的作品,讓我感受到了團(tuán)隊(duì)協(xié)作的真諦。團(tuán)隊(duì)成員通過(guò)配合來(lái)?yè)P(yáng)長(zhǎng)避短,兩個(gè)人的知識(shí)和想法融合在一起,來(lái)更好地完成這次考核。我們的作品也從最初的一直空白,變成了一個(gè)簡(jiǎn)易但是很完整的音樂(lè)游戲。我們都從這次的創(chuàng)作中受益匪淺。
鏈接地址:http://www.3dchina-expo.com/p-6681979.html