51单片机实现打地鼠游戏

本文节选自我的博客:51 单片机实现打地鼠游戏

  • 💖 作者简介:大家好,我是MilesChen,偏前端的全栈开发者。
  • 📝 CSDN主页:爱吃糖的猫🔥
  • 📣 我的博客:爱吃糖的猫
  • 📚 Github主页: MilesChen
  • 🎉 支持我:点赞👍+收藏⭐️+留言📝
  • 💬介绍:The mixture of WEB+DeepLearning+Iot+anything🍁

前言

在我的51单片机课程设计中,我最初计划制作一个打地鼠小游戏。然而,由于后来全班决定统一做同一个项目,再加上我参加了电赛,项目进展受到了一些耽误。不过,我利用暑假的时间将这个项目补上了,并成功实现了打地鼠小游戏。

效果图

image-20230615162539477

image-20230615162547348

main.c程序

使用到资源有,定时器产生随机数种子、st7565型号的128*64的显示器、矩阵按键。目前实现的比较简单,按哪个按键打对应格子的地鼠,打死才会出现下一个

#include < reg51.h > 
#include < stdlib.h > 
#include "st7565.h"

unsigned char keyvalue = 0; //存放按键值
bit flag = 0; //打地鼠 为1   刷新下一个地鼠的标志
bit keyflag = 0; //启动按键的标志
bit startflag = 0; //游戏开始标志


unsigned char num[3] = {
    5, 5, 5
}; //1-9随机数储存 
unsigned char t = 0, a = 0; //随机数的变量

sbit KEY_IN_1 = P1 ^ 3;
sbit KEY_IN_2 = P1 ^ 2;
sbit KEY_IN_3 = P1 ^ 1;
sbit KEY_IN_4 = P1 ^ 0;
sbit KEY_OUT_1 = P1 ^ 4;
sbit KEY_OUT_2 = P1 ^ 5;
sbit KEY_OUT_3 = P1 ^ 6;
sbit KEY_OUT_4 = P1 ^ 7;

void KeyDriver(); //按键驱动函数

unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到标准键盘键码的映射表

    {
        0x30, 0x37, 0x34, 0x31
    }, //数字键0、数字键7、数字键4、数字键1
    {
        0x1b, 0x38, 0x35, 0x32
    }, //ESC键、数字键8、数字键5、数字键2
    {
        0x0d, 0x39, 0x36, 0x33
    }, //回车键、数字键9、数字键6、数字键3
    {
        0x27, 0x28, 0x25, 0x26
    } //向右键,向下键,向左键,向上键

};

unsigned char KeySta[4][4] = { //全部矩阵按键的当前状态
    {
        1, 1, 1, 1
    }, {
        1, 1, 1, 1
    }, {
        1, 1, 1, 1
    }, {
        1, 1, 1, 1
    }
};

//10ms  延时
void Delay10ms(unsigned int c) //延时工具自动生成
    {
        unsigned char a, b;
        for (; c > 0; c--)
            for (b = 38; b > 0; b--)
                for (a = 130; a > 0; a--);
    }


//    刷新地鼠
void refresh12864() {
    //关闭敲死的地鼠
    Lcd12864_Write16CnCHAR(96, 2, "壤土"); //1
    Lcd12864_Write16CnCHAR(64, 2, "壤土"); //2
    Lcd12864_Write16CnCHAR(32, 2, "壤土"); //3
    Lcd12864_Write16CnCHAR(96, 4, "壤土"); //4
    Lcd12864_Write16CnCHAR(64, 4, "壤土"); //5
    Lcd12864_Write16CnCHAR(32, 4, "壤土"); //6
    Lcd12864_Write16CnCHAR(96, 6, "壤土"); //7
    Lcd12864_Write16CnCHAR(64, 6, "壤土"); //8
    Lcd12864_Write16CnCHAR(32, 6, "壤土"); //9

    Delay10ms(30);
    if (num[1] == 1)
        Lcd12864_Write16CnCHAR(96, 2, "鼠地"); //1
    else if (num[1] == 2)
        Lcd12864_Write16CnCHAR(64, 2, "鼠地"); //2
    else if (num[1] == 3)
        Lcd12864_Write16CnCHAR(32, 2, "鼠地"); //3
    else if (num[1] == 4)
        Lcd12864_Write16CnCHAR(96, 4, "鼠地"); //4
    else if (num[1] == 5)
        Lcd12864_Write16CnCHAR(64, 4, "鼠地"); //5
    else if (num[1] == 6)
        Lcd12864_Write16CnCHAR(32, 4, "鼠地"); //6
    else if (num[1] == 7)
        Lcd12864_Write16CnCHAR(96, 6, "鼠地"); //7
    else if (num[1] == 8)
        Lcd12864_Write16CnCHAR(64, 6, "鼠地"); //8
    else
        Lcd12864_Write16CnCHAR(32, 6, "鼠地"); //9
}


void Timer0Init() //定时器
    {
        TMOD |= 0X01; //选择为定时器0模式,工作方式1,仅用TR0打开启动。
        TH0 = 0XFC; //给定时器赋初值,定时1ms
        TL0 = 0X18;
        ET0 = 1; //打开定时器0中断允许
        EA = 1; //打开总中断
        TR0 = 1; //打开定时器			
    }

