AVR 单片机小学期 12864液晶 双人贪吃蛇游戏



这日志算是记录一下吧。

之所以比较认真对待这个小学期,是因为一个同学找我(因为这个小学期是两人一队验收)。

说想认真做一下分数得高一点。然后我就答应了。

我就负责编,刚开始因为12864他把管脚插错了,总是不行,刚开始我还比较急躁。

觉得他连硬件电路都搭不好我怎么编,而且眼看着只剩两三天时间就验收了。

好在后来电路搭好了(就一个管脚接错了而已!),而且我编程顺利,最后验收效果不错。

我觉得玩起来挺爽的,加速键用着爽。

算一个纪念吧。


#include<avr/io.h>
#include<avr/interrupt.h>
#define uchar unsigned char
#define uint  unsigned int
/********************端口************************/
#define RS_0 PORTB&=~(1<<0)
#define RS_1 PORTB|=(1<<0)
#define RW_0 PORTB&=~(1<<1)
#define RW_1 PORTB|=(1<<1)
#define E_0  PORTB&=~(1<<2)
#define E_1  PORTB|=(1<<2)
#define RESET_0 PORTB&= ~(1<<4) 
#define RESET_1 PORTB|= (1<<4)
/**************** 常用操作命令和参数定义 *****************/   
   
#define DisplayClear 0x01 //清屏指令(00000001)   
#define ReturnHome   0x02 //光标回到"00H"(0000001X)   
#define EntryMode    0x06 //进入点设定,光标右移,AC加1(00000110)   
#define DisplayOn    0x0c //整体显示开,游标显示关,反白显示关   
#define DisplayOff   0x08 //整体显示关   
#define CursorOn     0x0e //光标显示开   
#define Reverse      0x0d //反白显示开   
#define Basic        0x30 //基本指令   
#define FAST 		 0x03;
#define SLOW		 0x05;
#define NORM       0x04;
#define Up 0
#define Down 1
#define Left 2
#define Right 3
/******************************延迟函数***********************************/
uchar snakex1[16]={32,31,30,29,28,27,26,25,0,0};
uchar snakey1[16]={16,16,16,16,16,16,16,16,0,0};
uchar snakex2[16]={82,81,80,79,78,77,26,25,0,0};
uchar snakey2[16]={16,16,16,16,16,16,16,16,0,0};
uchar count1,count2;
uchar stonex1[9]={31,50,10,15,20,2,22,44,47/*,8,5,55,12,34,10*/};
uchar stoney1[9]={3,21,22,20,35,40,24,11,22/*,41,35,10,5,27,33*/};
uchar stonex2[9]={70,77,80,100,120,112,90,99,88/*,78,96,111,122,64,100*/};
uchar stoney2[9]={3,13,11,20,35,15,24,11,22/*,39,25,10,5,41,33*/};
uchar len1,len2;
uchar nowdir1,nowdir2;
uchar flag1;//游戏结束的标志
uchar flag2;
uchar stop1,stop2;
uchar Movex[4]={0,0,-1,1};//依次是上下左右
uchar Movey[4]={-1,1,0,0};
uchar speed;
uchar player;
void delay(uint ms)
{
    uint i,j;
	for(i=0;i<ms;i++)
    {
   		for(j=0;j<1141;j++);
    }
}
/************************检测LCD是否忙***************************/
void check_busy(void) 
{
	DDRD =0xff;
	RS_0;
	RW_1;
	E_1;
	DDRD =0x00;
	delay(1);
	while(PIND&0x80);
	E_0;
	DDRD =0xff;	

}

/***********************************写指令**********************************/
void write_com(uchar com)
{		
	   check_busy();
       RS_0;
	   RW_0;
	   PORTD=com;
	   E_1;
	   delay(1);
	   E_0;
	 
}
/***********************************写数据*********************/
void write_data(uchar write_data)
{    
	   check_busy();
       RS_1;
	   RW_0;
	   PORTD=write_data;
	   E_1;
	   delay(1);
	   E_0;

}
void DisplayCLR()//GDRAM清屏,如果没有清屏,会出现花屏现象。
{
	uchar i,j,k;
	write_com(0x36);
	delay(1);
	for(i=0;i<2;i++)
	{
		for(j=0;j<32;j++)
		{
			write_com(0x80+j);
			delay(1);
			if(i==0)
			{
			write_com(0x80);
			delay(1);
			}
			else//写下半屏
			{
				write_com(0x88);
			}
			for(k=0;k<16;k++)
			{
				write_data(0x00);
				delay(1);
			}
		}
	}
	write_com(0x30);
}
/********************************初始化**********************************/
void init(void)
{
	DDRD=0XFF;
	DDRB|=0x1f;
	E_0;
	RESET_1;
	len1=5;
	len2=5;
	nowdir1=Right;
	nowdir2=Right;
	flag1=0;
	flag2=0;
	count1=0;
	count2=0;
    delay(50);
	RESET_1;
	RESET_0;
	RESET_1;
	delay(50);
	write_com(Basic);
	delay(50);
	write_com(Basic);
	delay(50);
	write_com(DisplayOn);//开显示
	delay(500);
	write_com(DisplayClear);//清屏
	delay(50);
	write_com(EntryMode);//显示模式
    delay(50);

}
uchar read_write_dataa()
{
    uchar i;
	check_busy();
	RW_1;
    RS_1;    
    delay(1);
	E_0;
    delay(1);
    E_1;
	delay(1);
	DDRD=0X00;
	delay(1);//缺了这句话则不行!!!
	i=PIND;
	delay(1);
	E_0;
	delay(1);
 	DDRD=0XFF;
    return i;  
}	
void Showdot(uchar x, uchar y,uchar i)//在128*64的lcd矩阵中显示单个像素点
{
	uchar x_byte,x_bit;
	uchar y_byte,y_bit;
	x_byte=x/16;
	x_bit=x%16;
	y_byte=y/32;
	y_bit=y%32;
	write_com(0x36);//开扩展指令集
	delay(1);
	write_com(0x80+y_bit);
	delay(1);
	write_com(0x80+x_byte+8*y_byte);
	delay(1);
	read_write_dataa();
	delay(1);
	uchar check_busy1;
	uchar check_busy2;
	check_busy1=read_write_dataa();
	delay(1);
	check_busy2=read_write_dataa();
	delay(1);
	
	if(x_bit<8)
	{
		if(i==1)
		{
			check_busy1 |=(0x01<<(7-x_bit));
			delay(1);
		}
		else
		{
			check_busy1 &= ~(0x01<<(7-x_bit));
			delay(1);
		}
	}
	else
	{
		if(i==1)
		{
			check_busy2|=0x01<<(15-x_bit);
			delay(1);
		}
		else
		{
			check_busy2 &=~(0x01<<(15-x_bit));
			delay(1);
		}
	}
	write_com(0x80+y_bit);
	delay(1);
	write_com(0x80+x_byte+8*y_byte);
	delay(1);
	write_data(check_busy1);
	delay(1);
	write_data(check_busy2);
	delay(1);
	write_com(0x30);
	delay(1);
}
/********************************设置xy*******************************************/
void set_position(uint x,uint y) 
{
	uint adr;
	switch(y)
	{
	case 0: adr = 0x80 + x;
		break;		 //在第1行y列显示
	case 1: adr = 0x90 + x;
		break;		//在第2行y列显示
	case 2: adr = 0x88 + x;
		break;		//在第3行y列显示
	case 3: adr = 0x98 + x;
		break;		//在第4行y列显示
	default:;	
		adr=0x80;
		break;
	}
	write_com(adr);
}
/********************************写字符串*******************************************/
void write_string(uint x,uint y,uchar *a)  //显示字符串
{
  uint i=0;
  set_position(x,y);
  delay(1);
  while(a[i]!='\0')
  {
  	write_data(a[i]);
	delay(1);
    i++;
  }
}
void Showsnake(uchar * sx,uchar * sy,uchar len)//显示整条蛇
{
	uchar i;
	for(i=0;i<len;i++)
	{
		Showdot(sx[i],sy[i],1);
	}
}
uchar Meetwall(uchar x,uchar y,uchar i)//撞到墙
{
	if(i==0)
	{
		if(x>0&&x<59&&y>0&&y<45)
			return (0);
		else
			return (1);
	}
	else
	{
		if(x>68&&x<127&&y>0&&y<45)
			return (0);
		else
			return (1);
	}
}
uchar Meetself(uchar *sx,uchar *sy,uchar len,uchar x,uchar y)//撞到自己
{
	uchar i;
	for(i=0;i<len;i++)
	{
		if(sx[i]==x&&sy[i]==y)
		{
			return (1);
		}
	}
	return (0);
}
void Move(uchar * sx,uchar * sy,uchar *len,uchar dir,uchar add)//蛇移动函数
{
	uchar i;
	if(add==1)//add==1时增加尾巴。
	{
		sx[(*len)]=sx[(*len)-1];
		sy[(*len)]=sy[(*len)-1];
		(*len)++;
	}
	else
		Showdot(sx[(*len)-1],sy[(*len)-1],0);
	for(i=(*len)-1;i!=0;i--)
	{
		sx[i]=sx[i-1];
		sy[i]=sy[i-1];
	}
	sx[0]+=Movex[dir];
	sy[0]+=Movey[dir];
	Showdot(sx[0],sy[0],1);
}
void write_count(uchar i)//显示分数
{
	uchar arr[2]="0";
	if(i==1)
	{
		arr[0]+=count1;
		write_string(3,3,arr);
	}
	else
	{
		arr[0]+=count2;
		write_string(7,3,arr);
	}
}
void write_result(uchar i)//显示结果
{
	uchar str1[]="You";
	uchar str21[]="Win!";
	uchar str22[]="Lose!";
	uchar str3[]="Game Over";
	switch(i)
	{
	case 1:
			write_string(1,0,str1);
			write_string(1,1,str22);
			break;
	case 2:
			write_string(5,0,str1);
			write_string(5,1,str22);
			break;
	case 3:
			write_string(2,2,str3);
			break;
	case 4:
			write_string(1,0,str1);
			write_string(1,1,str21);
			break;			
	case 5:
			write_string(5,0,str1);
			write_string(5,1,str21);
			break;
	}
}
void Frame()
{
	uchar i;
	for(i=0;i<60;i++)
	{
		Showdot(i,0,1);
		Showdot(i,45,1);
		if(player==2)
		{
			Showdot(i+68,0,1);
			Showdot(i+68,45,1);
		}
	}
	for(i=1;i<45;i++)
	{
		Showdot(0,i,1);
		Showdot(59,i,1);
		if(player==2)
		{
			Showdot(68,i,1);
			Showdot(127,i,1);
		}
	}
	uchar str[]="Count:";
	write_string(0,3,str);
	write_count(1);
	if(player==2)
	{
	write_string(4,3,str);
	write_count(2);
	}
}
void Starttimer0()
{
	TCNT0=0;
	TIMSK |=(1<<TOIE0);
	SREG|=0X80;
	TCCR0 |=speed;
}
void Starttimer2()
{
	TCNT2=0;
	TIMSK |=(1<<TOIE2);
	SREG|=0X80;
	TCCR2|=speed+2;
}
uchar key_press()
{
     uchar i;
     DDRA=0XFF;
	 PORTA=0XF0;
	 
	 DDRA=0X0F;
	 
	 i=PINA;
	 if(i==0XF0)
	 {
	   DDRA=0XFF;
	   return 0;
	 }
	 else
	 {
	   DDRA=0XFF;
	   return 1;
	 }
}

