当前位置首页 > 办公文档 > 其它办公相关文档
搜柄,搜必应! 快速导航 | 使用教程  [会员中心]

单片机课程设计报告俄罗斯方块

文档格式:DOC| 49 页|大小 657.02KB|积分 10|2022-04-21 发布|文档ID:78124189
第1页
下载文档到电脑,查找使用更方便 还剩页未读,继续阅读>>
1 / 49
此文档下载收益归作者所有 下载文档
  • 版权提示
  • 文本预览
  • 常见问题
  • 《单片机原理及应用》课程设计报告基于MCS-51单片机的俄罗斯方块游戏设计学 号 U200911880 姓 名 杨 欣 同 组 成 员 黄 涛 专 业 电气工程及其自动化 班 级 0905 班 指 导 教 师 黄 劲 院(系、所) 电气与电子工程学院 华中科技大学教务处制目录一、课程设计的目的 4二、题目要求 4三、原理概述 4四、调试过程 4五、验收结果 4六、个人体会 10七、参考文献 10八、致谢 10附录一:外电路原理图 11附录二:程序清单 12一、课程设计的目的1. 加深对单片机的理解,熟悉单片机的原理,掌握单片机的编程方法与技巧2. 灵活运用单片机的基础知识,增强利用理论知识解决实际问题的能力二、题目要求 1.验收时,先在仿真机的LED上显示自己学号的后6位,再进行相应内容验收 2.能在单片机实验箱通用板上的液晶显示屏(LCD)上玩俄罗斯方块游戏,使用4*4键盘输入,能够完美实现俄罗斯方块的基本游戏内容,界面优美,最后可以显示分数和级别,如果游戏失败显示“GAME OVER”,最后显示本人姓名“YANG XIN”和学号“200911880”。

    三、 原理概述1. 程序整体思路单片机上的程序设计一般是一个大循环结构,对于俄罗斯方块的程序设计,首先产生一个伪随机数,其范围是0-6,分别对应俄罗斯方块中随机产生的七种方块,然后程序根据此数值所对应的图形模块装入RAM的固定区域内,紧接着将此图像写入LCD所对应的显示缓冲区中,显示程序将缓冲区内的内容显示在显示屏上,如果没有控制键按下,图形将自动向下移动如果有键按下,程序将根据按下的键来改变图形存储区的值,同时程序将判断图形是否已到达边界,当图形最上层到达显示区顶部,则游戏结束,此时将清除显示缓冲的内容,游戏重新开始2.图形显示 QH12864T液晶显示器一共有128*64个像素点,本程序中每个像素点用一个坐标表示,左上角为(0,0),右下角为(128,64)对于显示类的基本操作是任意点亮一个点,熄灭一个点,任意点亮n个点,灭掉n个点,如此一来游戏的第一个关键点就完成了 我们将RAM中选取一段为显示缓冲区,这里面的每一位对应LCD上的的一个点,LCD将实时显示缓冲区的内容,也就是当此缓冲区内容一改变,LCD上看到的也会做出相应的改变同时游戏中的那些方块被存放在一个类似于一维的数组之中,每一个方块对应四个小方块,根据伪随机数将这四个小方块对应的值装入RAM固定的区域内。

    如果要显示方块只需将这些方块所对应的点写入缓冲区就可以了3.消层算法 设置循环变量数R2=128,即总共要扫描128次,每一次循环过程中检测此行是否已满,也就是检测这一行所对应的存储单元是否全为1,如果全为1则将此行消去,即将此行对应的RAM清零同时将此行上面RAM的内容按每列依次下移,下移完之后重新令R2=0,又从第一行开始检测此行不全为零时只需令R2=R2+1,直到R2=128消层过程结束4.旋转算法首先要确定一个旋转中心,假如旋转中心的编号是n,将n除以64,商是m存入R1余数是l存入R2,及时用商和余数建立坐标系此时若以此为旋转中心的图形需要旋转,则将每一个小方块对应编号除以64,商减去R1,余数减去R2,而每一个方块一次将旋转90度,所以有公式x1,y1,x2,y2 , x2= - y1,y2= x1,其中x1,y1为旋转之前的坐标,x2,y2为旋转之后的坐标旋转完之后将x2加上R1将y2加上R2,然后将x2乘以64再加上y2执行四次,此过程旋转结束四、 调试过程1、学号显示部分单独调试 这个在之前的很多实验中都有涉及,程序较为简单,语句也不复杂,本实验中才用的是并行动态显示,调试一次成功。

    2、 游戏程序调试相关五、 验收结果下图为当时的整个实验装置:实验中,游戏随机产生方块按下控制键,方块旋转:按下控制键,方块加速下落方块下落到底部,最底层被填满:最底层填满后,自动消去这一层:随机产生下一个方块,每次产生方块完全随机:最后一幅图为游戏结束后,显示所得分数,每消去一行得到一分,显示“GAME OVER”和游戏者姓名学号本次实验默认游戏者为本人YANG XIN(杨欣),学号(U)200911880六、个人体会 在这次实验过程中,特别是调试的时候,如果能够顺利排除BUG实现程序预定功能是最让人开心的,这个过程也会让我很享受但是如果由于一些搞不清楚的原因迟迟达不到目标,也是最让人烦躁的而其中的问题,只能漫无目的地去尝试,无法在逻辑中得到答案,我个人遇到了这种状况,确实很无奈不知道是否是因为我的分析不到位,而实际上有一个合乎逻辑的答案在那里呢?这次课程设计,第一周主要是在修改程序的驱动部分,从第二周开始上机调试,用了差不多半个月的时间,最终也没有成功调试出结果,实在很受打击,让我很沮丧从单片机课程到单片机实验到如今的单片机课程设计,我感触最深的一点就是:必须有耐心、足够细心。

    单片机程序前前后后有不少联系,可能前面用的的某个变量跟后面重复、字与字节搞乱等等细小的问题,如果不能耐心、细心地去寻找,很难发现最后,我还体会到了,硬件学习是编写程序前最重要的部分,只有对所使用的硬件设备完全了解才能编写出适合使用的程序,否则会做很多无用功,浪费很多时间和精力以后一定要谨记这个道理!七、主要参考文献【1】《单片机原理及应用》 姜志海主编【2】《MCS-51系列单片机原理及应用实验指导书》 八、致谢感谢黄劲老师的耐心指导!感谢管理实验室的老师多日来为我们开放实验室!附录一:电路原理图按键与单片机连接:按照4*4键盘上的布置,游戏控制键分别为:Move_Left 8 ,Move_Right A,Add_Speed 9,Change_Shape D,Game_Star C,Game_Pause E液晶显示器与单片机的连接:附录二:程序清单:(因程序繁多,此处只列出.C文件程序,如果老师要看完成程序,请看电子档报告)1. STARPUP.C$NOMOD51;------------------------------------------------------------------------------; This file is part of the C51 Compiler package; Copyright (c) 1988-2005 Keil Elektronik GmbH and Keil Software, Inc.; Version 8.01;; *** <<< Use Configuration Wizard in Context Menu >>> ***;------------------------------------------------------------------------------; STARTUP.A51: This code is executed after processor reset.;; To translate this file use A51 with the following invocation:;; A51 STARTUP.A51;; To link the modified STARTUP.OBJ file to your application use the following; Lx51 invocation:;; Lx51 your object file list, STARTUP.OBJ controls;;------------------------------------------------------------------------------;; User-defined Power-On Initialization of Memory;; With the following EQU statements the initialization of memory; at processor reset can be defined:;; IDATALEN: IDATA memory size <0x0-0x100>; Note: The absolute start-address of IDATA memory is always 0; The IDATA space overlaps physically the DATA and BIT areas.IDATALEN EQU 80H;; XDATASTART: XDATA memory start address <0x0-0xFFFF> ; The absolute start address of XDATA memoryXDATASTART EQU 0 ;; XDATALEN: XDATA memory size <0x0-0xFFFF> ; The length of XDATA memory in bytes.XDATALEN EQU 0 ;; PDATASTART: PDATA memory start address <0x0-0xFFFF> ; The absolute start address of PDATA memoryPDATASTART EQU 0H;; PDATALEN: PDATA memory size <0x0-0xFF> ; The length of PDATA memory in bytes.PDATALEN EQU 0H;;;------------------------------------------------------------------------------;; Reentrant Stack Initialization;; The following EQU statements define the stack pointer for reentrant; functions and initialized it:;; Stack Space for reentrant functions in the SMALL model.; IBPSTACK: Enable SMALL model reentrant stack; Stack space for reentrant functions in the SMALL model.IBPSTACK EQU 0 ; set to 1 if small reentrant is used.; IBPSTACKTOP: End address of SMALL model stack <0x0-0xFF>; Set the top of the stack to the highest location.IBPSTACKTOP EQU 0xFF +1 ; default 0FFH+1 ; ;; Stack Space for reentrant functions in the LARGE model. ; XBPSTACK: Enable LARGE model reentrant stack; Stack space for reentrant functions in the LARGE model.XBPSTACK EQU 0 ; set to 1 if large reentrant is used.; XBPSTACKTOP: End address of LARGE model stack <0x0-0xFFFF>; Set the top of the stack to the highest location.XBPSTACKTOP EQU 0xFFFF +1 ; default 0FFFFH+1 ; ;; Stack Space for reentrant functions in the COMPACT model. ; PBPSTACK: Enable COMPACT model reentrant stack; Stack space for reentrant functions in the COMPACT model.PBPSTACK EQU 0 ; set to 1 if compact reentrant is used.;; PBPSTACKTOP: End address of COMPACT model stack <0x0-0xFFFF>; Set the top of the stack to the highest location.PBPSTACKTOP EQU 0xFF +1 ; default 0FFH+1 ; ;;------------------------------------------------------------------------------;; Memory Page for Using the Compact Model with 64 KByte xdata RAM; Compact Model Page Definition;; Define the XDATA page used for PDATA variables. ; PPAGE must conform with the PPAGE set in the linker invocation.;; Enable pdata memory page initalizationPPAGEENABLE EQU 0 ; set to 1 if pdata object are used.;; PPAGE number <0x0-0xFF> ; uppermost 256-byte address of the page used for PDATA variables.PPAGE EQU 0;; SFR address which supplies uppermost address byte <0x0-0xFF> ; most 8051 variants use P2 as uppermost address bytePPAGE_SFR DATA 0A0H;; ;------------------------------------------------------------------------------; Standard SFR Symbols ACC DATA 0E0HB DATA 0F0HSP DATA 81HDPL DATA 82HDPH DATA 83H NAME ?C_STARTUP?C_C51STARTUP SEGMENT CODE?STACK SEGMENT IDATA RSEG ?STACK DS 1 EXTRN CODE (?C_START) PUBLIC ?C_STARTUP CSEG AT 0?C_STARTUP: LJMP STARTUP1 RSEG ?C_C51STARTUPSTARTUP1:IF IDATALEN <> 0 MOV R0,#IDATALEN - 1 CLR AIDATALOOP: MOV @R0,A DJNZ R0,IDATALOOPENDIFIF XDATALEN <> 0 MOV DPTR,#XDATASTART MOV R7,#LOW (XDATALEN) IF (LOW (XDATALEN)) <> 0 MOV R6,#(HIGH (XDATALEN)) +1 ELSE MOV R6,#HIGH (XDATALEN) ENDIF CLR AXDATALOOP: MOVX @DPTR,A INC DPTR DJNZ R7,XDATALOOP DJNZ R6,XDATALOOPENDIFIF PPAGEENABLE <> 0 MOV PPAGE_SFR,#PPAGEENDIFIF PDATALEN <> 0 MOV R0,#LOW (PDATASTART) MOV R7,#LOW (PDATALEN) CLR APDATALOOP: MOVX @R0,A INC R0 DJNZ R7,PDATALOOPENDIFIF IBPSTACK <> 0EXTRN DATA (?C_IBP) MOV ?C_IBP,#LOW IBPSTACKTOPENDIFIF XBPSTACK <> 0EXTRN DATA (?C_XBP) MOV ?C_XBP,#HIGH XBPSTACKTOP MOV ?C_XBP+1,#LOW XBPSTACKTOPENDIFIF PBPSTACK <> 0EXTRN DATA (?C_PBP) MOV ?C_PBP,#LOW PBPSTACKTOPENDIF MOV SP,#?STACK-1; This code is required if you use L51_BANK.A51 with Banking Mode 4; Code Banking; Select Bank 0 for L51_BANK.A51 Mode 4#if 0 ; Initialize bank mechanism to code bank 0 when using L51_BANK.A51 with Banking Mode 4.EXTRN CODE (?B_SWITCH0) CALL ?B_SWITCH0 ; init bank mechanism to code bank 0#endif; LJMP ?C_START END2. Fangkuai.c#include "t6963c.h"#include "typedef.h"#include "key.h"#define X_START 5#define Y_START 0#define MIN_SLOW_SPEED 300#define BX_START 30#define BY_START 15#define Nothing 100//全局数据code uchar Game_Char[]={0x27,0x41,0x4d,0x45};code uchar Over_Char[]={0x2f,0x56,0x45,0x52}; static uchar xx,yy; //方块的位置static uint Game_Score=0;static uchar xdata Platform[14][21]; //游戏平台数据static uchar This_shape; //当前形状static uchar Next_shape=0;static uint Game_Speed=MIN_SLOW_SPEED; //等级速度,正常情况,方块下降的速度static uchar Game_Stop=1;static uchar Game_Level=0;///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////方块形状的定义///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////struct POINT{ uchar x; uchar y; };struct SHAPE{ struct POINT point[4]; uchar next; //下一个形状 } xdata shape[19]={ { 1,0,0,1,1,1,2,1,1 }, { 1,0,1,1,2,1,1,2,2 }, { 0,0,1,0,2,0,1,1,3 }, { 1,0,0,1,1,1,1,2,0 }, { 1,0,2,0,1,1,1,2,5 }, { 0,0,1,0,2,0,2,1,6 }, { 2,0,2,1,2,2,1,2,7 }, { 0,0,0,1,1,1,2,1,4 }, { 1,0,2,0,2,1,2,2,9 }, { 2,0,0,1,1,1,2,1,10 }, { 1,0,1,1,1,2,2,2,11 }, { 0,0,1,0,2,0,0,1,8 }, { 0,0,0,1,1,1,1,2,13 }, { 1,0,2,0,0,1,1,1,12 }, { 2,0,1,1,2,1,1,2,15 }, { 0,0,1,0,1,1,2,1,14 }, { 1,0,1,1,1,2,1,3,17 }, { 0,1,1,1,2,1,3,1,16 }, { 1,0,2,0,1,1,2,1,18 }, }; //////////////////////////////////////////////////////////////////////////////**************************************************************************//= 函数原型:void Init_GamePlatform()//= 功 能: 初始化游戏平台//= 参 数: 无 //= 返 回 值: 无//= 函数性质:公有函数//= 注 意://*************************************************************************** void Show_score(uchar);void Init_GamePlatform(){ uchar i; uchar j;// uchar N_Hanzi; Wr_line(1,33,13,64,1 ); //初始化游戏平台边界 画游戏区域 Wr_line(1,33,14,64,1 ); //画上横线 Wr_line(0,33,15,100,1); Wr_line(0,34,15,100,1); //画左竖线 Wr_line(1,33,115,64,1); Wr_line(1,33,116,64,1); //画下横线 Wr_line(0,95,15,100,1); Wr_line(0,96,15,100,1); //画右竖线//-------------------------------------------------------- for(i=1;i<13;i++) //游戏平台数据清零 { for(j=0;j<20;j++) { Platform[i][j]=0; } } for(i=1;i<13;i++) { Platform[i][20]=1; //游戏平台最下面一行的每一个方块位置为1,作为下边界 } for(j=0;j<20;j++) //游戏平台左右方块位置置1,作为左右边界 { Platform[0][j]=1; Platform[13][j]=1; }//--------------------------------------------------------- hanzhi(13,3,6,1); //输入“分”汉字 hanzhi(13,8,8,1); //输入“级”汉字 Show_score(0); //显示初始分数 Show_num(13,11,Game_Level); //显示初始等级水平 Game_Speed=MIN_SLOW_SPEED/(Game_Level+1); //根据水平确定速度//------------------------------------------------------------------ Game_Score=0; xx=X_START ; yy=Y_START ;}////////////////////////Init_Game/////////////////////void Init_Game(){ Game_Stop=1; Init_GamePlatform(); hanzhi(7,3,9,1); //输入“按”汉字 Show_num(7,6,7); //显示7 hanzhi(7,8,10,1); //输入“开”汉字 hanzhi(7,10,11,1); //输入“始”汉字}//**************************************************************************//= 函数原型:void XiaoFengKuai(uchar x,uchar y,uchar mode)//= 功 能: 显示一个小方块//= 参 数: 小方块的横x,坚坐标y,mode=1:显示小方块,mode=0:删除小方块 //= 返 回 值://= 函数性质:私有函数//= 注 意://*************************************************************************** void XiaoFengKuai(uchar x,uchar y,bit mode){ uchar x1=5*x+BX_START; //将方块在平台的位置转化成LCD的点坐标(地址转换) uchar y1=5*y+BY_START; uchar i; if(mode==1) { for(i=0;i<5;i++) { Point(x1+i,y1,1); //画一条横线 } y1+=4; for(i=0;i<5;i++) { Point(x1+i,y1,1); //画第二条横线 } for(i=0;i<5;i++) { Point(x1,y1-i,1); //画第1条坚线 } x1+=4; for(i=0;i<5;i++) { Point(x1,y1-i,1); //画第2条坚线 } y1-=4; for(i=0;i<5;i++) { Point(x1-i,y1+i,1); //画斜线 } } else { for(i=0;i<5;i++) { Point(x1+i,y1,0); //画一条横线 } y1+=4; for(i=0;i<5;i++) { Point(x1+i,y1,0); //画第二条横线 } for(i=0;i<5;i++) { Point(x1,y1-i,0); //画第1条坚线 } x1+=4; for(i=0;i<5;i++) { Point(x1,y1-i,0); //画第2条坚线 } y1-=4; for(i=0;i<5;i++) { Point(x1-i,y1+i,0); //画斜线 } }}//////////////////////////////////////////////////////////////////////////////////////////左冲突检测//////////////////////////////////////////////bit Left_Anti(){ uchar i; for(i=0;i<4;i++) { if(Platform[xx+shape[This_shape].point[i].x-1][yy+shape[This_shape].point[i].y]==1) return 1; } return 0;} ////////////////右冲突检测///////////////////////////////////////////bit Right_Anti(){ uchar i; for(i=0;i<4;i++) { if(Platform[xx+shape[This_shape].point[i].x+1][yy+shape[This_shape].point[i].y]==1) return 1; } return 0; }////////////////////////////////////////下冲突检测//////////////////////////bit Bottom_Anti(){ uchar i; for(i=0;i<4;i++) { if(Platform[xx+shape[This_shape].point[i].x][yy+shape[This_shape].point[i].y+1]==1) return 1; } return 0; }//////////////////////////////////改变形状时产生的冲突检测////////////////////bit Change_Shape_Anti(){ uchar i; for(i=0;i<4;i++) { if(Platform[xx+shape[shape[This_shape].next].point[i].x][yy+shape[shape[This_shape].next].point[i].y]==1) return 1; //检测一个形状的冲突情况 } return 0;}//////////////////////////////////产生一个随机数,返回一个随机数///////////////uchar Random(){ static uchar m; m+=49; return (m%19);}//////////////计分函数,参数为 消行行数n///////////////////////////////////////void Show_score(uchar n){ Game_Score=Game_Score+10*n; if(Game_Score<10) { Show_num(13,6,Game_Score%10); //显示个位 } else if(Game_Score<100) { Show_num(14,6,Game_Score%10); Show_num(13,6,Game_Score/10%100); //显示个位,十位 } else if(Game_Score<1000) { Show_num(14,6,Game_Score%10); Show_num(13,6,Game_Score/10%10); Show_num(12,6,Game_Score/100%10); //显示个位 ,十位,百位 } else { Show_num(15,6,Game_Score%10); Show_num(14,6,Game_Score/10%10); Show_num(13,6,Game_Score/100%10); //显示个位 ,十位,百位,千位 Show_num(12,6,Game_Score/1000); } if(Game_Score%1000==0) { if(Game_Score>0) { Game_Level++; if(Game_Level==10) { Game_Stop=1; hanzhi(7,5,12,1); // 输出“太棒了" hanzhi(7,7,13,1); hanzhi(7,9,14,1); } Show_num(13,11,Game_Level); //显示水平 } }}//**************************************************************************//= 函数原型:void Undisplay_line()//= 功 能: 消除行//= 参 数: 无 //= 返 回 值: 无//= 函数性质:私有函数//= 注 意://*************************************************************************** void UnDisplay_line(){ uchar Del_Line; //标识要删除的行 uchar Del_Line_Num=0; //标识删除的行数 uchar i,j,k; bit HavePoint; //标识一行中是否有空白点 for(i=0;i<4;i++) { for(j=1;j<13;j++) { if(Platform[j][yy+i]==0) break; //如果这一行中有一个为空,则退出这一行的循环 else if(j==12) { Del_Line=yy+i; //确定要删除的行 if(Del_Line<20) { Del_Line_Num++; //计算共删除的行数 for(k=1;k<13;k++) { XiaoFengKuai(k,Del_Line,0); //删除行 Platform[k][Del_Line]=0; //平台数据清零 } while(1) //下移 { HavePoint=0; for(k=1;k<13;k++) { if(Platform[k][Del_Line-1]==1) { HavePoint=1; //标识这一行有要下移的点 XiaoFengKuai(k,Del_Line-1,0); //删除小方块 Platform[k][Del_Line-1]=0; //平台数据清零 XiaoFengKuai(k,Del_Line,1) ; //将小方块下移 Platform[k][Del_Line]=1; //平台数据置1,表明此位置已被占用 } } if(HavePoint==0) break; //没有要下移的行,退出本循环 Del_Line--; //下移上一行 } } } } } if(Del_Line_Num) { Show_score(Del_Line_Num); //刷新分数显示 }} //**************************************************************************//= 函数原型:void Show_shape(uchar x1,uchar y1,uchar Tshape,bit mode)//= 功 能: 显示一个方块形状或删除一个方块形状//= 参 数: (x1,y1)为显示位置,Tshape为显示的形状,mode=1为显示,mode=0不显示 //= 返 回 值://= 函数性质:私有函数//= 注 意://*************************************************************************** void Show_shape(uchar x1,uchar y1,uchar Tshape,bit mode){ if(mode==1) { XiaoFengKuai(x1+shape[Tshape].point[0].x,y1+shape[Tshape].point[0].y,1); //显示形状 XiaoFengKuai(x1+shape[Tshape].point[1].x,y1+shape[Tshape].point[1].y,1); XiaoFengKuai(x1+shape[Tshape].point[2].x,y1+shape[Tshape].point[2].y,1); XiaoFengKuai(x1+shape[Tshape].point[3].x,y1+shape[Tshape].point[3].y,1); } else { XiaoFengKuai(x1+shape[Tshape].point[0].x,y1+shape[Tshape].point[0].y,0); //删除形状 XiaoFengKuai(x1+shape[Tshape].point[1].x,y1+shape[Tshape].point[1].y,0); XiaoFengKuai(x1+shape[Tshape].point[2].x,y1+shape[Tshape].point[2].y,0); XiaoFengKuai(x1+shape[Tshape].point[3].x,y1+shape[Tshape].point[3].y,0); }}//**************************************************************************//= 函数原型:void Fangkuai_down()//= 功 能: 方块下降处理//= 参 数: //= 返 回 值://= 函数性质:公有函数//= 注 意://*************************************************************************** static uint DSpeed=MIN_SLOW_SPEED; //标识下降速度static uint Now_Speed=MIN_SLOW_SPEED; //当前速度void Fangkuai_down(){ uchar i; static bit New_shape=1; //标识是否要产生新形状 if(Game_Stop==1) return; if(New_shape==1) { New_shape=0; xx=X_START; yy=Y_START; This_shape=Next_shape; //当前方块等于预方块 Show_shape(15,18,Next_shape,0); // 产生一下个方块前,将预方块删除 Next_shape=Random(); //产生下一个方块 Show_shape(xx,yy,This_shape,1); //显示当前方块 Show_shape(15,18,Next_shape,1); //预显示下一个方块 if(Bottom_Anti()) { Game_Stop=1; Show_Image(35,15,9。

    点击阅读更多内容