重力感应贪吃蛇(C51 MPU6050 8*8LED点阵)

重力感应贪吃蛇(C51 MPU6050 8*8LED点阵)

本文讲述如何从无到有用C51做一个重力感应的贪吃蛇,包括元件选型、原理图PCB绘制和代码编写。

  • 重力感应贪吃蛇(C51 MPU6050 8*8LED点阵)
    • 所需元件及其选型
    • 原理图绘制
      • C51外围电路
      • 复位晶振电路
      • USB下载电路/电源电路
      • MPU6050插座电路
    • 代码部分
      • 驱动程序
      • 贪吃蛇算法部分

所需元件及其选型

元件 封装 数量
89C52 双列直插DIP40 1
MPU6050 8引脚(实际只用到4个) 1
8*8LED点阵屏 18引脚(2个起固定作用) 1
74HC595 贴片SOP16 1
CH340 贴片SOP16 1
12M晶振 HC-49S 2
USB母座 6脚(USB-A) 1
排阻10k 0603 8P4R 4
电阻10k 贴片0603 2
电阻33R 贴片0603 2
电阻47k 贴片0603 1
电阻4.7k 贴片0603 1
电容22P 贴片0603 3
电容33P 贴片0603 2
电容103 贴片0603 1
电容104 贴片0603 2
电解电容10u 插件 1
电解电容470u 插件 1
SS14肖特基二极管 SMA 1
A1SHB SOT-23 1
贴片LED 0603 1
轻触开关 6*6*5 1
按钮开关 8*8 1

原理图绘制

C51外围电路

重力感应贪吃蛇(C51 MPU6050 8*8LED点阵)_第1张图片
//上拉电阻画得比较丑,大家意会一下。。。

复位晶振电路

重力感应贪吃蛇(C51 MPU6050 8*8LED点阵)_第2张图片

USB下载电路/电源电路

重力感应贪吃蛇(C51 MPU6050 8*8LED点阵)_第3张图片
实不相瞒,最难的这部分电路是从网上抄的- -

MPU6050插座电路

重力感应贪吃蛇(C51 MPU6050 8*8LED点阵)_第4张图片

代码部分

驱动程序

  • IIC&MPU6050

这个比较简单的,网上有很多资料,不赘述。
拿P1的0和1做IIC通信的时钟线和数据线,配置一下MPU6050的寄存器:

sbit SCL=P1^0;
sbit SDA=P1^1;

#define SMPLRT_DIV      0x19
#define CONFIG          0x1A
#define GYRO_CONFIG     0x1B
#define ACCEL_CONFIG    0x1C
#define ACCEL_XOUT_H    0x3B
#define ACCEL_XOUT_L    0x3C
#define ACCEL_YOUT_H    0x3D
#define ACCEL_YOUT_L    0x3E
#define ACCEL_ZOUT_H    0x3F
#define ACCEL_ZOUT_L    0x40
#define TEMP_OUT_H      0x41
#define TEMP_OUT_L      0x42
#define GYRO_XOUT_H     0x43
#define GYRO_XOUT_L     0x44
#define GYRO_YOUT_H     0x45
#define GYRO_YOUT_L     0x46
#define GYRO_ZOUT_H     0x47
#define GYRO_ZOUT_L     0x48
#define PWR_MGMT_1      0x6B
#define WHO_AM_I        0x75
#define SlaveAddress    0xD0

延时5秒IIC要用,写得精确点:

void Delay5us()
{
    _nop_();_nop_();_nop_();_nop_();
    _nop_();_nop_();_nop_();_nop_();
    _nop_();_nop_();_nop_();_nop_();
    _nop_();_nop_();_nop_();_nop_();
    _nop_();_nop_();_nop_();_nop_();
    _nop_();_nop_();_nop_();_nop_();
}

IIC通信的相关函数:

void I2C_Start()
{
    SDA = 1;
    SCL = 1;
    Delay5us();
    SDA = 0;
    Delay5us();
    SCL = 0;
}