uchar key_scan()//8个管脚全部接在PINA上
{
     uchar key,i=0X7F,j;//
     delay(10);//消抖
	 if(key_press())
	 {
	    do
		{
		 i=(i<<1|i>>7);//因为每次都只能给一个IO口赋值为0,所以这行语句目的是对这个0循环移位。
		 PORTA=i;
		 DDRA=0X0F;
		 
		 key=PINA;
		 j=key&0XF0;//读取高4位(高4位为输入,低4位为输出)
		 
	    }while(j==0XF0);
		switch(key)
		{
	   case 0xDE://0b11011110
	   		key=0x1;
					if(nowdir2!=Left&&flag2==0)
					nowdir2=Right;
			break; 
	   case 0x7E:
			TCCR2 &=~(0x07);
			TCCR2|=FAST;
			TCCR2+=2;
			while(key_press());//直到按键松开才跳出这个语句。
			TCCR2 &=~(0x07);
			TCCR2|=(speed);
			TCCR2+=2;
			key=0x3;
			break;
	   case 0xED:
	  		key=0x4;
			if(nowdir2!=Up&&flag2==0)
					nowdir2=Down;
			break;
	   case 0xDD:
	   		key=0x5;
			if(nowdir1!=Left&&flag1==0)
					nowdir1=Right;
			break;
	   case 0xBD:
	   		key=0x6;
					if(nowdir2!=Down&&flag2==0)
					nowdir2=Up;
			break;
	   case 0x7D:
	   		TCCR0 &=~(0x07);
			TCCR0 |=FAST;
			while(key_press());//直到按键松开才跳出这个语句。
			TCCR0 &=~(0x07);
			TCCR0|=(speed);
			key=0x7;
			
			break;
	   case 0xEB:
	   		key=0x8;
				if(nowdir1!=Up&&flag1==0)
					nowdir1=Down;
			break;
	   case 0xDB:
	   	key=0x9;
			if(nowdir2!=Right&&flag2==0)
					nowdir2=Left;
			break;
	   case 0xBB:
	   		key=0xA;
			if(nowdir1!=Down&&flag1==0)
					nowdir1=Up;
			break;
	   case 0x7B:
	   		while(key_press());
				stop2=stop2^1;
	   		key=0xB;
		
			break;
	   case 0xD7:
	   if(nowdir1!=Right&&flag1==0)
					nowdir1=Left;
			
			break;
	   case 0x77://暂停键
	   		while(key_press());
				stop1=stop1^1;
	   		key=0xF;
		
			break;
	   default:
	        key=16;		
		}
	 }
	 else
	 {
	    key=16;
	 }
	 return key;
}

