51单片机项目实战---贪吃蛇(测试版)


来源: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


  1. #include "snake.h"


  2. int error = 0;

  3. int time=0;


  4. void interrupt_init()

  5. {

  6.     EA = 0;             //关闭总中断

  7.     IT0 = 1;        //外部中断0方式 下降沿

  8.     EA = 1;         //开启总中断

  9.     EX0 = 1;        //开启外部中断

  10. }


  11. void timer_init()

  12. {

  13.     EA = 0;                //关总中断

  14.     ET0 = 1;            //开定时器0中断

  15.     TMOD = 0x02;        //定时器0工作方式2

  16.     TL0 = 6;            //定时250us

  17.     TH0 = 6;

  18.     EA = 1;             //开总中断

  19.     TR0 = 1;         //开始定时

  20. }

  21. int main()

  22. {

  23. //    unsigned char tempx, tempy;

  24. //    unsigned char i,j;


  25.     interrupt_init();

  26.     timer_init();

  27.     snk_init();


  28.     while(1)

  29.     {    

  30.         //如果位置错了就重新初始化蛇

  31.         if(error)

  32.             snk_init();

  33.         //点亮点阵

  34.         matrix();    

  35.     }

  36. }


  37. void inter0() interrupt 0

  38. {    

  39.     //按键产生外部中断,获取新的方向

  40.     get_direction();

  41.     //设置新的位置

  42.     error = set_location();    

  43. //    matrix();

  44. }

  45. void timer0() interrupt 1

  46. {    

  47.     time++;

  48.     //定时器为250us 积累4000次就是1s

  49.     if(time == 4000)

  50.     {

  51.         //每隔1s都需要重新设置位置,让蛇前进

  52.         error = set_location();    

  53.         time = 0;

  54.     }

  55. }

snake.c

点击(此处)折叠或打开

  1. #include "snake.h"


  2. //蛇的结构体,x为横坐标,y为纵坐标,length为蛇的长度,direction为蛇的前进方向

  3. struct snake{

  4.     unsigned char x[20];

  5.     unsigned char y[20];

  6.     unsigned char length;

  7.     unsigned char direction;

  8. }snk;


  9. void matrix()

  10. {

  11.     unsigned char i;

  12.     int count=500;

  13.     //关闭所有的点

  14.     P2 = 0x00;

  15.     P0 = 0xff;


  16.     //根据蛇每一个点的坐标,将对应的点阵点亮

  17.     for(i=0; i<snk.length; i++)

  18.     {

  19.         P2 = 1<<snk.x[i];

  20.         P0 = ~(1<<snk.y[i]);

  21.     }

  22. }


  23. void snk_init()

  24. {

  25.     //初始化坐标,总共4个点(3,0) (2,0)    (1,0)     (1,0)

  26.     snk.x[0] = 3;

  27.     snk.y[0] = 0;

  28.     snk.x[1] = 2;

  29.     snk.y[1] = 0;

  30.     snk.x[2] = 1;

  31.     snk.y[2] = 0;

  32.     snk.x[3] = 0;

  33.     snk.y[3] = 0;


  34.     //初始长度4

  35.     snk.length = 4;

  36.     //初始移动方向 向右

  37.     snk.direction = RIGHT;

  38.     //点亮点阵

  39.     matrix();

  40. }




  41. void get_direction()

  42. {

    单片机的品种很多,设计时怎样表示操作码和操作数,都有各自的规定,指令代码也各不相同,因此,必须对所选单片机的全部指令,也就是所谓“指令系统”,有足够的了解。各个系列的单片机虽然有不同的指令系统,但也有其共同性。掌握一种单片机的指令系统,对其它系列单片机可以起到触类旁通的作用。MCS-51单片机应用广泛,派生品种多,具有代表性,所以,这里以MCS-51系列的指令系统为例说明“指令”的组成和应用。

  43.     //通过按键的状态获取方向

  44.     if(!up)

  45.         snk.direction = UP;


  46.     if(!down)

  47.         snk.direction = DOWN;

  48.             

  49.     if(!left)

  50.         snk.direction = LEFT;

  51.         

  52.     if(!right)

  53.         snk.direction = RIGHT;

  54. }


  55. int set_location()

  56. {

  57.     unsigned char i;

  58.     int err = 0;

  59.     if(snk.direction == UP)

  60.     {                

  61.         for(i=snk.length-1; i>0; i--)

  62.         {

  63.             snk.x[i] = snk.x[i-1];

  64.             snk.y[i] = snk.y[i-1];

  65.         }


  66.         //如果向上运动,第0个点的横坐标不变,纵坐标加1

  67.         snk.x[0] = snk.x[0];

  68.         snk.y[0] = snk.y[0] + 1;

  69.     }

  70.     else if(snk.direction == DOWN)

  71.     {

  72.         for(i=snk.length-1; i>0; i--)

  73.         {

  74.             snk.x[i] = snk.x[i-1];

  75.             snk.y[i] = snk.y[i-1];

  76.         }


  77.          //如果向下运动,第0个点的横坐标不变,纵坐标减1

  78.         snk.x[0] = snk.x[0];

  79.         snk.y[0] = snk.y[0] - 1;            

  80.     }

  81.     else if(snk.direction == LEFT)

  82.     {

  83.         for(i=snk.length-1; i>0; i--)

  84.         {

  85.             snk.x[i] = snk.x[i-1];

  86.             snk.y[i] = snk.y[i-1];

  87.         }


  88.          //如果向左运动,第0个点的横坐标减1,纵坐标不变

  89.         snk.x[0] = snk.x[0] - 1;

  90.         snk.y[0] = snk.y[0];            

  91.     }

  92.     else

  93.     {

  94.         for(i=snk.length-1; i>0; i--)

  95.         {

  96.             snk.x[i] = snk.x[i-1];

  97.             snk.y[i] = snk.y[i-1];

  98.         }


  99.         //如果向右运动,第0个点的横坐标加1,纵坐标不变

  100.         snk.x[0] = snk.x[0] + 1;

  101.         snk.y[0] = snk.y[0];        

  102.     }


  103.     err = is_location_error();

  104.     return err;

  105. }


  106. int is_location_error()

  107. {

  108.     unsigned char i;


  109.     //如果第0个点的坐标和其他任意一个点重复,那么蛇就自己撞自己,出错

  110.     for(i=1; i<snk.length; i++)

  111.     {

  112.         if((snk.x[0]==snk.x[i]) && (snk.y[0]==snk.y[i]))

  113.             return 1;

  114.     }


  115.     //如果蛇的坐标超出范围,也出错

  116.     if(snk.x[0]>|| snk.y[0]>7)

  117.         return 1;


  118.     return 0;

  119. }