void I2C_Stop()
{
    SDA = 0;
    SCL = 1;
    Delay5us();
    SDA = 1;
    Delay5us();
}

void I2C_SendACK(bit ack)
{
    SDA = ack;
    SCL = 1;  
    Delay5us();
    SCL = 0;   
    Delay5us();
}

bit I2C_RecvACK()
{
    SCL = 1;                    
    Delay5us();                 
    CY = SDA;                   
    SCL = 0;                    
    Delay5us();                 
    return CY;
}

void I2C_SendByte(uchar dat)
{
    uchar i;
    for (i=0; i<8; i++)         
    {
        dat <<= 1;              
        SDA = CY;               
        SCL = 1;                
        Delay5us();             
        SCL = 0;                
        Delay5us();             
    }
    I2C_RecvACK();
}

uchar I2C_RecvByte()
{
    uchar i;
    uchar dat = 0;
    SDA = 1;                    
    for (i=0; i<8; i++)         
    {
        dat <<= 1;
        SCL = 1;                
        Delay5us();             
        dat |= SDA;                            
        SCL = 0;                
        Delay5us();             
    }
    return dat;
}

void Single_WriteI2C(uchar REG_Address,uchar REG_data)
{
    I2C_Start();                  
    I2C_SendByte(SlaveAddress);   
    I2C_SendByte(REG_Address);    
    I2C_SendByte(REG_data);       
    I2C_Stop();                   
}

uchar Single_ReadI2C(uchar REG_Address)
{
    uchar REG_data;
    I2C_Start();                   
    I2C_SendByte(SlaveAddress);    
    I2C_SendByte(REG_Address);      
    I2C_Start();                   
    I2C_SendByte(SlaveAddress+1);  
    REG_data=I2C_RecvByte();       
    I2C_SendACK(1);                
    I2C_Stop();                    
    return REG_data;
}

MPU6050相关函数:

void InitMPU6050()
{
    Single_WriteI2C(PWR_MGMT_1, 0x00);
    Single_WriteI2C(SMPLRT_DIV, 0x07);
    Single_WriteI2C(CONFIG, 0x06);
    Single_WriteI2C(GYRO_CONFIG, 0x18);
    Single_WriteI2C(ACCEL_CONFIG, 0x01);
}

int GetData(uchar REG_Address)
{
    uchar H,L;
    H=Single_ReadI2C(REG_Address);
    L=Single_ReadI2C(REG_Address+1);
    return (H<<8)+L;
}
  • 74HC595&LED点阵

定义接口:

sbit SRCLK=P3^6;
sbit RCLK=P3^5;
sbit SER=P3^4;

595串转并函数:

void HC595SendByte(unsigned char dat)
{
    unsigned char a;
    SRCLK=0;
    RCLK=0;
    for(a=0;a<8;a++)
    {
        SER=dat>>7;
        dat<<=1;

        SRCLK=1;
        _nop_();
        _nop_();
        SRCLK=0;    
    }

    RCLK=1;
    _nop_();
    _nop_();
    RCLK=0;
}

贪吃蛇算法部分

  • 数据结构
    由于51内存很有限,LED点阵还需要时刻进行扫描,MPU6050还要进行通信,要尽量节约时间和空间。本文并没有使用二维数组,而是使用一个一维数组表示蛇的信息,每个元素从头至尾,前半个字节表示横坐标,后半个字节表示纵坐标。实物也是这样存储,只需要一个uchar。

宏定义如下:

#define COMMONPORTS P0
#define UP    0x00
#define DOWN  0x01
#define LEFT  0x02
#define RIGHT 0x03

全局变量如下:

unsigned char code TAB[8] = {0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};//scan 8*8 LED column

unsigned char snake[40] = {0x34}; //initial position
unsigned char food = 0x33;
unsigned char refresh = 0; //determine whether to refresh
unsigned char len = 1; //the length of snake
unsigned char dir = DOWN; // direction
unsigned char map[8] = {0,0,0,0,0,0,0,0}; //scan 8*8 LED row
unsigned char isalive = 1; // determine game over or not
static int ts; // timer

