实验八 自主创新实验(打地鼠小游戏)

本实验实现了一个打地鼠小游戏。

电路连接

代码

//ch451.h
#ifndef __CH451_H__
#define __CH451_H__
#include<reg51.h>
#define CH451_RESET     0x0201                           //复位
#define CH451_LEFTMOV   0x0300		            //设置移动方式-作移
#define CH451_LEFTCYC   0x0301		            //设置移动方式-左循
#define CH451_RIGHTMOV  0x0302		            //设置移动方式-右移
#define CH451_RIGHTCYC  0x0303		            //设置移动方式-右循	
#define CH451_SYSOFF    0x0400                      //关显示、键盘、看门狗
#define CH451_SYSON1    0x0401                      //开显示
#define CH451_SYSON2    0x0403                      //开显示、键盘
#define CH451_SYSON3    0x0407                      //开显示、键盘、看门狗功能
#define CH451_DSP       0x0500                      //设置默认显示方式
#define CH451_BCD       0x0580                      //设置BCD译码方式
#define CH451_TWINKLE   0x0600		            //设置闪烁控制
#define CH451_DIG0      0x0800                      //数码管位0显示
#define CH451_DIG1      0x0900		            //数码管位1显示 
#define CH451_DIG2      0x0a00		            //数码管位2显示
#define CH451_DIG3      0x0b00		            //数码管位3显示
#define CH451_DIG4      0x0c00		            //数码管位4显示
#define CH451_DIG5      0x0d00	                    //数码管位5显示	
#define CH451_DIG6      0x0e00	                    //数码管位6显示	
#define CH451_DIG7      0x0f00		            //数码管位7显示
 
#define  USE_KEY  1//如果使用键盘中断请定义
sbit ch451_load = P1^3;  //串行命令加载,上升延激活
sbit ch451_din = P1^1;   // 串行数据输出,接CH451的数据输入
sbit ch451_dclk = P1^2;  //串行数据时钟上升延激活
sbit ch451_dout = P3^2;  //INT0对应P3.2,键盘中断和键值数据输入,接CH451的数据输出
extern unsigned char  ch451_key;// 全局变量,存放键盘中断中读取的键值
void ch451_init();
void ch451_write(unsigned int command);
unsigned char ch451_read();
void ch451_inter();

#endif 
//ch451.c
#include "ch451.h"

//初始化子程序

void ch451_init()
{
  ch451_din=0;                                     //先低后高,选择4线输入
  ch451_din=1;
#ifdef USE_KEY
  IT1=0;			                  //设置下降沿触发
  IE1=0;			                  //清中断标志
  PX1=0;			                  //设置低优先级
  EX1=1;			                  //开中断
#endif

}
//*****************************************************

 //输出命令子程序
 //定义一无符号整型变量存储12字节的命令字。
 void ch451_write(unsigned int command)
{
  unsigned char i;
#ifdef USE_KEY
  EX1=0;			                 //禁止键盘中断
#endif	
  ch451_load=0;                                  //命令开始       
  for(i=0;i<12;i++){                             //送入12位数据,低位在前
    ch451_din=command&1;				
    ch451_dclk=0;
    command>>=1;
    ch451_dclk=1;                               //上升沿有效
  }
  ch451_load=1;                                 //加载数据
#ifdef USE_KEY
  EX1=1;
#endif
}

#ifdef USE_KEY

//*************************************************
//输入命令子程序,MCU从451读一字节
 unsigned char ch451_read()
{
  unsigned char i;
  unsigned char command,keycode;		//定义命令字,和数据存储器
  EX1=0;			               //关中段	
  command=0x07;			               //输入读451命令字	
  ch451_load=0;
  for(i=0;i<4;i++){
 
    ch451_din=command&1;		      //送入最低位
    ch451_dclk=0;			 
    command>>=1;			      //往右移一位
    ch451_dclk=1;			      //产生时钟上升沿锁通知CH451输入位数据
 }
  ch451_load=1;				      //产生加载上升沿通知CH451处理命令数据
  keycode=0;				      //清除keycode
  for(i=0;i<7;i++){
    keycode<<=1;			      //数据移入keycode,高位在前,低位在后
    keycode|=ch451_dout;		      //从高到低读入451的数据
    ch451_dclk=0;			      //产生时钟下升沿通知CH451输出下一位
    ch451_dclk=1;
 }
  IE1=0;				      //清中断标志
  EX1=1;
  return(keycode);			     //反回键值
}
//*************************************************
//中断子程序  使用中断0