snake.h


  1. #include <reg51.h>


  2. //定义四个方向按键

  3. sbit up = P3^4;

  4. sbit down = P3^5;

  5. sbit left = P3^6;

  6. sbit right = P3^7;

  7. //定义1个游戏级别按键

  8. sbit level = P3^0;

  9. //定义一个复位按键

  10. sbit reset = P3^1;


  11. //定义4个方向的值

  12. #define RIGHT 0

  13. #define    UP 1

  14. #define LEFT 2

  15. #define DOWN 3


  16. void delay_us();

  17. void delay_10us();

  18. void delay_ms();

  19. void delay_10ms();

  20. void delay_100ms();

  21. void delay_s();

  22. int is_location_error();

  23. void matrix();

  24. void snk_init();

  25. void set_direction();

  26. int get_location();

  27. int is_location_error();

> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >

搞完单片机的项目,你就入门了,可以去做一些电路方面的硬件设计。也顺便考察一下自己是否真的热爱电子设计,以便于选择主攻硬件方向还是软件方向。毕竟电子设计是一个非常大的门类,可以做的选择非常多。

生成海报
点赞 0

thePro

我还没有学会写个人说明!

相关推荐

基于8051单片机实现电子时钟+数字秒表设计

概述 电子时钟是一种利用数字电路来显示秒、分、时的计时装置,与传统的机械钟相比,它具有走时准确、显 示直观、无机械传动装置等优点,因而得到广泛应用。随着人们生活环境的不断改善和美化,在许

STM32+HC-05蓝牙模块学习与使用

HC-05蓝牙串口通信 HC05模块是一款高性能主从一体蓝牙串口模块,是一种集成蓝牙功能的PCBA板,用于短距离无线通信,十分方便。 从某宝商家那里可以看到,蓝牙可以使用多种方法使用&