一、课程设计内容 利用89c52单片机设计一款音乐游戏。 游戏说明:游戏基于单片机开发箱实现。游戏开始时,会播放一首音乐,同时每隔一个音符,LED点阵中会从最上方下落一个小点。玩家需要根据音乐的节奏,在小点下降到LED点阵的底部时,按下对应的按键。其中,矩阵键盘第一行从左到右的四个键分别对应LED点阵的1、3、5、7列。根据每个按键按下的时间,会有不同的得分,通过LED模块的D1-D5亮起来代表获得1-5分。如果没有LED灯亮则代表得0分。当整首歌播放完后,会在LED点阵上显示整首歌的得分,得分为0-99分。
二、设计思路和方案选择 在决定制作音乐游戏后,利用开发箱的哪几个模块来制作也是需要考虑的一个问题,最终我们决定利用现在的这个方案。其主要原因是LED点阵和蜂鸣器是游戏中的主体部分,因此无可替代。独立键盘还是矩阵键盘的选择上,经过试验发现独立键盘由于离LED点阵较远,在实际游戏中经常会有反应不过来的情况。而矩阵键盘就在点阵的正下方,因此在操作上较为方便,所以选择了矩阵键盘。而游戏得分的显示,最初是想利用数码管来实现,后来发现数码管和LED点阵都用到了单片机的P0口。在LED点阵无法被取代的情况下,只能退而求其次,选择用LED模块显示得分。 程序的具体设计思路如下: - 实现音乐的播放效果
- 实现LED点阵的显示下落效果
- 实现音乐的节奏和LED点的下落同步
- 实现按键得分功能
- 实现显示得分效果
三、硬件设计 本游戏的实现基于单片机开发试验仪,其主要用到的硬件电路有 89C52CPU模块,LED点阵模块,74HC595模块,LED模块,矩阵键盘模块和蜂鸣器模块。 四、软件框图及设计
程序流程图:
五、实物照片及调试结果 游戏时照片: 结束显示分数照片:
六、心得体会 这次单片机的课程设计,也是我们第一次自己完成一个由自己构思并实现的作品。在选择方案的时候,我们首先决定的是自己编写一款游戏。在听完老师讲解蜂鸣器播放音乐和LED点阵的原理后,我想到了以前玩过的一款名为节奏大师的音乐游戏,我发现利用LED点阵和蜂鸣器结合,完全可以在单片机上实现它的主要框架。再配合按键的功能,就能用单片机来玩这一简易的音乐游戏了。于是,我们便初步决定了这个题目。 然而,在制作的过程中,我们遇到了许多事先没有料想到的困难。比如LED显示点的出现和对应的音符的不同步,按键的不同得分判定难以实现,用于显示分数的数码管和LED点阵共用了一个端口等等。这些困难都给我们编写程序造成了很大的困扰,甚至令我们一度想要放弃。但在我们的努力下,最终还是一个个解决了这些困难,实现了我们原本的构想。这也让我体会到实践才是检验问题最好的办法。 通过这次课程设计,我也对单片机的软件、硬件有了进一步的认识。完成这一课程设计,需要能熟练地运用51单片机的C语言编程,以及单片机内部的定时器、中断等功能,也需要对LED、按键、蜂鸣器、数码管等的原理进行一定的了解。更重要的是,能够将这两者结合,才能让各个模块都能受我们控制,达到我们想要的效果。
这一次的实验作品的完成,让我们充分体会到了学习是一个循序渐进的过程。在课程开始之初,我们对于单片机一无所知,老师给与我们的开发箱也只不过是一个上课依葫芦画瓢,展现一下黑板上程序的工具。然而随着课程的深入,我们可以不断的运用课本上新学到的知识已经实验之中运用过的程序和结构,来实现更多的功能以及想法。直到最后,我们在开发箱上,依靠着这门课程给予我们的知识和技巧,完成了一个简易的音乐游戏。虽然游戏非常的简单,但是能够设计出这样一个游戏是我们在开始完全没有预想到的。
通过这门课程,我们第一次实际感受到了知识是如何运用于实际的。在别的课程之中考核基本都是通过书面的考试,而这一次是让我们自己在开发箱上创造自己的作品,让我们受益匪浅。我们不仅仅需要用这一门课程上的知识,许多之前学过的知识也再一次被我们从课本中找了回来,运用于其中。在这一次的期末作业考核之中,我们真正认识到了之前学过的许多知识的作用,例如微机原理、C语言等等,这些东西不再是课本上的知识,而变成了我们的一种能力,这是我们在开发箱创造作品的过程之中最大的收获。
另外,通过这次的作品,让我感受到了团队协作的真谛。团队成员通过配合来扬长避短,两个人的知识和想法融合在一起,来更好地完成这次考核。我们的作品也从最初的一直空白,变成了一个简易但是很完整的音乐游戏。我们都从这次的创作中受益匪浅。
单片机源程序如下: - #include <REG52.H>
- #include <INTRINS.H>
- #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
- }; //歌曲格式为{音调,发音长度,不发音长度,...,0,0,0}
- unsigned char code P0TAB[8] =
- {0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe}; //分别为选中点阵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; //音符定时器初值暂存
- unsigned int Sound_Temp_TH1,Sound_Temp_TL1; //音长定时器初值暂存
- 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; //计算TL1应装入的初值
- Sound_Temp_TL1 = (65535-30000)%256; //计算TH1应装入的初值
- 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) //延时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 //音符发生中断
- {
- BeepIO = !BeepIO;
- TH0 = Sound_Temp_TH0;
- TL0 = Sound_Temp_TL0;
- }
-
- void Hc595SendByte(unsigned char dat) //向74H595发送一个字节的数据
- {
- 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分音符的长度
- while(Sound[SoundLength] != 0x00) //计算歌曲总长度
- {
- 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) //一个音符一个音符的来
- {
- CurrentFre = Sound[Point_led];
- LDiv1 = Sound[Point_led+1];
- LDiv1_sound = Sound[Point_sound+1]; //发音长度
- LDiv2 = Sound[Point_led+2];
- if(Sound[Point_led]!=0)
- LDiv=LDiv1+LDiv2; //两个LED灯之间的间隔长度
- else
- LDiv=5;
- if (Point_led!=0)
- {
- if (Fre_Temp>CurrentFre) //频率比上个音符大,LED显示的点右移两行,比上个音符小则左移两行,移到最左或最右循环
- {
- if(k==0) k=6;
- else k=k-2;
- }
- else if (Fre_Temp<CurrentFre)
- {
- if(k==6) k=0;
- else k=k+2;
- }
- }
- Fre_Temp= CurrentFre;
- if(Sound[Point_led]!=0)
- HC595TAB[k]|=0x80; //显示led灯在最上方
- for(i=LDiv;i>0;i--) //间隔规定长度显示下一个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个16分音符的时间
- {
- for(tab=0;tab<8;tab++)
- {
- HC595TAB[tab]>>=1; //led灯下降1格
- if(HC595TAB[tab]&1 == 1) //判断led灯是否下降到底部
- {
- 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; //计算计数器初值
- 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; //发下一个音
- if(Sound[Point_sound]==0)
- i=LDiv1_sound+LDiv2_sound;
- }
- }
- LDiv16_temp=0;
- }
- while(TF1==0)
- {
- ……………………
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
全部资料51hei下载地址: |