uchar Meetstone(uchar x,uchar y,uchar i)
{
	if(i==1)
	{
		if(x==stonex1[count1]&&y==stoney1[count1])
		{
			count1 ++;
			if(count1==9)
			{
				flag1=1;
				write_result(4);
			}
			write_count(1);
			return 1;
		}
		else
			return 0;
	}
	else
	{
		if(x==stonex2[count2]&&y==stoney2[count2])
		{
			count2 ++;
			if(count2==9)
			{
				flag2=1;
				write_result(5);
			}
			write_count(2);
			return 1;
		}
		else
			return 0;
	}
}
void choose(int i)
{
	uchar arr0[]="*";
	uchar arr[4][7]={"Hard","Easy","Single","Double"};
	uchar pos=1;
	uchar *arr1;
	uchar *arr3;
	if(i==1)
	{
		arr1=arr[0];
		arr3=arr[1];
	}
	else
	{
		arr1=arr[2];
		arr3=arr[3];
	}
	uchar arr5[]=" ";
	write_string(1,pos,arr0);
	write_string(2,1,arr1);
	write_string(2,2,arr3);
	uchar flag=1;
	while(flag)
	{
		if(key_press())
		{
			switch(key_scan())
			{
			case 7://按键D
			write_string(1,pos,arr5);
			pos=(pos)%2+1;
			delay(1);
			write_string(1,pos,arr0);
			break;
			case 3://按F键确认
			flag=0;
			
			break;
			}
		}
	}
	switch(pos)
	{
	case 1:
	if(i==1)
	{	speed=NORM;}
	else
	{	player=pos;}
	break;
	default:
	if(i==1)
	{	speed=SLOW;}
	else
	{	player=pos;}
	break;
	}
	write_com(0x01);//清屏命令
}
void write_welcome()
{
	uchar a1[]="Welwrite_come";
	uchar a2[]=" To";
	uchar a3[]="Greedy Snake";
	write_string(2,1,a1);
	write_string(3,2,a2);
	write_string(1,3,a3);
	while(key_press()==0);
	write_com(0x01);
}
int main(void)
{	 

	init();//初始化驱动12864液晶屏和初始化相关参数。
	write_welcome();//欢迎界面
	choose(1);//选择难易程度
	choose(2);//选择单人或者双人模式
	DisplayCLR();//清屏
	Frame();//显示边框
	Showsnake(snakex1,snakey1,len1);//显示蛇
	Showdot(stonex1[count1],stoney1[count1],1);//显示小豆
	Starttimer0();//启动定时器1
	if(player==2)//双人模式时
	{	
		Starttimer2();//启动定时器2
		Showdot(stonex2[count2],stoney2[count2],1);
		Showsnake(snakex2,snakey2,len2);
	}
	while(flag1==0||flag2==0)//循环读取键盘,蛇的转向和加速和暂停等案件处理已经写在key_scan()内部。
	{
		if(key_press())
		{
			key_scan();
		}
	};
	write_result(3);//显示game over
	SREG&=0x7f;//关全局中断,即关定时器,让蛇停止爬行。
	DisplayCLR();//将蛇和小豆和边框清除,而字符型不会清楚。即游戏结果还显示着。
	return 0;
}

