推箱子,12864液晶实物

  

  


嗯,首先要说的是,仿真和实物真心差的很远很远……

所以YY的盆友一定不要想的太简单,就像我一样,一直以为仿真做完了,实物会很easy,其实不然,特别是我这

种敲代码敲的比较多,上手焊电路比较少的来说,更是蛋疼的要死,按键我就焊了好几次……


言归正传,之前写过仿真的推箱子(在这里)。实物我用的是CX12864B,具体的参数,网上多的是,略去不提。

至于实物和仿真的区别,差不多有以下几点吧:

(PS:这都是 I think ,因为水平有限,如果有错误,麻烦您提出来,我的邮箱laoda.greenhand@foxmail.com)

Node 1:

仿真是没有字库的,需要用取模软件一个一个自己扣字,相当恶心啊……

实物是有字库的,直接字符串输出就行(汉字是16*16的点阵,字母数字是16行8列点阵,具体见代码,图片)


Node 2:

仿真的显示通常是有片选位的,用来选择控制左半屏和右半屏,就像我写的那个仿真一样,CS1、CS2分别对应两

个半屏

实物没有片选,PSB口来控制串行还是并行,因为默认的高电平对应并行8位,所以在下面的代码里面,我并没有处

理那个口


Node 3:

仿真的推箱子,我用的是光标定址,然后更新 8*8 点阵,这有点类似控制台C游戏的做法,因为这样的话,可以把地图

的每个元素(人,箱子,墙,箱子目的地,空格)用一个具体数值表示,然后用数组存下来,更新和维护都比较方便


然后是实物12864下的推箱子,对于实物12864,有两种模式,普通模式(显示汉字,还有自定义字符);绘图模式(需要打开绘图开关),整个屏分为

上下两个半屏。控制端给12864分先后,送垂直地址,水平地址,每次显示16位,然后地址自动向后累加。

问题来了,刚才提到的,我用仿真的时候,是每次更新一个 8*8 点阵来实现游戏画面对按键录入的动态响应的,可是,我尝试了很久,都没有找到实物液

晶,一次在一个指定坐标画出一个 8*8 点阵的方法……= =1,最后,最后的最后,我用了一个比较扯淡的办法:

地图还是按照 8*8 矩阵来存,更新的时候,精度不能到 8*8 的话,那就更新每个需要更新点所在的 16*16 (也就是两行两列的四个 8*8 点阵)的点阵……

是不是挺笨的= =!,可是暂时找不到别的办法了,哪位路过的大大,如果有好办法,麻烦您告诉我一声,省的咱继续把代码贴在这现眼……


差不多了吧,至于别的我没提到的,看看代码吧,很简单

Thanks :大海橡树:http://hi.baidu.com/new/dahaixiangshu


Code:

#include<reg52.h>
#include<stdlib.h>
#include<stdio.h>
#include<intrins.h>
#define uchar unsigned char
#define uint  unsigned int

sbit RS = P3^0;
sbit RW = P3^1;
sbit EN = P3^2;

sbit key_up = P2^1;
sbit key_down = P2^2;
sbit key_left = P2^0;
sbit key_right = P2^3;

#define LCD_data P1
int cur_x,cur_y,X,dir[4][2]={-1,0,1,0,0,-1,0,1},where[3][2]={4,6,5,6,6,6};


uchar code table1[]="LCD12864";
uchar code table2[]="推箱子";
uchar code table3[]="BY laoda";
uchar code table4[]="You Win!";

uchar code tmp[]={//空格
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
};
/* 0表示空格,1表示墙,2表示人,3表示箱子,4表示目的地 */
uchar code mat[8][8]={
1,1,1,1,1,1,1,1,
1,1,1,1,0,2,1,1,
1,1,0,0,3,0,1,1,
1,1,0,1,0,1,1,1,
1,0,0,1,0,1,4,1,
1,0,1,0,0,3,4,1,
1,0,3,0,0,0,4,1,
1,1,1,1,1,1,1,1
};

uchar map[8][8];

uchar code Qiang[]={//方格,有间隙
0xff,0x81,0x81,0x81,0x81,0x81,0x81,0xff
};

uchar code Ren[]={//人
0xff,0xff,0xe3,0xc5,0xb1,0xc5,0xe3,0xff
};

uchar code Xiang[]={//箱子  
0xff,0x42,0x24,0x18,0x18,0x24,0x42,0xff  
};

uchar code Mudi[]={//目的地  
0xff,0xff,0xe7,0x81,0x81,0xe7,0xff,0xff  
};

void delay(uint i) //延时函数 
{
	while(--i);
}

void read_busy()//读忙函数。每次读写都要进行读忙操作 
{
	RS=0;
	RW=1;
	EN=1;
	while(LCD_data & 0x80);//最高位为BF位 DB7---BF 
	EN=0;
}

void write_LCD_command(uchar value)//写指令函数 
{
	read_busy();
	RS=0;
	RW=0;
	EN=1;		//EN从1---0锁存数据 
	LCD_data=value;
	delay(20);
	EN=0;
} 

void write_LCD_data(uchar value)//写数据函数 
{
	read_busy();
	RS=1;
	RW=0;
	EN=1;		//EN从1---0锁存数据 
	LCD_data=value;
	delay(20);
	EN=0;
} 
void init_BMP()	 
{
	write_LCD_command(0x36);//CL=1--8位。扩充指令(RE=1),绘图打开(G=1)
	delay(100);	//适当延时 
	write_LCD_command(0x36);
	delay(37);
	write_LCD_command(0x3E);//8位(CL=1),扩充指令(RE=1),绘图打开(G=1) 
	delay(100);
	write_LCD_command(0x01);//清屏指令 
	delay(100);
}

void init_Hanzi()				//8位并口方式LCD1864初始化函数 
{
	delay(4000);			//等待时间>40ms 
	write_LCD_command(0x30);//功能设定:8位数据、基本指令操作 
	delay(100);				//等待时间>100us 
	write_LCD_command(0x30);//功能设定:8位数据、基本指令操作 
	delay(37);				//等待时间>37us 
	write_LCD_command(0x0C);//显示设定:整体显示、游标关、不反白 
	delay(100);				// 等待时间>100us 
	//write_LCD_command(0x01);//清屏指令 
	//delay(10000);			//等待时间>10ms 
	write_LCD_command(0x06);//进入点设定:地址指针加1 
}

void display_BMP(uchar fuck,uchar name1,uchar name2,uchar y,uchar x)	
{
	uchar shit,i;
	if(fuck==0)shit=0x80;
	else shit=0x88;
	for(i=0;i<8;i++)		
	{	
		write_LCD_command(0x80+i+8*x);//先送垂直地址 
		write_LCD_command(shit+y);  //再送水平地址 ----显示图片的上半部分 

		if(name1==0)	write_LCD_data(tmp[i]);
		else if(name1==1)	write_LCD_data(Qiang[i]);
		else if(name1==2)	write_LCD_data(Ren[i]);
		else if(name1==3)	write_LCD_data(Xiang[i]);
		else if(name1==4)	write_LCD_data(Mudi[i]);

		if(name2==0)	write_LCD_data(tmp[i]);
		else if(name2==1)	write_LCD_data(Qiang[i]);
		else if(name2==2)	write_LCD_data(Ren[i]);
		else if(name2==3)	write_LCD_data(Xiang[i]);
		else if(name2==4)	write_LCD_data(Mudi[i]);
	}
}

void show_map(){
	uchar i,j,x,y;

	for(i=0;i<2;i++){
		for(j=0;j<4;j++){
			x=2*i; y=2*j;
			display_BMP(0,map[x][y],map[x][y+1],j,2*i);
			x=2*i+1; y=2*j;
			display_BMP(0,map[x][y],map[x][y+1],j,2*i+1);
		}
	}

	for(i=2;i<4;i++){
		for(j=0;j<4;j++){
			x=2*i; y=2*j;
			display_BMP(1,map[x][y],map[x][y+1],j,2*(i-2));
			x=2*i+1; y=2*j;
			display_BMP(1,map[x][y],map[x][y+1],j,2*(i-2)+1);
		}
	}

	init_Hanzi();		
	write_LCD_command(0x80+4);
	for(i=0;i<8;i++)
	{
		write_LCD_data(table1[i]);
	}
	write_LCD_command(0x90+4);
	for(i=0;i<8;i++)
	{
		write_LCD_data(table2[i]);
	}

	write_LCD_command(0x88+4);
	for(i=0;i<8;i++)
	{
		write_LCD_data(table3[i]);
	}
	delay(100);
}

