来源:http://blog.chinaunix.net/uid-29270124-id-4949163.html
点击左下角“阅读原文”直接进入
一、元器件
1、AT89C51
关于51单片机就不在啰嗦了,相信大家都已经很熟悉了,关于它的一些常用细节,已经在另一篇博文中提到
http://blog.chinaunix.net/uid-29270124-id-4571661.html
2、8x8点阵
点阵里面就是一些二极管啦,通过纵横交叉连接,横8竖8,每个交叉点都接一个二极管。这里给大家找到一个点阵的实物图
我想大家看到这个图就应该知道如何去点亮一个点阵了。假如要点亮最左上角那个,那么9号引脚拉高,13号引脚拉低,这样既可。
二、原理图
三、项目分析
1、首先定义一个结构体
struct snake{
unsigned char x[20];
unsigned char y[20];
unsigned char length;
unsigned char direction;
}snk;
数组x,y分别存放每一个点的横纵坐标,length为蛇的长度,direction为蛇前进的方向
2、坐标系:点阵的左下角为点(0,0),横纵坐标都是正向增长,P2控制横坐标;P0控制纵坐标。通过坐标可以找到点阵中点的位置,然后将其点亮
假设现在有第2个点的坐标x[2] = 1, y[2] = 2,那么点亮这个点的方式为
P2 = 0x04; //0000 0100
P0 = 0xfb; //1111 1011
3、按键产生外部中断,在中断里判断按下那个方向get_direction(),并且同时设置坐标set_location()
4、定时器每隔1s就应该更新位置,因为蛇要不停的前进。定时器不需要更新方向,因为方向只有按键才会改动,定时器用前一步的方向
5、关于点的位置更新方式
1)、向上移动
后面的点去覆盖前面的点,第一个点用新坐标表示x[0]不变,y[0]+1
2)、向下移动
后面的点去覆盖前面的点,第一个点用新坐标表示x[0]不变,y[0]-1
3)、向左移动
后面的点去覆盖前面的点,第一个点用新坐标表示x[0]-1,y[0]不变
4)、向右移动
后面的点去覆盖前面的点,第一个点用新坐标表示x[0]+1,y[0]不变
6、关于边界问题:
1)、任何一个点的横坐标 0 <= x[i] < 8
2)、任何一个点的纵坐标 0 <= y[i] < 8
3)、第一个点在移动的时候不能和其他点重复,否则就自己追尾了
7、关于原理图按键的设计
贪吃蛇要求系统能迅速响应按键,因此轮询的方式并不可取,只有靠外部中断。然而51只有2个外部中断,我们起码需要4个方向键,这样就不能一个按键配一个外部中断,通过使用4输入与门,将所有按键状态集合在一起,然后送给外部中断0。我们将4个按键都接在与门,只要有一个按下,那么与门的输出就会产生一个下降沿,从而产生外部中断。
四、源代码
main.c
-
#include "snake.h"
-
-
int error = 0;
-
int time=0;
-
-
void interrupt_init()
-
{
-
EA = 0; //关闭总中断
-
IT0 = 1; //外部中断0方式 下降沿
-
EA = 1; //开启总中断
-
EX0 = 1; //开启外部中断
-
}
-
-
void timer_init()
-
{
-
EA = 0; //关总中断
-
ET0 = 1; //开定时器0中断
-
TMOD = 0x02; //定时器0工作方式2
-
TL0 = 6; //定时250us
-
TH0 = 6;
-
EA = 1; //开总中断
-
TR0 = 1; //开始定时
-
}
-
int main()
-
{
-
// unsigned char tempx, tempy;
-
// unsigned char i,j;
-
-
interrupt_init();
-
timer_init();
-
snk_init();
-
-
while(1)
-
{
-
//如果位置错了就重新初始化蛇
-
if(error)
-
snk_init();
-
//点亮点阵
-
matrix();
-
}
-
}
-
-
void inter0() interrupt 0
-
{
-
//按键产生外部中断,获取新的方向
-
get_direction();
-
//设置新的位置
-
error = set_location();
-
// matrix();
-
}
-
void timer0() interrupt 1
-
{
-
time++;
-
//定时器为250us 积累4000次就是1s
-
if(time == 4000)
-
{
-
//每隔1s都需要重新设置位置,让蛇前进
-
error = set_location();
-
time = 0;
-
}
-
}
snake.c
点击(此处)折叠或打开
-
#include "snake.h"
-
-
//蛇的结构体,x为横坐标,y为纵坐标,length为蛇的长度,direction为蛇的前进方向
-
struct snake{
-
unsigned char x[20];
-
unsigned char y[20];
-
unsigned char length;
-
unsigned char direction;
-
}snk;
-
-
void matrix()
-
{
-
unsigned char i;
-
int count=500;
-
//关闭所有的点
-
P2 = 0x00;
-
P0 = 0xff;
-
-
//根据蛇每一个点的坐标,将对应的点阵点亮
-
for(i=0; i<snk.length; i++)
-
{
-
P2 = 1<<snk.x[i];
-
P0 = ~(1<<snk.y[i]);
-
}
-
}
-
-
void snk_init()
-
{
-
//初始化坐标,总共4个点(3,0) (2,0) (1,0) (1,0)
-
snk.x[0] = 3;
-
snk.y[0] = 0;
-
snk.x[1] = 2;
-
snk.y[1] = 0;
-
snk.x[2] = 1;
-
snk.y[2] = 0;
-
snk.x[3] = 0;
-
snk.y[3] = 0;
-
-
//初始长度4
-
snk.length = 4;
-
//初始移动方向 向右
-
snk.direction = RIGHT;
-
//点亮点阵
-
matrix();
-
}
-
-
-
-
void get_direction()
-
{
单片机的品种很多,设计时怎样表示操作码和操作数,都有各自的规定,指令代码也各不相同,因此,必须对所选单片机的全部指令,也就是所谓“指令系统”,有足够的了解。各个系列的单片机虽然有不同的指令系统,但也有其共同性。掌握一种单片机的指令系统,对其它系列单片机可以起到触类旁通的作用。MCS-51单片机应用广泛,派生品种多,具有代表性,所以,这里以MCS-51系列的指令系统为例说明“指令”的组成和应用。
-
//通过按键的状态获取方向
-
if(!up)
-
snk.direction = UP;
-
-
if(!down)
-
snk.direction = DOWN;
-
-
if(!left)
-
snk.direction = LEFT;
-
-
if(!right)
-
snk.direction = RIGHT;
-
}
-
-
int set_location()
-
{
-
unsigned char i;
-
int err = 0;
-
if(snk.direction == UP)
-
{
-
for(i=snk.length-1; i>0; i--)
-
{
-
snk.x[i] = snk.x[i-1];
-
snk.y[i] = snk.y[i-1];
-
}
-
-
//如果向上运动,第0个点的横坐标不变,纵坐标加1
-
snk.x[0] = snk.x[0];
-
snk.y[0] = snk.y[0] + 1;
-
}
-
else if(snk.direction == DOWN)
-
{
-
for(i=snk.length-1; i>0; i--)
-
{
-
snk.x[i] = snk.x[i-1];
-
snk.y[i] = snk.y[i-1];
-
}
-
-
//如果向下运动,第0个点的横坐标不变,纵坐标减1
-
snk.x[0] = snk.x[0];
-
snk.y[0] = snk.y[0] - 1;
-
}
-
else if(snk.direction == LEFT)
-
{
-
for(i=snk.length-1; i>0; i--)
-
{
-
snk.x[i] = snk.x[i-1];
-
snk.y[i] = snk.y[i-1];
-
}
-
-
//如果向左运动,第0个点的横坐标减1,纵坐标不变
-
snk.x[0] = snk.x[0] - 1;
-
snk.y[0] = snk.y[0];
-
}
-
else
-
{
-
for(i=snk.length-1; i>0; i--)
-
{
-
snk.x[i] = snk.x[i-1];
-
snk.y[i] = snk.y[i-1];
-
}
-
-
//如果向右运动,第0个点的横坐标加1,纵坐标不变
-
snk.x[0] = snk.x[0] + 1;
-
snk.y[0] = snk.y[0];
-
}
-
-
err = is_location_error();
-
return err;
-
}
-
-
int is_location_error()
-
{
-
unsigned char i;
-
-
//如果第0个点的坐标和其他任意一个点重复,那么蛇就自己撞自己,出错
-
for(i=1; i<snk.length; i++)
-
{
-
if((snk.x[0]==snk.x[i]) && (snk.y[0]==snk.y[i]))
-
return 1;
-
}
-
-
//如果蛇的坐标超出范围,也出错
-
if(snk.x[0]>7 || snk.y[0]>7)
-
return 1;
-
-
return 0;
-
}
snake.h
-
#include <reg51.h>
-
-
//定义四个方向按键
-
sbit up = P3^4;
-
sbit down = P3^5;
-
sbit left = P3^6;
-
sbit right = P3^7;
-
//定义1个游戏级别按键
-
sbit level = P3^0;
-
//定义一个复位按键
-
sbit reset = P3^1;
-
-
//定义4个方向的值
-
#define RIGHT 0
-
#define UP 1
-
#define LEFT 2
-
#define DOWN 3
-
-
void delay_us();
-
void delay_10us();
-
void delay_ms();
-
void delay_10ms();
-
void delay_100ms();
-
void delay_s();
-
int is_location_error();
-
void matrix();
-
void snk_init();
-
void set_direction();
-
int get_location();
-
int is_location_error();
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >