历史上的今天
返回首页

历史上的今天

今天是:2025年03月24日(星期一)

正在发生

2020年03月24日 | 基于51单片机的贪吃蛇小程序(8*8LED点阵实现)

2020-03-24 来源:eefocus

一直很想写一个贪吃蛇的小程序,这两天终于抽空完成了,这里把我的思路分享给大家,仅供参考!


代码如下:

先放段主函数压压惊


void main()

{

x_s[0] = 2;    //初始化三个点

y_s[0] = 3;

x_s[1] = 3;

y_s[1] = 3;

x_s[2] = 4;

y_s[2] = 3;

Timer0Init();

suijishu();

while(1)

{

   appear();

   if(flag_a) //蛇身移动位

   {

   if(!flag_s)   //蛇身死亡位

    {

auto_move();

HC_595(0x00);     //消隐

P0 = 0xFF; //消隐

shensi();

chiguo();

delay(1000);

flag_a = 0;

}

}

}

}


/*

本程序核心程序:

appear函数,显示蛇身

auto_move函数,控制蛇身惯性移动 ,同时里面附了判断是否穿墙的一段代码

direction函数,控制蛇的移动方向

suijishu(随机数)函数,随机生成果子

chiguo(吃果)函数,判断是否吃到果子

shensi(身死)函数,碰到自己身体则死亡


*/


#include

#include

#include

#define M 12 //难度系数,12中等,越小越难

typedef unsigned char uchar;

typedef unsigned int uint;


sbit SRCLK = P3^6;   //595芯片

sbit RCLK = P3^5;

sbit SER = P3^4;


sbit LSA=P2^2; //138译码器

sbit LSB=P2^3;

sbit LSC=P2^4;


uchar code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,

0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值


//定义方向键

sbit K1 = P3^1; //上 

sbit K2 = P3^0; //下 

sbit K3 = P3^2; //左

sbit K4 = P3^3; //右 


uchar m; //储存果子的X坐标

uchar n; //储存果子的Y坐标


uchar snakelong = 3;    //初始化蛇长

char flag_x = 1;    //初始化运动方向

char flag_y = 0;    //初始化运动方向

uchar flag_gg = 0;    //生成果子的重复性标志

uchar flag_c = 0, sheshen = 0;   //吃果标志

uchar flag_a = 0; //移动标志

uchar flag_s = 0; //死亡标志

uchar x_s[32] = {0},y_s[32] = {0};    //定义蛇的最大长度

uchar code X_[8] = {0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0xFE};  

uchar code Y_[8] = {0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};    


/*  

y1 //定义x,y坐标

y2   //如,我想让(4,2)亮,只需要P0 = X_[4];HC_595 = Y_[2];

y3

y3

y5

y6

y7

y8

  x1    x2   x3   x4  x5   x6   7   x8

*/


一个小的延时函数:



void suijishu();    //提前定义随机数生成函数

void delay(uint i)    //简单的delay函数,对于51单片机,delay(1)约等于9微秒

{

while(i--);

}


定时器初始化:



void Timer0Init()   //定时器初始化函数    模式1,16位定时器

{

TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。


TH0=0XFC; //给定时器赋初值,定时1ms

TL0=0X18;

ET0=1;//打开定时器0中断允许

EA=1;//打开总中断

TR0=1;//打开定时器

}


两个显示函数,分别控制点阵和数码管;


void HC_595(uchar date) //595例程,这里就不详细解释了

{

uchar a;

SRCLK = 0;

RCLK = 0;


for(a=0;a<8;a++)

{

SER = date>>7;

date<<=1;

SRCLK = 1;

_nop_();

_nop_();

SRCLK = 0;

}

RCLK = 1;

_nop_();

_nop_();

RCLK = 0;

}


void display() //数码管显示,用来显示蛇长

{

uchar i,b=2;

HC_595(0x00);     //消隐

_nop_();

P0=0x00;   //因为我用的板子上数码管和点阵共用的P0口,所以这里消隐的时候需要特别注意。

_nop_();

for(i=0;i<2;i++)

{

while(b) {    //这里循环两次是为了让数码管显示更清楚一点。

switch(i) //位选,选择点亮的数码管

{

case(0):

LSA=0;LSB=0;LSC=0; P0=smgduan[(snakelong%10)];delay(10);P0=0x00;break;

case(1):

LSA=1;LSB=0;LSC=0; P0=smgduan[(snakelong/10)];delay(10);P0=0x00;break;

}

_nop_();

b--;

}

}

LSA=1;LSB=1;LSC=1; P0=0xFF; //还是为了消隐。以一个数码管常亮为代价,以求不影响点阵和数码管显示蛇长      

}