void clear()//图形模式下,没有清屏函数,手动全部送0						
{
	uchar i,j;
	for(i=0;i<32;i++){
		write_LCD_command(0x80+i);//先送垂直地址 
		write_LCD_command(0x80);  //再送水平地址 ----显示图片的上半部分 
		for(j=0;j<16;j++)write_LCD_data(0);
	}
	for(i=0;i<32;i++){
		write_LCD_command(0x80+i);	//先送垂直地址
		write_LCD_command(0x88);	//显示图片的下半部分 
		for(j=0;j<16;j++)write_LCD_data(0);
	}
}

uchar judge(int x,int y,int id){							//id 表示方向数组的行标,0,1,2,3 分别表示上下左右
	int xx,yy,xxx,yyy;
	xx=x+dir[id][0]; yy=y+dir[id][1];
	if(map[xx][yy]==0 || map[xx][yy]==4)return 1;			   //1表示前面是 空格 或者 目的地,就是可以直接移动
	else if(map[xx][yy]==1)return 0;						   //0表示无法移动
	else if(map[xx][yy]==3){
		xxx=xx+dir[id][0]; yyy=yy+dir[id][1];
		if(map[xxx][yyy]==1 || map[xxx][yyy]==3)return 0;
		else if(map[xxx][yyy]==0 || map[xxx][yyy]==4)return 2; //2表示需要间接移动,先把前面的箱子移动一,再让人移动一
	}return 0;
}

void update(uchar a,uchar b){
	uchar i,j,x,y;

	write_LCD_command(0x3E);//8位(CL=1),扩充指令(RE=1),绘图打开(G=1) 
	delay(100);

	if(a<4){
		i=a/2; j=b/2;
		x=2*i; y=2*j;
		display_BMP(0,map[x][y],map[x][y+1],j,2*i);
		x=2*i+1; y=2*j;
		display_BMP(0,map[x][y],map[x][y+1],j,2*i+1);
	}
	else {
		i=a/2; j=b/2;
		x=2*i; y=2*j;
		display_BMP(1,map[x][y],map[x][y+1],j,2*(i-2));
		x=2*i+1; y=2*j;
		display_BMP(1,map[x][y],map[x][y+1],j,2*(i-2)+1);
	}
}

void fun(uchar key,uchar dirction){
	uchar x,y;
	/* 0表示空格,1表示墙,2表示人,3表示箱子,4表示目的地 */
	if(key==1){//直接移动
		if(mat[cur_x][cur_y]==4)map[cur_x][cur_y]=4;
		else map[cur_x][cur_y]=0;
		update(cur_x,cur_y);

		cur_x+=dir[dirction][0]; cur_y+=dir[dirction][1];
		map[cur_x][cur_y]=2;
		update(cur_x,cur_y);
					
	}
	else if(key==2){//间接移动
		if(mat[cur_x][cur_y]==4)map[cur_x][cur_y]=4;
		else map[cur_x][cur_y]=0;
		update(cur_x,cur_y);
					
		cur_x+=dir[dirction][0]; cur_y+=dir[dirction][1];
		map[cur_x][cur_y]=2;
		update(cur_x,cur_y);

		x=cur_x+dir[dirction][0]; y=cur_y+dir[dirction][1];
		map[x][y]=3;
		update(x,y);
	}
}

void lcd_init(){
	uchar i,j;

	init_BMP();

	cur_x=1; cur_y=5;
	X=3;
	
	for(i=0;i<8;i++){
		for(j=0;j<8;j++)map[i][j]=mat[i][j];
	}
}