// 生成随机数
void freenum() {
    while (1) {
        srand(t); //函数来设置随机数的种子
        a = (char)(rand() % 10); //生成一个0到9之间的随机数
        if (a == num[1] | a == 0) //不能与上次的相同,且不能为0,因为只有9个洞
        {
            continue;
        } else {
            num[2] = num[1]; //存入上一次的值
            num[1] = num[0]; //存入上一次的值
            num[0] = a;
            break;
        }
    }
}




void main() {
    Timer0Init(); //定时器0初始化
    Lcd12864_Init();
    freenum();

    Lcd12864_ClearScreen();
    Lcd12864_Write16CnCHAR(40, 3, "鼠地打");
    while (1) {
        if (flag == 1) {
            Lcd12864_ClearScreen();
            flag = 0;
            refresh12864(); //刷新num[1]里面的地鼠   
        }

        KeyDriver(); //调用按键驱动函数

        if (keyflag) {
            keyflag = 0;
            if (num[1] == keyvalue) //num[1]随机数;ketvalue即按下值
            {
                freenum();
                flag = 1;
            } else {
                Lcd12864_ClearScreen();
                Lcd12864_Write16CnCHAR(32, 3, "束结戏游");

            }
        }

    }
}


// 按键动作函数,根据键码执行相应的操作,keycode-按键键码 
void KeyAction(unsigned char keycode) {

    if ((keycode >= 0x31) && (keycode <= 0x39)) //输入0-9的数字
    {
        if (startflag) {
            keyvalue = (keycode - 0x30); //把ascll转数字
            keyflag = 1;
        }
    } else if (keycode == 0x1B) //返回主菜单   esc键
    {
        startflag = 0;
        flag = 0;
        Lcd12864_ClearScreen();
        Lcd12864_Write16CnCHAR(40, 3, "鼠地打");
    } else if (keycode == 0x0D) //回车键,开始游戏
    {
        flag = 1;
        startflag = 1;
    }


}

//按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用 
void KeyDriver() {
    unsigned char i, j;
    static unsigned char backup[4][4] = { //按键值备份,保存前一次的值
        {
            1, 1, 1, 1
        }, {
            1, 1, 1, 1
        }, {
            1, 1, 1, 1
        }, {
            1, 1, 1, 1
        }
    };
    for (i = 0; i < 4; i++) //循环检测4*4的矩阵按键
    {
        for (j = 0; j < 4; j++) {
            if (backup[i][j] != KeySta[i][j]) //检测按键动作
            {
                if (backup[i][j] != 0) //按键按下时执行动作
                {
                    KeyAction(KeyCodeMap[i][j]); //调用按键动作函数
                }
                backup[i][j] = KeySta[i][j]; //刷新前一次的备份值
            }
        }
    }
}

//按键扫描函数,需在定时中断中调用,推荐调用间隔1ms 
void KeyScan() {
    unsigned char i;
    static unsigned char keyout = 0; //矩阵按键扫描输出索引
    static unsigned char keybuf[4][4] = { //矩阵按键扫描缓冲区
        {
            0xFF, 0xFF, 0xFF, 0xFF
        }, {
            0xFF, 0xFF, 0xFF, 0xFF
        }, {
            0xFF, 0xFF, 0xFF, 0xFF
        }, {
            0xFF, 0xFF, 0xFF, 0xFF
        }
    };

    //将一行的4个按键值移入缓冲区
    keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1;
    keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2;
    keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3;
    keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4;
    //消抖后更新按键状态
    for (i = 0; i < 4; i++) //每行4个按键,所以循环4次
    {
        if ((keybuf[keyout][i] & 0x0F) == 0x00) { //连续4次扫描值为0,即4*4ms内都是按下状态时,可认为按键已稳定的按下
            KeySta[keyout][i] = 0;
        } else if ((keybuf[keyout][i] & 0x0F) == 0x0F) { //连续4次扫描值为1,即4*4ms内都是弹起状态时,可认为按键已稳定的弹起
            KeySta[keyout][i] = 1;
        }
    }
    //执行下一次的扫描输出
    keyout++; //输出索引递增
    keyout = keyout & 0x03; //索引值加到4即归零
    switch (keyout) //根据索引,释放当前输出引脚,拉低下次的输出引脚
    {
        case 0:
            KEY_OUT_4 = 1;
            KEY_OUT_1 = 0;
            break;
        case 1:
            KEY_OUT_1 = 1;
            KEY_OUT_2 = 0;
            break;
        case 2:
            KEY_OUT_2 = 1;
            KEY_OUT_3 = 0;
            break;
        case 3:
            KEY_OUT_3 = 1;
            KEY_OUT_4 = 0;
            break;
        default:
            break;
    }
}

void Timer0() interrupt 1 {
    TH0 = 0xFC; //重新加载初值
    TL0 = 0x67;
    t++; //随机数种子生成
    if (t == 100)
        t = 0;
    KeyScan(); //调用按键扫描函数
}

总结

随机数获取随机数是当时遇到的难点,最终采用定时器获取随机数种子,从而获得伪随机数。显示屏是st7565,需要使用文字取模软件将图片或文字转换成对应的显示码。


有任何疑问和想法,欢迎在评论区与我交流。

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值