C51单片机实现 贪吃蛇

简介: 笔记

一. 实验所需模块


4×4矩阵键盘,8×8点阵,定时器0


二. 模块简介


4×4矩阵键盘


矩阵键盘采用行和列扫描的方式进行判断那个按键被按下了

先进行扫描或先进行列扫描都可以

行扫描的时候,先将行所在的引脚置一,列所在的引脚置0,然后判断行所在引脚的电平的值,随后进行列扫描

8×8点阵


8×8点阵共由64个发光二极管组成,且每个发光二极管是放置在行线和列线的交叉点上。

当某一行置1,某一列置0时,对应的二极管就会点亮。

与动态数码类似,利用视觉残留效果,即可实现动态依次点亮的效果

定时器0


16位定时器由高8位和低8位的两个寄存器组成TH0和TL0。

每来一个脉冲,定时器的值就会加一,当达到最大的时候,就会溢出清零,并产生中断信号。

预先设置TH0和TL0的值,可以得到我们所需的生产中断信号的间隔。


三. 点亮一个灯


这个很简单,就是把某一行线置1,并且把某一列线置0,其他的行线全部为0,列线全部为1.

void  Led(u8 dat,u8 dat2)  //dat为行线值,dat2为列线值
{
  u16 i;
  RCLKS = 1;
  SRCLK = 1;
  for(i=0;i<8;i++)
  {
  //行线的输入是由74HC595模块输入的,
  //通过时钟的上升沿把每一位的数据输入即可,高位优先
    SET = dat>>7;
    dat = dat <<1;
    SRCLK = 0;
    SRCLK =1;
  }
  RCLKS = 0;
  RCLKS = 1;
  P0 = dat2;   //列线由P0端口输入
}

这里令dat = 0x20,dat2=fb的时候,一个发光二极管就被点亮啦!


四. 一个灯的移动


前面我们已经知道了,行线可以控制那一行亮那一行不亮,列线也同样如此。

假设当我们的列线不变,行线左移一位或者右移一位,是不是我们的灯向上或者向下移动了一个!行线不变时,列线的移动同样如此,但是要注意,列线是为0点亮,每一次左移或右移,低位或高位就会多出来一个0,这时,我们还需要加上0x01或者0x80以消掉这个多出来的0


1.向上移动


列线不变,行线左移一位即可
if(Rows[0] != 0x80)  //防止移过头了
  Rows[0] <<=1;


2.向下移动


列线不变,行线右移一位即可
if(Rows[0] != 0x01)
  Rows[0]>>=1;


3.向左移动


行线不变,列线左移一位加0x01即可


if(Cols[0] != 0x7F)
  {
  Cols[0] <<=1;
  Cols[0] += 1;
  }


4.向右移动


行线不变,列线右移一位加0x80即可


if(Cols[0] != 0xFE)
  {
  Cols[0] >>=1;
  Cols[0] += 0x80;
  }


自此,一个灯的上下左右的移动我们已经完成了,接下来蛇的移动了


五. 贪吃蛇


认真看玩过贪吃蛇的朋友应该都清楚,蛇身是随着蛇头的移动而移动的。其中不难发现。当前蛇的某一节的位置是它前面一节蛇的上一个状态的位置,知道了这个规律之后,我们就可以很方便地更新蛇的状态了。

由于8×8点阵显示有限,我就只设置了蛇的最大长度为5,防止闪烁。


1.蛇的存储


这里我采取的是数组的方式,存储每一节的行线值和列线值


u8 Rows[5];  //行线值
u8 Cols[5];  //列线值


2.蛇的更新


前面说过,蛇的当前蛇的某一节的位置是它前面一节蛇的上一个状态的位置,也就是说,Rows[n-1] = Rows[n],Cols[n-1] = Cols[n]
当然这里的Rows[n],和Cols[n]是上一个状态的值了


void Up(u8 len)   //当前蛇的长度
{
  u8 i;
  u8 tmp = Rows[0];
  u8 rr,cc_1,cc_2;
  if(Rows[0] != 0x80)
  Rows[0] <<=1;
  cc_1 = Cols[0];
        //后一节依次等于前一节的上一个状态的值
  for(i=1;i<len;i++)
  {
  rr = Rows[i];  // 保存上一个状态
  Rows[i] =tmp;  //Rows[i]为当前状态
  tmp = rr;
  cc_2 = Cols[i];   // 保存上一个状态
  Cols[i] = cc_1;   
  cc_1 = cc_2;
  }
}


这里就只拿了向上移动的代码作为参考,其他方向的移动类型,基本上一样,这里蛇的移动也完成了。


3.食物


食物的行线与列线同样用两个变量存储
食物的位置是随机的,这里我们用rand产生随机数并对8求余数,
这样就得到了0-7的随机数,然后对0x01进行相应的左移,就得到了
选定的行,取反就得到了选定的列。
Food_col = ~(0x01 << rand()%8);
Food_row = 0x01 << rand()%8;


4.吃到食物


这也是很好办的事,只需要判断蛇头的Row和Col是否等于食物的Row和Col,
如果相等,则更新食物的位置,蛇身加一.


if((Rows[0] == Food_row) && (Cols[0] == Food_col)) // 判断是否吃到食物
  {
    if(len<5)  //判断蛇长是否达到最大值
    {
    Rows[len] = Rows[len-1];
    Cols[len] = Cols[len-1];
    len++;
    }
    //随机更新食物的位置
    Food_col = ~(0x01 << rand()%8);
    Food_row = 0x01 << rand()%8;
  }


自此,贪吃蛇可以说已经基本完成了,剩下的就是用键盘来控制蛇的移动方向了