void main()
{
	uchar i,num,index=6;
	lcd_init();
	clear();
	show_map();

	/* 0表示空格,1表示墙,2表示人,3表示箱子,4表示目的地 */
	while(1){
		num=0;
		for(i=0;i<3;i++){
			if(map[ where[i][0] ][ where[0][1] ]==3)num++;
			else break;
		}
		if(num==X)goto WIN;
		if(key_up==0){
			delay(500);
			if(key_up==0){
				index=judge(cur_x,cur_y,0);
				fun(index,0);
			}while(!key_up);
		}
		else if(key_down==0){
			delay(500);
			if(key_down==0){
				index=judge(cur_x,cur_y,1);
				fun(index,1);
			}while(!key_down);
		}
		else if(key_left==0){
			delay(500);
			if(key_left==0){
				index=judge(cur_x,cur_y,2);
				fun(index,2);
			}while(!key_left);
		}
		else if(key_right==0){
			delay(500);
			if(key_right==0){
				index=judge(cur_x,cur_y,3);
				fun(index,3);
			}while(!key_right);
		}
	}
WIN:
	init_Hanzi();		

	write_LCD_command(0x98+4);
	for(i=0;i<8;i++)
	{
		write_LCD_data(table4[i]);
	}
	delay(1000);
	while(1); 
}

本仿真工程庞大,整个项目源代码全部公开,你下载后只要你装了Keil就可以编译通过(我用的是Keil4低版本能否通过不清楚)。为了达到复用的目的文件较多,不管是算法效率,游戏逻辑分析,各种芯片驱动,还是整个项目架构或是代码风格,都经过本人精心设计,可以说每个方面都有其独到之处,会让你有一种赏心悦目的感觉,不管你是新手还是老手都很难说这其中没有值得你学习的地方,特别是在处理游戏寻路逻辑那一块,在最开始时我用的算法是采用栈来处理,但后来发现这种方式找到的路径不一定是最短路径,所以又改用了队列算法来实现,但栈的代码并没有删除,而是采用了宏处理 你可以去掉#define USEQUEUE 这句宏定义而不需改变其它的任何代码,再次Build一次就可以看到采用栈寻路的效果了。本仿真中游戏关数只设了四关,如果你想多加关数,只要在数据文件中添加正确的关数据,并修改宏#define MAX_HURDLE 4的值再一次Build即可,下面是复制以前帖子的游戏看点内容: 本游戏之十大看点 游戏看点一:游戏采用LCD-KEYPAD液晶显示屏做为显示界面(256*256),大家不要去你自己的元器件库里搜索这块液晶,你永远都搜索不到,至于为什么我的仿真里会有,留你自己慢慢想,如果你够细心就会明白,只要你装了Proteus 仿真不是问题。 游戏看点二:游戏启动界面简洁,依然沿用了以前游戏的启动界面 游戏看点三:游戏所用的单片机为最原始的80C52单片机,不需要AVR也不需要ARM,小小的80系列单片机就能绰绰有余的实现游戏所有功能。 游戏看点四:游戏无需外加数据存取器,根本就用不完256字节的数据段(本游戏151.6字节,还有100多个字节空闲),很多朋友认为写游戏不扩展外部数据存储器几乎不可能,如果你看了我的仿真,你就会改变你的想法。 游戏看点五:本游戏属原创,绝无抄袭,也没地方抄袭。 游戏看点六:游戏的硬件电路,极其简单,由于采用了触摸屏,移动按钮被取消,但保留了虚拟终端。 游戏看点七:游戏采用了虚拟终端,能通过PC键盘完成游戏的所有操作,具体操作看终端显示。 游戏看点八:游戏代码相当规范,工程建立独到,如果你看了我的代码架构,再和论坛里大多数朋友的相比较一下,你就会明白高手和菜鸟的区别。 游戏看点九:Proteus的触摸屏看似引脚众多,其实驱动也没那么复杂,类似行列式键盘,但如果让你来写你还真不一定能写出来,不是我很狂,现在很多人都眼高手低的,说是简单,写起来却不是那么回事。 游戏看点十:游戏中用到了24C02C芯片,可以任意时间加载和保存游戏。 仿真注意:Proteus软件必须是7.4或7.4以上版本
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值