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

简介: 一、元器件 1、AT89C51     关于51单片机就不在啰嗦了,相信大家都已经很熟悉了,关于它的一些常用细节,已经在另一篇博文中提到     http://blog.chinaunix.net/uid-29270124-id-4571661.html2、8x8点阵     点阵里面就是一些二极管啦,通过纵横交叉连接,横8竖8,每个交叉点都接一个二极管。
一、元器件
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     2)、任何一个点的纵坐标  0     3)、第一个点在移动的时候不能和其他点重复,否则就自己追尾了

7、关于原理图按键的设计
    贪吃蛇要求系统能迅速响应按键,因此轮询的方式并不可取,只有靠外部中断。然而51只有2个外部中断,我们起码需要4个方向键,这样就不能一个
    按键配一个外部中断,通过使用4输入与门,将所有按键状态集合在一起,然后送给外部中断0。我们将4个按键都接在与门,只要有一个按下,那么与
    门的输出就会产生一个下降沿,从而产生外部中断。

四、源代码
main.c

点击(此处)折叠或打开

#include "snake.h"

int error = 0;
int time=0;

void interrupt_init()
{
    EA = 0;             //关闭总中断
    IT0 = 1;        //外部中断0方式 下降沿
    EA = 1;         //开启总中断
    EX0 = 1;        //开启外部中断
}

void timer_init()
{
    EA = 0;                //关总中断
    ET0 = 1;            //开定时器0中断
    TMOD = 0x02;        //定时器0工作方式2
    TL0 = 6;            //定时250us
    TH0 = 6;
    EA = 1;             //开总中断
    TR0 = 1;         //开始定时
}
int main()
{
//    unsigned char tempx, tempy;
//    unsigned char i,j;

    interrupt_init();
    timer_init();
    snk_init();

    while(1)
    {    
        //如果位置错了就重新初始化蛇
        if(error)
            snk_init();
        //点亮点阵
        matrix();    
    }
}

void inter0() interrupt 0
{    
    //按键产生外部中断,获取新的方向
    get_direction();
    //设置新的位置
    error = set_location();    
//    matrix();
}
void timer0() interrupt 1
{    
    time++;
    //定时器为250us 积累4000次就是1s
    if(time == 4000)
    {
        //每隔1s都需要重新设置位置,让蛇前进
        error = set_location();    
        time = 0;
    }
}
snake.c

点击(此处)折叠或打开

#include "snake.h"

//蛇的结构体,x为横坐标,y为纵坐标,length为蛇的长度,direction为蛇的前进方向
struct snake{
    unsigned char x[20];
    unsigned char y[20];
    unsigned char length;
    unsigned char direction;
}snk;

void matrix()
{
    unsigned char i;
    int count=500;
    //关闭所有的点
    P2 = 0x00;
    P0 = 0xff;

    //根据蛇每一个点的坐标,将对应的点阵点亮
    for(i=0; isnk.length; i++)
    {
        P2 = 1snk.x[i];
        P0 = ~(1snk.y[i]);
    }
}

void snk_init()
{
    //初始化坐标,总共4个点(3,0) (2,0)    (1,0)     (1,0)
    snk.x[0] = 3;
    snk.y[0] = 0;
    snk.x[1] = 2;
    snk.y[1] = 0;
    snk.x[2] = 1;
    snk.y[2] = 0;
    snk.x[3] = 0;
    snk.y[3] = 0;

    //初始长度4
    snk.length = 4;
    //初始移动方向 向右
    snk.direction = RIGHT;
    //点亮点阵
    matrix();
}



void get_direction()
{
    //通过按键的状态获取方向
    if(!up)
        snk.direction = UP;

    if(!down)
        snk.direction = DOWN;
            
    if(!left)
        snk.direction = LEFT;
        
    if(!right)
        snk.direction = RIGHT;
}

int set_location()
{
    unsigned char i;
    int err = 0;
    if(snk.direction == UP)
    {                
        for(i=snk.length-1; i>0; i--)
        {
            snk.x[i] = snk.x[i-1];
            snk.y[i] = snk.y[i-1];
        }

        //如果向上运动,第0个点的横坐标不变,纵坐标加1
        snk.x[0] = snk.x[0];
        snk.y[0] = snk.y[0] + 1;
    }
    else if(snk.direction == DOWN)
    {
        for(i=snk.length-1; i>0; i--)
        {
            snk.x[i] = snk.x[i-1];
            snk.y[i] = snk.y[i-1];
        }

         //如果向下运动,第0个点的横坐标不变,纵坐标减1
        snk.x[0] = snk.x[0];
        snk.y[0] = snk.y[0] - 1;            
    }
    else if(snk.direction == LEFT)
    {
        for(i=snk.length-1; i>0; i--)
        {
            snk.x[i] = snk.x[i-1];
            snk.y[i] = snk.y[i-1];
        }

         //如果向左运动,第0个点的横坐标减1,纵坐标不变
        snk.x[0] = snk.x[0] - 1;
        snk.y[0] = snk.y[0];            
    }
    else
    {
        for(i=snk.length-1; i>0; i--)
        {
            snk.x[i] = snk.x[i-1];
            snk.y[i] = snk.y[i-1];
        }

        //如果向右运动,第0个点的横坐标加1,纵坐标不变
        snk.x[0] = snk.x[0] + 1;
        snk.y[0] = snk.y[0];        
    }

    err = is_location_error();
    return err;
}