六. 蛇方向的控制


方向的控制我把它放在中断里面,这样可以解决如下问题


如果不用延迟,蛇移动得太快,根本看不清楚蛇的移动。

如果用延迟来控制蛇移动的速度,那么键盘的读取就会变动不灵敏,

要按下许久之后,才能读取,游戏效果不好。

因此我决定把它放在定时器中断中,通过设定初值,以极小的时间差,来读取键盘的内容,即可达到实时的效果,而且延时对这个完全没有干扰。


void Time_0() interrupt 1
{
  u8 key;
  key = KEY();
  if(key != -1)
  {
  if(key == 1)
  {
    //Up(len);
    direct = 0;
    delay(10000); // 不用也没有关系,不会造成影响
  }
  if(key == 9)
  {
    //Down(len);
    direct = 1;
    delay(10000);
  }
  if(key == 4)
  {
    //Left(len);
    direct = 2;
    delay(10000);
  }
  if(key == 6)
  {
    //Right(len);
    direct = 3;
    delay(10000);
  }
  }
  //防止p过大
  if(p>8*100)
  p = 0;
  p++;
  //每8次移动一下,这是合理的,由于每一次的时间差极小
  //8次加起来的时间差也是非常小的,
  //该时间差小于先后按下两个不同方向的时间差,因为这是合理的
  if(p%8 == 0)
  {
  switch(direct)
  {
  case 0: Up(len);break;
  case 1: Down(len);break;
  case 2: Left(len);break;
  case 3: Right(len);break;
  }
  }
  TH0 = 0x0c;
  TL0 = 0x0c;
}


七. 最后就是main函数啦


这部分也是非常简单的。


void main()
{
  u8 i;
  //u8 len = 1;  // 初始化长度,这个放到全局变量中了
  u8 Food_row = 0x20;
  u8 Food_col = 0xfb;
  //u8 direct = 0;// 初始化方向,这个放到全局变量中了
  Rows[0] = Row;  //初始化,蛇头的位置
  Cols[0] = Col;
  Init_time_0();   //配置定时器0,开启中断
  while(1)
  {
  //判断蛇头是否到达食物的位置
  if((Rows[0] == Food_row) && (Cols[0] == Food_col))
  {
    if(len<5)
    {
    Rows[len] = Rows[len-1];
    Cols[len] = Cols[len-1];
    len++;
    }
                        //随机更新食物的位置
                Food_col = ~(0x01 << rand()%8);
                Food_row = 0x01 << rand()%8;
  }
  Led(Food_row,Food_col);  //显示食物
  for(i=0;i<len;i++)
    Led(Rows[i],Cols[i]);  // 显示蛇
  //这里用延时来控制速度的话,容易产生闪烁。
  }
}


至此贪吃蛇就完成啦!!!!


Thank for your reading !!!


公众号:FPGA之旅

目录
相关文章
|
7月前
|
数据格式
用C51单片机制作LED流水的灯
用C51单片机制作LED流水的灯
|
监控 数据处理 开发工具
单片机的C51基础知识介绍2
三、单片机的开发流程和工具 单片机的开发流程通常包括以下几个步骤: 硬件设计:根据应用需求,设计和选择合适的外部电路和接口电路,包括时钟电路、输入输出电路、存储器等。 软件开发:根据应用需求,使用相应的开发工具和编程语言进行软件开发,如C语言、汇编语言等。开发人员需要编写相应的代码,实现系统的功能和控制。 编译和烧录:将开发好的软件代码进行编译和链接,生成可执行的机器代码。然后将机器代码通过编程器烧录到单片机的存储器中。 调试和测试:将烧录好的单片机进行调试和测试,验证系统的功能和性能是否符合要求。可以通过调试工具和示波器等设备进行调试和测试。 单片机的开发工具和软件包括: 开发板
91 0
|
5月前
|
传感器 存储 程序员
《单片机原理与应用及C51编程技术》期末复习笔记
《单片机原理与应用及C51编程技术》期末复习笔记
|
6月前
|
数据安全/隐私保护
单片机课程设计——基于C51电子密码锁(源代码)
单片机课程设计——基于C51电子密码锁(源代码)
|
7月前
|
存储 移动开发
80C51单片机----数据传送类指令
80C51单片机----数据传送类指令
210 2
|
7月前
|
存储 移动开发 程序员
80C51单片机的七种寻址方式
80C51单片机的七种寻址方式
138 1
|
7月前
|
数据安全/隐私保护
C51单片机实现矩阵键盘密码锁
C51单片机实现矩阵键盘密码锁
|
存储 编译器 开发工具
单片机的C51基础知识介绍1
C51是指Intel公司推出的一款8位单片机,它的全称是Intel MCS-51系列单片机,也被称为8051系列单片机。C51系列单片机是一种非常经典和常用的单片机,具有广泛的应用领域和丰富的资源支持。下面将介绍C51单片机的基础知识。 一、C51单片机的特点和优势 1. 8位架构:C51单片机采用8位架构,即每个数据位的宽度为8位,能够处理8位的数据,适用于大部分嵌入式应用。 2. 高度集成:C51单片机具有高度集成的特点,包括CPU、存储器、输入输出端口、定时器、串口等功能模块,能够满足大多数应用的需求。 3. 低功耗设计:C51单片机采用低功耗设计,能够在低电压和低功耗的环境下正常工作,
477 0
【C51单片机】中断系统之单一外中断应用
【C51单片机】中断系统之单一外中断应用
C51单片机1.点亮一个LED灯
C51单片机1.点亮一个LED灯
334 0