其中TAB是8*8LED点阵屏的列扫描,map是一个长度为8的数组,记录每一列上8个LED的亮灭。

  • 整体思路:

先放个主函数压压惊:

void main()
{
    unsigned char tab;
    InitMPU6050();
    Timer0Init();
    while(1){
        while(!refresh){
            for(tab=0;tab<8;tab++){
                HC595SendByte(0x00);
                COMMONPORTS = TAB[tab];
                HC595SendByte(map[tab]);
            }
        }
        refresh = 0;
        get_dir();
        snake_move();
        if(!isalive){
            ET0 = 0;
            continue;
        }
        print_game();
    }
}

思路很简单,根本不需要解释。

  • 工具函数tran()
void tran(unsigned char num,unsigned char *x, unsigned char *y)
{
    *x = num >> 4;
    *y = (unsigned char)(num << 4) >> 4;
}

这个函数就是用来把一个字节拆成两半,分别代表横坐标和纵坐标。

  • 定时器:1s刷新一次屏幕
void Timer0Init()
{
    TMOD|=0X01;
    TH0=0XFC;
    TL0=0X18;   
    ET0=1;
    EA=1;
    TR0=1;          
}

void Timer0() interrupt 1
{
    TH0=0XFC;
    TL0=0X18;
    ts++;
    if(ts==1000)
    {
        ts=0;
        refresh = 1;    
    }   
}
  • 更新map
void print_game()
{
    unsigned char i;
    unsigned char x,y;
    memset(map,0,sizeof(unsigned char)*8);
    for(i=0;imap[x] += (1<map[x] += (1<
  • 产生食物
unsigned char generate_food()
{
    unsigned char food_,fx,fy;
    unsigned char in_snake = 0,i;
    int rd;
    do {
        rd = (unsigned char)(GetData(ACCEL_ZOUT_H));
        fx = rd & 0x07;
        fy = (rd & 0x38) >> 3;
        in_snake = 0;
        food_ = ((fx^0) << 4) ^ fy;
        for (i = 0; i < len; ++i){
            if (food_ == snake[i])
                in_snake = 1;
        }
    } while(in_snake);
    return food_;
}

这里本来打算用rand生成随机数的,后来一想,MPU在手里,为何不直接取位数取余做随机数呢,岂不是更好。

  • 蛇移动
void snake_move()
{
    unsigned char grow = 0,i,tmp,x_snake_head,y_snake_head;
    unsigned char last = snake[0];
    tran(snake[0],&x_snake_head,&y_snake_head);
    switch (dir){
        case UP:    y_snake_head++; break;
        case DOWN:  y_snake_head--; break;
        case LEFT:  x_snake_head--; break;
        case RIGHT: x_snake_head++; break;
    }
    snake[0] = ((x_snake_head^0) << 4) ^ y_snake_head;
    if(x_snake_head>7 || y_snake_head>7)
        isalive = 0;
    if(snake[0] == food){
        grow = 1;
        food = generate_food();
    }
    for(i=1;iif(snake[0] == snake[i])
            isalive = 0;
        tmp = snake[i];
        snake[i] = last;
        last = tmp;
    }
    if(grow){
        snake[len] = last;
        len++;
    }
}
  • 用MPU获得方向
void get_dir()
{
    int ax = GetData(ACCEL_XOUT_H);
    int ay = GetData(ACCEL_YOUT_H);
    if(ax < -8000)
        dir = LEFT;
    else if(ax > 8000)
        dir = RIGHT;
    else if(ay < -8000)
        dir = UP;
    else if(ay > 8000)
        dir = DOWN;
}

OK大功告成。
可以下载源码试一试,亲测有效
C51贪吃蛇(MPU6050控制方向,LED点阵显示)

你可能感兴趣的:(C51)