int is_location_error()
{
    unsigned char i;

    //如果第0个点的坐标和其他任意一个点重复,那么蛇就自己撞自己,出错
    for(i=1; isnk.length; i++)
    {
        if((snk.x[0]==snk.x[i]) && (snk.y[0]==snk.y[i]))
            return 1;
    }

    //如果蛇的坐标超出范围,也出错
    if(snk.x[0]>7 || snk.y[0]>7)
        return 1;

    return 0;
}
snake.h

点击(此处)折叠或打开

#include reg51.h>

//定义四个方向按键
sbit up = P3^4;
sbit down = P3^5;
sbit left = P3^6;
sbit right = P3^7;
//定义1个游戏级别按键
sbit level = P3^0;
//定义一个复位按键
sbit reset = P3^1;

//定义4个方向的值
#define RIGHT 0
#define    UP 1
#define LEFT 2
#define DOWN 3

void delay_us();
void delay_10us();
void delay_ms();
void delay_10ms();
void delay_100ms();
void delay_s();
int is_location_error();
void matrix();
void snk_init();
void set_direction();
int get_location();
int is_location_error();




相关文章
|
7月前
|
存储 Rust 测试技术
【一起学Rust · 项目实战】命令行IO项目minigrep——测试驱动开发完善功能
【一起学Rust · 项目实战】命令行IO项目minigrep——测试驱动开发完善功能
136 0
|
2月前
|
测试技术
自动化测试项目实战笔记(三):测试用户注册(验证码错误,成功,出现弹框时处理)
本文是关于自动化测试项目实战笔记,主要介绍了如何测试用户注册功能,包括验证码错误、注册成功以及弹框处理的测试步骤和代码实现。
109 2
自动化测试项目实战笔记(三):测试用户注册(验证码错误,成功,出现弹框时处理)
|
2月前
|
Java 关系型数据库 MySQL
自动化测试项目实战笔记(一):JDK、Tomcat、MySQL、Jpress环境安装和搭建
这篇文章是关于自动化测试项目实战笔记,涵盖了JDK、Tomcat、MySQL、Jpress环境的安装和搭建过程,以及测试用例和常见问题总结。
53 1
自动化测试项目实战笔记(一):JDK、Tomcat、MySQL、Jpress环境安装和搭建
|
2月前
|
人工智能 算法 测试技术
自动化测试项目实战笔记(二):解决验证码识别问题
这篇文章介绍了三种自动化测试中验证码识别的方法:使用Python的pytesseract和PIL模块、利用第三方API如万维易源,以及使用开源的ddddocr库,还提到了一些注意事项,比如如何获取验证码区域的截图。
82 2
|
2月前
|
测试技术 数据安全/隐私保护 Python
自动化测试项目实战笔记(四):测试用户登录(账号密码错误,成功,出现弹框等情况)
本文介绍了使用Selenium进行自动化测试时如何测试用户登录的不同情况,包括账号密码错误、登录成功以及处理登录时出现的弹框,并提供了相应的Python代码实现。
75 0
自动化测试项目实战笔记(四):测试用户登录(账号密码错误,成功,出现弹框等情况)
|
6月前
|
IDE 前端开发 时序数据库
【Docker项目实战】使用Docker部署speedtest-tracker速度测试追踪器
【6月更文挑战第4天】使用Docker部署speedtest-tracker速度测试追踪器
469 0
|
前端开发
前端项目实战拾壹-pda测试平板打包为何白屏
前端项目实战拾壹-pda测试平板打包为何白屏
112 0
【单片机期中测试】9.定时器实现简单的秒表程序
【单片机期中测试】9.定时器实现简单的秒表程序
155 0
|
编解码 前端开发 安全
基于CW32F030单片机的便携式多功能测试笔
一、产品背景 在日常的硬件调试工作中,我们最常使用的仪器仪表可能就是万用表了,虽然万用表号称“万用”,但大部分时候,我们需要使用到的功能无非是电压测量和通断测量。 作为调试的“得力干将”,万用表有时候也会存在一些缺点和局限性,比如:体积较大不便于携带、无法直接反应逻辑电平情况而需要自己判断、不同型号万用表的通断档位阈值电阻不同等等,而最令人头大的,莫过于万用表的COM表笔通常需要接地,而pcb上可能没有直插孔位可以方便的固定笔尖,因此不得不用手辅助“黑表笔“,影响操作灵活性。 二、产品构思 对于这支测试笔,我对他的功能有以下期望: 1、电压测量+阈值判断,阈值电平可以根据需求自行设定
101 0
基于CW32F030单片机的便携式多功能测试笔
|
缓存 程序员 开发工具
《游戏测试》git培训(下)--项目实战 2
《游戏测试》git培训(下)--项目实战