一、課程設計內容 利用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下載地址:
|