这日志算是记录一下吧。
之所以比较认真对待这个小学期,是因为一个同学找我(因为这个小学期是两人一队验收)。
说想认真做一下分数得高一点。然后我就答应了。
我就负责编,刚开始因为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);
}
}
}