显示蛇身,核心函数之一:


void appear() // viod 蛇身出现     

{

/* x_s[0] = 2; //初始化三个点

y_s[0] = 3;

x_s[1] = 3;

y_s[1] = 3;

x_s[2] = 4;

y_s[2] = 3; */

     

//uint t = 150; //t越小越难,越大越简单 //这里也可以控制难度,经测试也有效。

//while(t)

//{

uchar i;

for(i=0;i {

if((x_s[i]!=0)&&(y_s[i]!=0))

{

P0 = X_[x_s[i]-1];

HC_595(Y_[y_s[i]-1]);

delay(100);    //延时

P0 = 0xFF; //消隐

HC_595(0x00);     //消隐

}

}

// t--;

//}


}


自动惯性移位函数:


void auto_move() // viod 自动移动   核心程序之一

{

uchar i;

for(i=0;i<(snakelong-1);i++)

{

x_s[i] = x_s[i+1];

y_s[i] = y_s[i+1];

}

x_s[snakelong-1] += flag_x;

y_s[snakelong-1] += flag_y;


if(x_s[snakelong-1]>8) // void 穿墙判断  

x_s[snakelong-1] = 1;

else if(x_s[snakelong-1]<1)

x_s[snakelong-1] = 8;

if(y_s[snakelong-1]>8)

y_s[snakelong-1] = 1;

else if(y_s[snakelong-1]<1)

y_s[snakelong-1] = 8;

}


键盘扫描(独立键盘),控制蛇的移动方向:


void direction() // void 方向控制 

{

if(!K1) 

{

delay(1000);

if(!K1)

{

if(flag_y == 1)    //是否正在下降 ,若是,则点击上升无用。

{flag_y = 1;}

   else

    {

flag_x = 0;

flag_y = -1; //shang

}

}

}

else if(!K2) 

{

delay(1000);

if(!K2)

{

if(flag_y == -1)    //是否正在上升

{flag_y = -1;}

else

{

flag_x = 0;

flag_y = 1; //xia

}

}

}

else if(!K3) 

{

delay(1000);

if(!K3)

{

if(flag_x == 1)    //是否正在右移

{flag_x = 1;}

else

{

flag_x = -1; //zuo

flag_y = 0;

}

}

}

else if(!K4) 

{

delay(1000);

if(!K4)

{

if(flag_x == -1)    //是否正在左移

{flag_x = -1;}

else

{

flag_x = 1; //you

flag_y = 0;

}

}

}

}


吃果函数,吃到果子蛇头增长


void chiguo() // void 吃果判断

{

if(((x_s[snakelong-1]+flag_x)==m)&&((y_s[snakelong-1]+flag_y)==n)  )

{ flag_c = 1; }

else if(((x_s[snakelong-1]+flag_x)==m)&&((y_s[snakelong-1]+flag_y)==n)  ) //重复两次防止程序bug——穿过果子但不去吃     

{ flag_c = 1; }

else if(((x_s[snakelong-2]+flag_x)==m)&&((y_s[snakelong-2]+flag_y)==n)  ) //放宽吃果判定,以防止出现bug——穿过果子但不去吃               

{ flag_c = 1; sheshen = 1;    }

else if(((x_s[snakelong-3]+flag_x)==m)&&((y_s[snakelong-3]+flag_y)==n)  )

{ flag_c = 1; sheshen = 2;    }

   

if(flag_c)

{

flag_c = 0;

TR0 = 0;//关闭定时器

snakelong++;

if(sheshen == 1)

{

x_s[snakelong-1] = m+flag_x;

y_s[snakelong-1] = n+flag_y;

sheshen = 0;

}

else if(sheshen == 2)

{

x_s[snakelong-1] = m+2*flag_x;

y_s[snakelong-1] = n+2*flag_y;

sheshen = 0;

}

else

{ //这才是吃果的正常情况

    x_s[snakelong-1] = m;

y_s[snakelong-1] = n;

sheshen = 0;

}

        suijishu();   //再生成一个果子

P0 = 0xFF; //消隐

HC_595(0x00);     //消隐

appear();

appear();

P0 = 0xFF; //消隐

HC_595(0x00);     //消隐

TR0=1;//打开定时器

}

}


碰到自己身体死亡函数


void shensi() // void 身死    碰到自己身死

{

uchar b,x_1,y_1;

if(snakelong>4)

{

for(b=0;b   {

if(((x_s[snakelong-1])==x_s[b])&&((y_s[snakelong-1])==y_s[b])  )  

{

flag_s = 1; 

}

  }

if(flag_s == 1)

{

P0 = 0x00;HC_595(0xFF);delay(2000);    //闪烁两次

P0 = 0xFF;HC_595(0x00);delay(2000);

P0 = 0x00;HC_595(0xFF);delay(3000);

P0 = 0xFF;HC_595(0x00);delay(1000);

x_1 = x_s[snakelong-1] ;

y_1 = y_s[snakelong-1] ;      //卡在死亡状态的下一刻  

  snakelong++;

x_s[snakelong-1] = x_1 + flag_x;

y_s[snakelong-1] = y_1 + flag_y;

}


}

}


随机生成果子:



void suijishu() //随机数,生成果子

{

uchar b,m_1,n_1;

do{

flag_gg = 0;

m_1 = (rand()%8)+1;    

n_1 = (rand()%8)+1;

for(b=0;b   {

if((m_1==x_s[b])&&(n_1==y_s[b])) //如果果子和蛇身有重复,则flag_gg置1,再生成一次                    

flag_gg = 1;

  }

}while(flag_gg);

m = m_1;

n = n_1;

}


主函数:


void main()

{

x_s[0] = 2;    //初始化三个点

y_s[0] = 3;

x_s[1] = 3;

y_s[1] = 3;

x_s[2] = 4;

y_s[2] = 3;

Timer0Init();

suijishu();

while(1)

{

   appear();

   if(flag_a) //蛇身移动位

   {

   if(!flag_s)   //蛇身死亡位

    {

auto_move();

HC_595(0x00);     //消隐

P0 = 0xFF; //消隐

shensi();

chiguo();

delay(1000);

flag_a = 0;

}

}

}

}


/*

果子的函数放在了中断里,1ms一次进行显示,同时键盘也是1mS一次进行扫描

通过调节M的大小来实现蛇的延时移动。

*/


void guozi() interrupt 1 // void 果子出现

{

uint a;

uchar i=3;

a++;

direction();   //用中断去扫描键盘,1mS/次

P0 = 0xFF; //消隐

HC_595(0x00);     //消隐

while(i)   //重复以增加亮度

{

P0 = X_[m-1];

HC_595(Y_[n-1]);

i--;

}

delay(100);

P0 = 0xFF; //消隐 ,因为我用的板子上数码管和点阵共用的P0口,所以这里消隐的时候需要特别注意

HC_595(0x00);     //消隐


_nop_();

P0=0x00;  //数码管消隐

_nop_();

display();

delay(100);


HC_595(0x00);     //消隐

_nop_();

LSA=1;LSB=1;LSC=1; P0 = 0xFF;

if (a>M)   //蛇身移动控制位

{

a = 0;

flag_a = 1;

}


/*

编写过程中共累计进行了两次优化,第一次对1,随机生成果子,2,吃果判定的放宽,3,碰到蛇身死亡,以及4,正在上升/下降时,不能下降上升

正在左移/右移时,不能右移/左移 进行了加入以及优化

第二次对数码管显示问题,吃到果子延时增长问题进行了解决,同时对死亡时的效果进行了优化


调试经验:

建议初学者首先构思好程序框架,想清楚程序需要哪几个必要模块(因此建议程序书写模块化),然后

对几个必要模块进行相对独立的调试,进而进行整合,确保基础功能的实现。

在此之后,在对程序出现的问题进行解决和优化。


*/

推荐阅读

史海拾趣

GE Solid State公司的发展小趣事
在某些无法直接观察光线的环境中,如暗室或夜间拍摄时,可听的曝光表电路提供了一种便捷的曝光判断方式。
Global Power Technology Co., Ltd公司的发展小趣事
冰箱显示屏不亮、按键无反应等。
CDIL[Continental Device India Pvt. Ltd.]公司的发展小趣事

CDIL采用无晶圆厂模式生产分立半导体器件,专注于功率半导体和高可靠性组件的研发与生产。这一模式使CDIL能够更加灵活地应对市场变化,降低生产成本,提高产品竞争力。同时,公司还采用自己的封装技术,确保产品能够满足太空应用等严苛环境的严格标准。

博通集成(BEKEN)公司的发展小趣事

随着公司规模的扩大和市场需求的增长,博通集成开始实施国际化战略,积极拓展海外市场。公司在全球范围内设立了多个子公司和技术分部,以便更好地服务全球客户。通过国际化战略和全球布局,博通集成不仅提升了企业的国际竞争力,还为全球用户提供了更加优质的产品和服务。

这些故事展示了博通集成在电子行业中的发展历程和成就。通过不断的技术创新、市场拓展、战略合作和国际化战略的实施,博通集成逐渐发展成为无线连接芯片设计领域的领军企业。未来,随着无线通讯技术的不断发展和应用领域的不断拓展,博通集成将继续保持创新精神和市场敏锐度,推动企业的持续发展。

Heidenhain Corp公司的发展小趣事

面对电子行业日益增长的自动化需求,海德汉公司凭借其丰富的自动化解决方案,助力多家电子企业实现了生产线的智能化升级。某大型电子制造厂采用海德汉的自动化控制系统和光栅尺技术,对生产线进行了全面改造。改造后的生产线实现了从原材料上料到成品下线的全自动化作业,大大提高了生产效率和产品质量稳定性。同时,通过集成海德汉的MES系统,实现了生产数据的实时监控和追溯,为企业的精细化管理提供了有力支持。

B&F;公司的发展小趣事

随着航空技术的不断发展,B&F公司开始积极探索与其他行业的跨界合作。例如,公司与一家知名电子公司合作,共同研发了一款新型飞行控制系统。这款系统采用了最先进的电子技术和算法,大大提高了飞机的操控性能和飞行效率。通过这种跨界合作,B&F公司不仅拓展了业务领域,还为公司带来了更多的发展机遇。

问答坊 | AI 解惑

DSP算法大全C语言版本

DSP算法大全C语言版本…

查看全部问答>

proteus介绍

本帖最后由 paulhyde 于 2014-9-15 09:04 编辑 学习proteus软件的,可以看一下。  …

查看全部问答>

EEW-DSO-V2.00设计思路

这段时间一直和网友东哥在讨论第二版的设计,大概确定了一下,下个版本的设计方案,采样芯片应该会换成ADC08100,最高100M采样,用两片IS61LV25616-10+CPLD来做FIFO,两片轮流寸,实现100M的读写速度.存储深度比上次用了很大的升级,CPU换成带外部总线的STM ...…

查看全部问答>

USB 2.0 规范

USB 2.0 规范 USB 是一种支持热插拔的高速串行传输总线,它使用差分信号来传输数据,最高速度 可达480Mb/S。USB 支持“总线供电”和“自供电”两种供电模式。在总线供电模式下, 设备最多可以获得500mA 的电流。USB2.0 被设计成为向下兼容的模式 ...…

查看全部问答>

请各位分析这样跳槽可以吗?

目前在公司担任研发主管,负责研发部门资源团队,个人觉的资源团队搞起来没啥意思,而且本人在资源团队搞了10年了。现在想需求更大发展。准备跳槽到另一家民营公司担任研发中心下一个研发一部担任部长,负责整个研发部门的工作。我想这样对这样生涯 ...…

查看全部问答>

请问tilt wheel mouse中的WM_MOUSEHWHEEL消息如何在Win200/XP DDK中实现呀?

在Vista中,有mouse消息WM_MOUSEHWHEEL,而在2000/XP中则没有,我想写个mousefilter在2000/XP下来支持该功能,我该如何做呢?在MouseServiceCallback()当中做吗?我看在MOUSE_INPUT_DATA结构中不知怎样才能实现.…

查看全部问答>

酬金500元,北京,请教51单片机的TCP/IP问题

本人在北京,现有一块杭州晶控电子的51NET以太网开发板,开发板介绍:http://www.hificat.com/net/net.asp,想请教一下附带光盘里的单片机源码,(源码已上传,在附件里)。 内容:讲解源码,源码我没仔细看过,是单片机和PC通信的内容,应该是TCP ...…

查看全部问答>

变频器已运行就产生很大的干扰

我是做工控软件的,最近遇到一个让人恼火的问题,我们设备上采用了台安的变频器,我在用串口调试器调试通信时,当变频器运行起来时,一打开串口就有大量的乱码传过来,很是痛苦!!! 什么接地、加电容都试了,,没有解决,,请问各位达人有何高招 ...…

查看全部问答>

gps记录器进度:已能够将主要的数据写入sd卡

已上传工程文件。这个程序会在写入三十行数据都自动停止,以便将书库卡拔出读取,利用库写入sd卡是很简单的…

查看全部问答>