目录
一、创作背景
由于大一的一个小作业,花了10天完成了这个小项目,中途克服了很多困难,发此文章以作纪念。
二、实现过程
1.一些声明与定义
这些后面会讲到。
#include <reg52.h>
#include <stdlib.h>
#define uint unsigned int
#define uchar unsigned char
#include <intrins.h>
sbit DIO = P3 ^ 4;
sbit S_CLK = P3 ^ 5;
sbit R_CLK = P3 ^ 6;
sbit key_s2 = P3 ^ 0;//独立键盘4个按键
sbit key_s3 = P3 ^ 1;
sbit key_s4 = P3 ^ 2;
sbit key_s5 = P3 ^ 3;
uchar coorx[8] = { 0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe };
uchar coory[8] = { 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 };
uchar snakex[20];//蛇体x坐标
uchar snakey[20];//蛇体y坐标
uchar m = 0;
uchar len = 1;//蛇体长度
uchar foodx, foody;//食物x, y坐标
uchar d, xx, yy;
2.键盘扫描
这里我用的是独立键盘,由于点阵屏的IO口和矩阵键盘的IO口冲突导致显示的内容不对(个人能力有限,暂时没办法解决这个冲突问题,只好避开),所以采用了独立键盘。同时也是因为这个原因我没有采用扫描的方式,而是一个一个判断。如果开发板不冲突可以选择矩阵键盘。
char key = 0;
void delay(uint z)
{
uint x, y;
for (x = z; x > 0; x--)
for (y = 114; y > 0; y--);
}
void keyscan()
{
if (key_s2 == 0)
{
delay(10);
if (key_s2 == 0)
{
if (key != 4)
key = 1;
}
}
if (key_s3 == 0)
{
delay(10);
if (key_s3 == 0)
{
if (key != 3)
key = 2;
}
}
if (key_s4 == 0)
{
delay(10);
if (key_s4 == 0)
{
if (key != 2)
key = 3;
}
}
if (key_s5 == 0)
{
delay(10);
if (key_s5 == 0)
{
if (key != 1)
key = 4;
}
}
}
3.定时器中断
这里是相关寄存器的配置,定时为5ms,用处是为了控制蛇的移动速度已经键盘的判断。
void timer0Init()
{
EA = 1;
ET0 = 1;
TR0 = 1;
TMOD = 0X01;
TH0 = 0XED;
TL0 = 0XFF;
}
void SendByte(uchar x, uchar y)//用来接收行列值,这里x和y是坐标,点阵屏左下角为坐标原点
{
uchar i, j, dat1, dat2;
dat1 = coorx[x - 1];//这里通过前面定义的两个数组将坐标转化为了行列的十六进制数
dat2 = coory[y - 1];
for (i = 0; i < 8; i++)
{
if (dat1 & 0x01)
DIO = 1;
else
DIO = 0;
S_CLK = 1;//穿行输入
S_CLK = 0;
dat1 >>= 1;
}
for (j = 0; j < 8; j++)
{
if (dat2 & 0x01)
DIO = 1;
else
DIO = 0;
S_CLK = 1;//串行输入
S_CLK = 0;
dat2 >>= 1;
}
R_CLK = 1;//并行输出
R_CLK = 0;
}
5.蛇的初始化
main函数主要内容
void main()
{
timer0Init();//定时器0初始化
snakex[0] = 5;//蛇体初始x坐标
snakey[0] = 5;//蛇初始y坐标
creat_food();
while (1)
{
snakemove();
FoodAnd();
}
}
void timer0() interrupt 1
{
TH0 = 0XED;
TL0 = 0XFF;
m++;//每加到一百蛇体移动一次,速度可以自己调
keyscan();//键盘扫描
for (d = 0; d < len; d++)
{
SendByte(snakex[d], snakey[d]);//将蛇体显示在点阵屏上,参数是xy坐标
}
SendByte(foodx, foody);//显示食物
CheckDead();//检验是否死亡
}
6.蛇体的移动
void left()//上下左右都是一个道理
{
uchar i, x1, x2, y1, y2;
x1 = snakex[0];//保留上一结点的x轴状态
snakex[0]--;//坐标减1
if (snakex[0] == 0)//这里是判定是否碰到边界了,这里设定的是可以从另一边出来,当然也可以设定为碰到就死
snakex[0] = 8;
y1 = snakey[0];//保留上一结点的y轴状态
for (i = 1; i < len; i++)
{
y2 = snakey[i];//进行各个结点的状态移动,形成蛇运动的效果
snakey[i] = y1;
y1 = y2;
x2 = snakex[i];
snakex[i] = x1;
x1 = x2;
}
}
void up()
{
uchar i, x1, x2, y1, y2;
y1 = snakey[0];
snakey[0]--;
if (snakey[0] == 0)
snakey[0] = 8;
x1 = snakex[0];
for (i = 1; i < len; i++)
{
x2 = snakex[i];
snakex[i] = x1;
x1 = x2;
y2 = snakey[i];
snakey[i] = y1;
y1 = y2;
}
}
void down()
{
uchar i, x1, x2, y1, y2;
y1 = snakey[0];
snakey[0]++;
if (snakey[0] == 9)
snakey[0] = 1;
x1 = snakex[0];
for (i = 1; i < len; i++)
{
y2 = snakey[i];
snakey[i] = y1;
y1 = y2;
x2 = snakex[i];
snakex[i] = x1;
x1 = x2;
}
}
void right()
{
uchar i, x1, x2, y1, y2;
x1 = snakex[0];
snakex[0]++;
if (snakex[0] == 9)
snakex[0] = 1;
y1 = snakey[0];
for (i = 1; i < len; i++)
{
x2 = snakex[i];
snakex[i] = x1;
x1 = x2;
y2 = snakey[i];
snakey[i] = y1;
y1 = y2;
}
}
void snakemove()
{
if (key == 1 && m == 100)//这里是判断按键值并且m要达到100,也就是500ms移动一次
{
left();
m = 0;//要清一下零
}
if (key == 2 && m == 100)
{
up();
m = 0;
}
if (key == 3 && m == 100)
{
down();
m = 0;
}
if (key == 4 && m == 100)
{
right();
m = 0;
}
}
7.食物的创建
这一部分本来以为使用c语言里面srand(unsigned seed)(这个函数只要种子的值是变化的生成的就是某种意义上随机数)函数加上一个一直变化的时间戳就可以生成随机数的,但是后来发现keil里面没有time.h这个头文件,导致没法实现真正意义的随机数。这个问题暂留
uchar CheckFood()//检验食物有没有和蛇体重合
{
uchar i;
for (i = 0; i < len; i++)
{
if (foodx == snakex[i] && foody == snakey[i])
{
return 1;
}
}
return 0;
}
void creat_food()
{
do
{
foodx = (uchar)(rand() % 8 + 1);
foody = (uchar)(rand() % 8 + 1);
} while (CheckFood() != 0);
}
8.食物的检测和身体的增长
void FoodAnd()//检验食物有没有被吃掉
{
if (foodx == snakex[0] && foody == snakey[0])
{
if (len < 20)
{
snakex[len] = snakex[len - 1];//和之前一样,把上一个结点的状态赋给下一个结点,用来增加长度
snakey[len] = snakey[len - 1];
len++;
}
creat_food();//被吃掉重新生成食物
}
}
9.检测死亡
这里i = 2是因为等于1的时候会在吃食物的时候冲突,而蛇头不可能碰到碰到第二节,所以初值设定为2
void CheckDead()
{
uchar i;
for (i = 2; i < len; i++)
{
if (snakex[0] == snakex[i] && snakey[0] == snakey[i])
{
while (1);
}
}
}
10.总代码
#include <reg52.h>
#include <stdlib.h>
#define uint unsigned int
#define uchar unsigned char
#include <intrins.h>
sbit DIO = P3 ^ 4;
sbit S_CLK = P3 ^ 5;
sbit R_CLK = P3 ^ 6;
sbit key_s2 = P3 ^ 0;
sbit key_s3 = P3 ^ 1;
sbit key_s4 = P3 ^ 2;
sbit key_s5 = P3 ^ 3;
uchar coorx[8] = { 0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe };
uchar coory[8] = { 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 };
uchar snakex[20];
uchar snakey[20];
uchar m = 0;
uchar len = 1;
uchar foodx, foody;
uchar d, xx, yy;
char key = 0;
void delay(uint z)
{
uint x, y;
for (x = z; x > 0; x--)
for (y = 114; y > 0; y--);
}
void keyscan()
{
if (key_s2 == 0)
{
delay(10);
if (key_s2 == 0)
{
if (key != 4)
key = 1;
}
}
if (key_s3 == 0)
{
delay(10);
if (key_s3 == 0)
{
if (key != 3)
key = 2;
}
}
if (key_s4 == 0)
{
delay(10);
if (key_s4 == 0)
{
if (key != 2)
key = 3;
}
}
if (key_s5 == 0)
{
delay(10);
if (key_s5 == 0)
{
if (key != 1)
key = 4;
}
}
}
void SendByte(uchar x, uchar y)//用来接收行列值,这里x和y是坐标,点阵屏左下角为坐标原点
{
uchar i, j, dat1, dat2;
dat1 = coorx[x - 1];//这里通过前面定义的两个数组将坐标转化为了行列的十六进制数
dat2 = coory[y - 1];
for (i = 0; i < 8; i++)
{
if (dat1 & 0x01)
DIO = 1;
else
DIO = 0;
S_CLK = 1;//穿行输入
S_CLK = 0;
dat1 >>= 1;
}
for (j = 0; j < 8; j++)
{
if (dat2 & 0x01)
DIO = 1;
else
DIO = 0;
S_CLK = 1;//串行输入
S_CLK = 0;
dat2 >>= 1;
}
R_CLK = 1;//并行输出
R_CLK = 0;
}
uchar CheckFood()//检验食物有没有和蛇体重合
{
uchar i;
for (i = 0; i < len; i++)
{
if (foodx == snakex[i] && foody == snakey[i])
{
return 1;
}
}
return 0;
}
void creat_food()
{
do
{
foodx = (uchar)(rand() % 8 + 1);
foody = (uchar)(rand() % 8 + 1);
} while (CheckFood() != 0);
}
void FoodAnd()//检验食物有没有被吃掉
{
if (foodx == snakex[0] && foody == snakey[0])
{
if (len < 20)
{
snakex[len] = snakex[len - 1];//和之前一样,把上一个结点的状态赋给下一个结点,用来增加长度
snakey[len] = snakey[len - 1];
len++;
}
creat_food();//被吃掉重新生成食物
}
}
void left()//上下左右都是一个道理
{
uchar i, x1, x2, y1, y2;
x1 = snakex[0];//保留上一结点的x轴状态
snakex[0]--;//坐标减1
if (snakex[0] == 0)//这里是判定是否碰到边界了,这里设定的是可以从另一边出来,当然也可以设定为碰到就死
snakex[0] = 8;
y1 = snakey[0];//保留上一结点的y轴状态
for (i = 1; i < len; i++)
{
y2 = snakey[i];//进行各个结点的状态移动,形成蛇运动的效果
snakey[i] = y1;
y1 = y2;
x2 = snakex[i];
snakex[i] = x1;
x1 = x2;
}
}
void up()
{
uchar i, x1, x2, y1, y2;
y1 = snakey[0];
snakey[0]--;
if (snakey[0] == 0)
snakey[0] = 8;
x1 = snakex[0];
for (i = 1; i < len; i++)
{
x2 = snakex[i];
snakex[i] = x1;
x1 = x2;
y2 = snakey[i];
snakey[i] = y1;
y1 = y2;
}
}
void down()
{
uchar i, x1, x2, y1, y2;
y1 = snakey[0];
snakey[0]++;
if (snakey[0] == 9)
snakey[0] = 1;
x1 = snakex[0];
for (i = 1; i < len; i++)
{
y2 = snakey[i];
snakey[i] = y1;
y1 = y2;
x2 = snakex[i];
snakex[i] = x1;
x1 = x2;
}
}
void right()
{
uchar i, x1, x2, y1, y2;
x1 = snakex[0];
snakex[0]++;
if (snakex[0] == 9)
snakex[0] = 1;
y1 = snakey[0];
for (i = 1; i < len; i++)
{
x2 = snakex[i];
snakex[i] = x1;
x1 = x2;
y2 = snakey[i];
snakey[i] = y1;
y1 = y2;
}
}
void CheckDead()
{
uchar i;
for (i = 2; i < len; i++)
{
if (snakex[0] == snakex[i] && snakey[0] == snakey[i])
{
while (1);
}
}
}
void snakemove()
{
if (key == 1 && m == 100)//这里是判断按键值并且m要达到100,也就是500ms移动一次
{
left();
m = 0;//要清一下零
}
if (key == 2 && m == 100)
{
up();
m = 0;
}
if (key == 3 && m == 100)
{
down();
m = 0;
}
if (key == 4 && m == 100)
{
right();
m = 0;
}
}
void timer0Init()
{
EA = 1;
ET0 = 1;
TR0 = 1;
TMOD = 0X01;
TH0 = 0XED;
TL0 = 0XFF;
}
void main()
{
timer0Init();//定时器0初始化
snakex[0] = 5;//蛇体初始x坐标
snakey[0] = 5;//蛇初始y坐标
creat_food();
while (1)
{
snakemove();
FoodAnd();
}
}
void timer0() interrupt 1
{
TH0 = 0XED;
TL0 = 0XFF;
m++;//每加到一百蛇体移动一次,速度可以自己调
keyscan();//键盘扫描
for (d = 0; d < len; d++)
{
SendByte(snakex[d], snakey[d]);//将蛇体显示在点阵屏上,参数是xy坐标
}
SendByte(foodx, foody);//显示食物
CheckDead();//检验是否死亡
}
总结
代码由于本人实力问题还有部分不足之处希望支持。