void ch451_inter() interrupt 0//外部中断0(INT0)对应interrupt 0 ,外部中断1(INT1)对应interrupt 2 
{
  unsigned char i;			      //定义循环变量
  unsigned char command,keycode;	      //定义控制字寄存器,和中间变量定时器
  command=0x07;				      //读取键值命令的高4位0111B
  ch451_load=0;				      //命令开始
  for(i=0;i<4;i++){
    ch451_din=command&1;		     //低位在前,高位在后
    ch451_dclk=0;
    command>>=1;			     //右移一位
    ch451_dclk=1;			     //产生时钟上升沿锁通知CH451输入位数据
 }
  ch451_load=1;				     //产生加载上升沿通知CH451处理命令数据
  keycode=0;				     //清除keycode
  for(i=0;i<7;i++){
    keycode<<=1;			     //数据作移一位,高位在前,低位在后
    keycode|=ch451_dout;		     //从高到低读入451的数据
    ch451_dclk=0;			     //产生时钟下升沿通知CH451输出下一位
    ch451_dclk=1;
 }
  ch451_key=keycode;			    //保存上次的键值,读取的按键代码与按键键值的关系需要测试
  IE0=0;				    //清中断标志
}

//***********************************************

#endif 
//lcd12864.h
#ifndef   _LCD12864_h_
#define   _LCD12864_h_
/**************************************************************
               iO口宏定义区
***************************************************************/
sbit CS =P0^0;//对应实验箱上IO1 RS
sbit SID=P0^1;//对应实验箱上IO2 RW
sbit SCK=P0^2;//对应实验箱上IO3 E
 
void write_command( unsigned char Cbyte );   //写入指令函数
void write_data( unsigned char Dbyte );  //写入指令数据
void lcd_init( void );   //显示屏初始化
void lcd_clear_txt( void );  //显示屏清屏
void location_xy_12864(unsigned char x,unsigned char y);
void put_str(unsigned char row,unsigned char col,unsigned char *puts);
void put_char(unsigned char row,unsigned char col,unsigned char put);
void lcd_display_picture(unsigned char p[][16]);
#endif
//lcd12864.c
#include <reg51.h>
#include <intrins.h>
#include "lcd12864.h"
/**************************************************************
 
 
      //串行方式控制
/*******************************************************************
              常量声明区
********************************************************************/      
unsigned char code AC_TABLE[]={                   //坐标编码
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,
0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
};
/****************************************************************                        
              发送一个字节
*****************************************************************/
void send_byte(unsigned char dbyte)
{
  unsigned char i;
  for(i=0;i<8;i++)
  {
    SCK = 0;
    dbyte=dbyte<<1;
    SID = CY;
    SCK = 1;
    SCK = 0;
  }
}
/**********************************************************
              接收一个字节
***********************************************************/
 
unsigned char receive_byte(void)
{
  unsigned char i,temp1,temp2;
  temp1=temp2=0;
  for(i=0;i<8;i++)
  {
    temp1=temp1<<1;
    SCK = 0;
    SCK = 1;                
    SCK = 0;
    if(SID) temp1++;
  }
  for(i=0;i<8;i++)
  {
    temp2=temp2<<1;
    SCK = 0;
    SCK = 1;
    SCK = 0;
    if(SID) temp2++;
  }
  return ((0xf0&temp1)+(0x0f&temp2));
}
/****************************************************************
                      检查忙状态
******************************************************************/
void check_busy( void )
{
  do send_byte(0xfc);     //11111,RW(1),RS(0),0
  while(0x80&receive_byte());
}
 
/******************************************************************
           写一个字节的指令
*******************************************************************/
void write_command( unsigned char Cbyte )
{
  CS = 1;
  check_busy();
  send_byte(0xf8);          //11111,RW(0),RS(0),0
  send_byte(0xf0&Cbyte);
  send_byte(0xf0&Cbyte<<4);
  CS = 0;
}
/*************************************************************
                 写一个字节的数据
**************************************************************/
void write_data( unsigned char dbyte )
{
  CS = 1;
  check_busy();
  send_byte(0xfa);          //11111,RW(0),RS(1),0
  send_byte(0xf0&dbyte);
  send_byte(0xf0&dbyte<<4);
  CS = 0;
}
 
/******************************************************************
                         lcd初始化函数
*******************************************************************/
void lcd_init( void )
{
     write_command(0x30);
     write_command(0x03);
     write_command(0x0c);
     write_command(0x01);
     write_command(0x06);
}
 
/*******************************************************************************************************
                                 设定光标函数
********************************************************************************************************/
void location_xy_12864(unsigned char x,unsigned char y)
{
  switch(x)
  {
    case 0:
      x=0x80;break;
    case 1:
      x=0x90;break;
    case 2:
      x=0x88;break;
    case 3:
      x=0x98;break;
    default:
      x=0x80;
  }
  y=y&0x07;
  write_command(0x30);
  write_command(y+x);
  write_command(y+x);
 
}
/***********************************************************************************
                  清除文本
************************************************************************************/
void lcd_clear_txt( void )
{
  unsigned char i;
  write_command(0x30);
  write_command(0x80);
  for(i=0;i<64;i++)
  write_data(0x20);
  location_xy_12864(0,0);      
}
/****************************************************************************************
                      显示字符串
*****************************************************************************************/
void put_str(unsigned char row,unsigned char col,unsigned char *puts)
{    
  write_command(0x30);
  write_command(AC_TABLE[8*row+col]);
  while(*puts != '\0')
  {
    if(col==8)
    {
      col=0;
      row++;
    }
    if(row==4) row=0;
    write_command(AC_TABLE[8*row+col]);
    write_data(*puts);
    puts++;
    if(*puts != '\0')
    {
      write_data(*puts);
      puts++;
      col++;
    }  
  }
}

/****************************************************************************************
                      显示字符
*****************************************************************************************/
void put_char(unsigned char row,unsigned char col,unsigned char put)
{    
  write_command(0x30);
  write_command(AC_TABLE[8*row+col]);
  write_data(put);
}

/****************************************************************************************
                      显示图片
*****************************************************************************************/
void lcd_display_picture(unsigned char p[][16])
{
    unsigned char x,y,a,b,c;
    write_command(0x34);    // رջ ͼ    
    x = 0x80;
    y = 0x80;
    for(c=0;c<2;c++)//先画上半屏,再画下半屏
    {
         for(a=0;a<32;a++)
        {
            write_command(y+a);
            write_command(x);
            for(b=0;b<16;b++)
                write_data(p[a+c*32][b]);
        }
        x=0x88;
    }
    write_command(0x36);     // 򿪻 ͼ    
    write_command(0x30);    
}
//main.c
/********************************************************************************************************
打地鼠游戏
********************************************************************************************************/
#include < reg51.h >
#include < stdlib.h >
#include <stdio.h>
#include "ch451.h"
#include "lcd12864.h"

unsigned char key_value = 0; //存放按键值
unsigned char free_num= 0; //随机数的变量
unsigned char flag_1s;//达到1s的标志
char space[5]={0x20,0x20,0x20,0x20,'\0'};//空格串
int sec=20;//秒数
char s_sec[3];//将秒数转换成为字符串
int score=0;//分数
char s_score[5];//将分数转换成为字符串
enum GAME_STATE{PLAY,OVER};
enum GAME_STATE game_state=OVER;


/********************************************************************************************************
游戏开始时或者每次打死地鼠之后,需要刷新地鼠的位置
********************************************************************************************************/
void refresh() {
    //关闭敲死的地鼠
    put_str(0,0,"田"); 
    put_str(0,1,"田"); 
    put_str(0,2,"田"); 
    put_str(0,3,"田"); 
    put_str(1,0,"田"); 
    put_str(1,1,"田"); 
    put_str(1,2,"田"); 
    put_str(1,3,"田"); 
    put_str(2,0,"田"); 
    put_str(2,1,"田"); 
    put_str(2,2,"田"); 
    put_str(2,3,"田"); 
   
    switch(free_num){
        case 0:put_str(0,0,"鼠"); break;
        case 1:put_str(0,1,"鼠"); break;
        case 2:put_str(0,2,"鼠"); break;
        case 3:put_str(0,3,"鼠"); break;
        case 4:put_str(1,0,"鼠"); break;
        case 5:put_str(1,1,"鼠"); break;
        case 6:put_str(1,2,"鼠"); break;
		case 7:put_str(1,3,"鼠"); break;
        case 8:put_str(2,0,"鼠"); break;
        case 9:put_str(2,1,"鼠"); break;
        case 10:put_str(2,2,"鼠"); break;
		case 11:put_str(2,3,"鼠"); break;
        default:break;
    }
    
}


/********************************************************************************************************
游戏状态机
********************************************************************************************************/
void game_state_machine(unsigned char key){
    switch(game_state){
	    case OVER:
		    if(key==12){//开始键
			    game_state=PLAY;//修改状态为PLAY
				//每次开始后重新加载界面
                lcd_clear_txt();
				put_str(0,0,"田"); 
    			put_str(0,1,"田"); 
    			put_str(0,2,"田"); 
    			put_str(0,3,"田"); 
    			put_str(1,0,"田"); 
    			put_str(1,1,"田"); 
    			put_str(1,2,"田"); 
    			put_str(1,3,"田"); 
    			put_str(2,0,"田"); 
    			put_str(2,1,"田"); 
    			put_str(2,2,"田"); 
    			put_str(2,3,"田"); 
				put_str(3,0,"开始");
    			put_str(3,3,"分数");
                put_str(0,5,"倒计时");
				refresh(); //刷新free_num对应的地鼠
				TMOD=0x01;//计时器0和计时器1均为方式1
	            TH0=0x4b;
	            TL0=0x83;//   (65536-19331)/(0.9241M)=0.05s,19331=0x4b83 ,20次中断为1秒
				ET0=1;
				TR0=1;//启用定时器0
				sec=20;//20秒倒计时
			}
	    break;
		case PLAY:
		    if(key==15){//结束码
			    char end[10];
			    game_state=OVER;
				lcd_clear_txt();
                put_str(1,2, "游戏结束");
				sprintf(end,"分数%d!",score);
				put_str(2,2,end);
				put_str(3,0,"继续");
				score=0;
                ET0=0;
				TR0=0;//关闭定时器0
			}else if(key==free_num){
			    //新生成一个随机数
			    unsigned char temp=(unsigned char)(rand() % 12); //生成一个0到11之间的随机数
                while(free_num==temp)temp=(unsigned char)(rand() % 12); 
				free_num=temp;
				refresh(); //刷新free_num对应的地鼠
                put_str(3,5,space);//暂时清除分数
                score++;//打到相应的地鼠,分数加一
				sprintf(s_score,"%d",score);
				put_str(3,5,s_score);
			}else{
			    put_str(3,5,space);//暂时清除分数
			    score--;//没有打到相应的地鼠,分数减一
				sprintf(s_score,"%d",score);
				put_str(3,5,s_score);
			}
		break;
	}
	return;
}





/**************************************************
转换子程序:将中断获取的按键代码转换为键值
**************************************************/
unsigned char tran(unsigned char key){
    key&=0x3f;//只保留低六位
    if (key<4)return key;
    else if(key<12)return key-4;
    else if(key<20)return key-8;
    else if(key<28)return key-12;
}

/*********************************************************
定时器T0中断
*********************************************************/
void InterruptTimer0() interrupt 1
{
	TH0=0x4b;
	TL0=0x83;
	flag_1s++;
    if(flag_1s>19)
	{
		flag_1s=0;                 //清0
		sec--;
    	put_str(1,5,space);//先清为空串
		sprintf(s_sec,"%d",sec);
        put_str(1,5,s_sec);
		if(sec==0)game_state_machine(15);//给游戏状态机发送结束码
	}
}

/*********************************************************
主函数
*********************************************************/
void main() {
    IE=0x81;//EA 空 空 ES ET1 EX1 ET0 EX0
	TCON=0x01;//TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0(IT0=1表示外部中断0位边沿触发)
	lcd_init();
    ch451_init(); //初始化
    ch451_write(0x402);//设定系统参数:0100000[CKHF][DPLR][WDOG][KEYB][DISP]B,键盘有效,关闭显示
    ch451_write(0x580);//580H=010110000000B,右数第八位表示BCD译码方式
    lcd_clear_txt();
	put_str(0,0,"田"); 
    put_str(0,1,"田"); 
    put_str(0,2,"田"); 
    put_str(0,3,"田"); 
    put_str(1,0,"田"); 
    put_str(1,1,"田"); 
    put_str(1,2,"田"); 
    put_str(1,3,"田"); 
    put_str(2,0,"田"); 
    put_str(2,1,"田"); 
    put_str(2,2,"田"); 
    put_str(2,3,"田"); 
	put_str(3,0,"开始");
    ch451_key=0xff;
    while (1) {
        if(ch451_key!=0xff){
		    key_value=ch451_key;
            key_value=tran(key_value);
            game_state_machine(key_value);
            ch451_key=0xff;
		}
    }
}

运行结果演示

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值