51单片机实现贪吃蛇(清翔单片机)

编程入门 行业动态 更新时间:2024-10-26 23:25:31

51<a href=https://www.elefans.com/category/jswz/34/1769836.html style=单片机实现贪吃蛇(清翔单片机)"/>

51单片机实现贪吃蛇(清翔单片机)

目录

一、创作背景

二、实现过程

1.一些声明与定义

2.键盘扫描

3.定时器中断

5.蛇的初始化

6.蛇体的移动

7.食物的创建

8.食物的检测和身体的增长

9.检测死亡

10.总代码

总结


一、创作背景

由于大一的一个小作业,花了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;elseDIO = 0;S_CLK = 1;//穿行输入S_CLK = 0;dat1 >>= 1;}for (j = 0; j < 8; j++){if (dat2 & 0x01)DIO = 1;elseDIO = 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]--;//坐标减1if (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;elseDIO = 0;S_CLK = 1;//穿行输入S_CLK = 0;dat1 >>= 1;}for (j = 0; j < 8; j++){if (dat2 & 0x01)DIO = 1;elseDIO = 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]--;//坐标减1if (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();//检验是否死亡
}

总结

代码由于本人实力问题还有部分不足之处希望支持。

更多推荐

51单片机实现贪吃蛇(清翔单片机)

本文发布于:2024-02-11 03:28:32,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1678895.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:单片机   贪吃蛇

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!