SIGNAL(SIG_OVERFLOW0)//定时器来实现蛇的不断前进
{
	
	uchar x=snakex1[0]+Movex[nowdir1];//蛇头前进的下一个坐标
	uchar y=snakey1[0]+Movey[nowdir1];//蛇头前进的下一个坐标
	if(flag1==0&&stop1==0)
	{
		if(Meetwall(x,y,0)||Meetself(snakex1,snakey1,len1,x,y))//两个判断函数
		{
			flag1=1;//flag1置1使这条蛇“死了”,即不再动了不再受控制了。
			write_result(1);//显示you lose

		}
		else if(Meetstone(x,y,1))//判断有没有吃到小豆
		{
			Move(snakex1,snakey1,&len1,nowdir1,1);//这里move最末一个参数为1,代表move的同时,蛇身体增长一个单位
			Showdot(stonex1[count1],stoney1[count1],1);//显示出新的一个小豆
		}
		else
		{
			Move(snakex1,snakey1,&len1,nowdir1,0);//没有吃到小豆的话则move最末参数为0,即蛇身体长度不变。
		}
	}

}
SIGNAL(SIG_OVERFLOW2)//同理SIGNAL(SIG_OVERFLOW0)
{
	if(flag2==0&&stop2==0)
	{
		uchar x=snakex2[0]+Movex[nowdir2];
		uchar y=snakey2[0]+Movey[nowdir2];
		if(Meetwall(x,y,1)||Meetself(snakex2,snakey2,len2,x,y))
		{
			flag2=1;
			write_result(2);
		}
		else if(Meetstone(x,y,2))
		{
			Move(snakex2,snakey2,&len2,nowdir2,1);
			Showdot(stonex2[count2],stoney2[count2],1);
		}
		else
		{
			Move(snakex2,snakey2,&len2,nowdir2,0);
		}
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值