基于51单片机和LCD1602、矩阵按键的小游戏《猜数字》

目录

系列文章目录


前言

用的是普中A2开发板,用到板上的矩阵按键,还需要外接一个LCD1602液晶显示屏。

【单片机】STC89C52RC

【频率】[email protected]

效果查看/操作演示:B站搜索"甘腾胜"或"gantengsheng"查看。

源代码下载:B站对应视频的简介有工程文件下载链接。

一、效果展示




二、原理分析

1、数字的输入与清除

输入数字时,通过十进制的移位和加法,将刚输入的数字实时保存到int变量中。

2、随机数的产生

设置的最大范围是:1~9999,我发现当范围较大时,虽然每次获取键码时用了定时器0的低八位做种子,但发现产生的随机数比较集中,集中在其中的一个小范围内。然后我将随机数的产生的算式弄复杂一些,就比较随机了,但还是有一定的伪随机性。道友有更好的办法请跟我说一下,谢谢了!

3、随机数的范围

由于屏幕大小有限,所以限定随机数的范围是1~9999,最多四位数。设定范围的时候要注意,最大值要比最小值大2以上,如果不合理,则需要重置后重新输入。

4、按键的检测

矩阵按键通过行列扫描检测,利用定时器0,每隔20ms检测一次,防止阻塞程序运行。

三、各模块代码

1、LCD1602

h文件

c 复制代码
#ifndef __LCD1602_H__
#define __LCD1602_H__

void LCD_WriteCommand(unsigned char Command);
void LCD_WriteData(unsigned char Data);
void LCD_SetCursor(unsigned char Line,unsigned char Column);
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_Clear(void);
void LCD_IsShowCursor(unsigned char State);
void LCD_Show16Char(unsigned char Line,unsigned char Column,char *String);

#endif

c文件

c 复制代码
#include <REGX52.H>

/*引脚配置*/
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0

/*函数定义*/
/**
  * 函    数:LCD1602私有延时函数,延时约50us
  * 参    数:无
  * 返 回 值:无
  */
void LCD_Delay50us(void)	//[email protected]
{
	unsigned char i;
	i=20;
	while(--i);
}

/**
  * 函    数:LCD1602私有延时函数,延时约2ms
  * 参    数:无
  * 返 回 值:无
  */
void LCD_Delay2ms(void)	//[email protected]
{
	unsigned char i,j;
	i=4;
	j=146;
	do
	{
		while(--j);
	}while(--i);
}

/**
  * 函    数:LCD1602写指令
  * 参    数:Command 要写入的指令
  * 返 回 值:无
  */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_RS=0;
	LCD_RW=0;
	LCD_DataPort=Command;
	LCD_EN=1;
	LCD_Delay50us();
	LCD_EN=0;
	LCD_Delay50us();
}

/**
  * 函    数:LCD1602写数据
  * 参    数:Data 要写入的数据
  * 返 回 值:无
  */
void LCD_WriteData(unsigned char Data)
{
	LCD_RS=1;
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	LCD_Delay50us();
	LCD_EN=0;
	LCD_Delay50us();
}

/**
  * 函    数:LCD1602设置光标位置
  * 参    数:Line 行位置,范围:1~2
  * 参    数:Column 列位置,范围:1~16
  * 返 回 值:无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
	if(Line==1)
	{
		LCD_WriteCommand(0x80|(Column-1));
	}
	else if(Line==2)
	{
		LCD_WriteCommand(0x80|(Column-1+0x40));
	}
}

/**
  * 函    数:LCD1602初始化函数
  * 参    数:无
  * 返 回 值:无
  */
void LCD_Init()
{
	LCD_WriteCommand(0x38);	//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0C);	//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);	//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);	//光标复位,清屏
	LCD_Delay2ms();	//清屏指令执行需要较长时间,需要较长的延时
}

/**
  * 函    数:在LCD1602指定位置上显示一个字符
  * 参    数:Line 行位置,范围:1~2
  * 参    数:Column 列位置,范围:1~16
  * 参    数:Char 要显示的字符
  * 返 回 值:无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}

/**
  * 函    数:在LCD1602指定位置开始显示所给字符串
  * 参    数:Line 起始行位置,范围:1~2
  * 参    数:Column 起始列位置,范围:1~16
  * 参    数:String 要显示的字符串
  * 返 回 值:无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}

/**
  * 函    数:返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}

/**
  * 函    数:在LCD1602指定位置开始显示所给数字
  * 参    数:Line 起始行位置,范围:1~2
  * 参    数:Column 起始列位置,范围:1~16
  * 参    数:Number 要显示的数字,范围:0~65535
  * 参    数:Length 要显示数字的长度,范围:1~5
  * 返 回 值:无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * 函    数:在LCD1602指定位置开始以有符号十进制显示所给数字
  * 参    数:Line 起始行位置,范围:1~2
  * 参    数:Column 起始列位置,范围:1~16
  * 参    数:Number 要显示的数字,范围:-32768~32767
  * 参    数:Length 要显示数字的长度,范围:1~5
  * 返 回 值:无
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
	unsigned char i;
	unsigned int Number1;
	LCD_SetCursor(Line,Column);
	if(Number>=0)
	{
		LCD_WriteData('+');
		Number1=Number;
	}
	else
	{
		LCD_WriteData('-');
		Number1=-Number;
	}
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * 函    数:在LCD1602指定位置开始以十六进制显示所给数字
  * 参    数:Line 起始行位置,范围:1~2
  * 参    数:Column 起始列位置,范围:1~16
  * 参    数:Number 要显示的数字,范围:0~0xFFFF
  * 参    数:Length 要显示数字的长度,范围:1~4
  * 返 回 值:无
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i,SingleNumber;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		SingleNumber=Number/LCD_Pow(16,i-1)%16;
		if(SingleNumber<10)
		{
			LCD_WriteData(SingleNumber+'0');
		}
		else
		{
			LCD_WriteData(SingleNumber-10+'A');
		}
	}
}

/**
  * 函    数:在LCD1602指定位置开始以二进制显示所给数字
  * 参    数:Line 起始行位置,范围:1~2
  * 参    数:Column 起始列位置,范围:1~16
  * 参    数:Number 要显示的数字,范围:0~1111 1111 1111 1111
  * 参    数:Length 要显示数字的长度,范围:1~16
  * 返 回 值:无
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
	}
}

/**
  * 函    数:LCD1602的光标复位,清屏
  * 参    数:无
  * 返 回 值:无
  */
void LCD_Clear(void)
{
	LCD_WriteCommand(0x01);
	LCD_Delay2ms();
}

/**
  * 函    数:LCD1602设置光标是否显示
  * 参    数:State 光标状态,1:显示,0:不显示
  * 返 回 值:无
  */
void LCD_IsShowCursor(unsigned char State)
{
	if(State){LCD_WriteCommand(0x0E);}
	else{LCD_WriteCommand(0x0C);}
}

2、矩阵按键

h文件

c 复制代码
#ifndef __MATRIXKEYSCAN_H__
#define __MATRIXKEYSCAN_H__

unsigned char MatrixKey(void);
void MatrixKey_Tick(void);

#endif

c文件

c 复制代码
#include <REGX52.H>

#define Matrix_Port P1	//矩阵按键接口

unsigned char KeyNumber;

/**
  * 函    数:获取矩阵按键键码
  * 参    数:无
  * 返 回 值:按下按键的键码,范围:0~48,0表示无按键按下
  * 说    明:在下一次检测按键之前,第二次获取键码一定会返回0
  */
unsigned char MatrixKey(void)
{
	unsigned char KeyTemp=0;
	KeyTemp=KeyNumber;
	KeyNumber=0;
	return KeyTemp;
}

/**
  * 函    数:检测当前按下的按键,无消抖及松手检测
  * 参    数:无
  * 返 回 值:按下按键的键值,范围:0~16,无按键按下时返回值为0
  */
unsigned char Key_GetState()
{
	unsigned char KeyValue=0;
	unsigned char i=0;
	
	Matrix_Port=0x0F;	//给所有行赋值0,列全为1
	i=2;while(i--);	//适当延时,延时5us
	if(Matrix_Port!=0x0F)
	{
		Matrix_Port=0x0F;	//测试列
		i=2;while(i--);
		switch(Matrix_Port)	//所有行拉低,检测哪一列按下
		{
			case 0x07:KeyValue=1;break;
			case 0x0B:KeyValue=2;break;
			case 0x0D:KeyValue=3;break;
			case 0x0E:KeyValue=4;break;
			default:break;
		}
		Matrix_Port=0xF0;	//测试行
		i=2;while(i--);
		switch(Matrix_Port)	//所有列拉低,检测哪一行按下
		{
			case 0x70:KeyValue=KeyValue;break;
			case 0xB0:KeyValue=KeyValue+4;break;
			case 0xD0:KeyValue=KeyValue+8;break;
			case 0xE0:KeyValue=KeyValue+12;break;
			default:break;
		}
	}
	else
	{
		KeyValue=0;
	}

	return KeyValue;
}

/**
  * 函    数:矩阵按键驱动函数,在中断中调用
  * 参    数:无
  * 返 回 值:无
  */
void MatrixKey_Tick(void)
{
	static unsigned char NowState,LastState;
	static unsigned int KeyCount;

	LastState=NowState;	//更新上一次的键值
	NowState=Key_GetState();	//获取当前的键值

	//如果上个时间点按键未按下,这个时间点按键按下,则是按下瞬间
	if(LastState==0)
	{
		switch(NowState)
		{
			case 1:KeyNumber=1;break;
			case 2:KeyNumber=2;break;
			case 3:KeyNumber=3;break;
			case 4:KeyNumber=4;break;
			case 5:KeyNumber=5;break;
			case 6:KeyNumber=6;break;
			case 7:KeyNumber=7;break;
			case 8:KeyNumber=8;break;
			case 9:KeyNumber=9;break;
			case 10:KeyNumber=10;break;
			case 11:KeyNumber=11;break;
			case 12:KeyNumber=12;break;
			case 13:KeyNumber=13;break;
			case 14:KeyNumber=14;break;
			case 15:KeyNumber=15;break;
			case 16:KeyNumber=16;break;
			default:break;
		}
	}
	
	//如果上个时间点按键按下,这个时间点按键还是按下,则是长按
	if(LastState && NowState)
	{
		KeyCount++;

		if(KeyCount%50==0)	//定时器中断函数中每隔20ms检测一次按键
		{	//长按后每隔1s返回一次长按的键码
			if     (LastState== 1 && NowState== 1){KeyNumber=17;}
			else if(LastState== 2 && NowState== 2){KeyNumber=18;}
			else if(LastState== 3 && NowState== 3){KeyNumber=19;}
			else if(LastState== 4 && NowState== 4){KeyNumber=20;}
			else if(LastState== 5 && NowState== 5){KeyNumber=21;}
			else if(LastState== 6 && NowState== 6){KeyNumber=22;}
			else if(LastState== 7 && NowState== 7){KeyNumber=23;}
			else if(LastState== 8 && NowState== 8){KeyNumber=24;}
			else if(LastState== 9 && NowState== 9){KeyNumber=25;}
			else if(LastState==10 && NowState==10){KeyNumber=26;}
			else if(LastState==11 && NowState==11){KeyNumber=27;}
			else if(LastState==12 && NowState==12){KeyNumber=28;}
			else if(LastState==13 && NowState==13){KeyNumber=29;}
			else if(LastState==14 && NowState==14){KeyNumber=30;}
			else if(LastState==15 && NowState==15){KeyNumber=31;}
			else if(LastState==16 && NowState==16){KeyNumber=32;}
		}
	}
	else
	{
		KeyCount=0;
	}

	//如果上个时间点按键按下,这个时间点按键未按下,则是松手瞬间
	if(NowState==0)
	{
		switch(LastState)
		{
			case 1:KeyNumber=33;break;
			case 2:KeyNumber=34;break;
			case 3:KeyNumber=35;break;
			case 4:KeyNumber=36;break;
			case 5:KeyNumber=37;break;
			case 6:KeyNumber=38;break;
			case 7:KeyNumber=39;break;
			case 8:KeyNumber=40;break;
			case 9:KeyNumber=41;break;
			case 10:KeyNumber=42;break;
			case 11:KeyNumber=43;break;
			case 12:KeyNumber=44;break;
			case 13:KeyNumber=45;break;
			case 14:KeyNumber=46;break;
			case 15:KeyNumber=47;break;
			case 16:KeyNumber=48;break;
			default:break;
		}
	}

}

3、定时器0

h文件

c 复制代码
#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0_Init(void);

#endif

c文件

c 复制代码
#include <REGX52.H>

/**
  * 函    数:定时器0初始化
  * 参    数:无
  * 返 回 值:无
  */
void Timer0_Init(void)
{	
//	AUXR&=0x7F;	//定时器时钟12T模式(STC89C52RC是12T单片机,无需设置)
	TMOD&=0xF0;	//设置定时器模式(高四位不变,低四位清零)
	TMOD|=0x01;	//设置定时器模式(通过低四位设为16位不自动重装)
	TL0=0x00;	//设置定时初值,定时10ms,[email protected]
	TH0=0xDC;	//设置定时初值,定时10ms,[email protected]
	TF0=0;	//清除TF0标志
	TR0=1;	//定时器0开始计时
	ET0=1;	//打开定时器0中断允许
	EA=1;	//打开总中断
	PT0=0;	//当PT0=0时,定时器0为低优先级,当PT0=1时,定时器0为高优先级
}

/*定时器中断函数模板
void Timer0_Routine() interrupt 1	//定时器0中断函数
{
	static unsigned int T0Count;	//定义静态变量
	TL0=0x00;	//设置定时初值,定时10ms,[email protected]
	TH0=0xDC;	//设置定时初值,定时10ms,[email protected]
	T0Count++;
	if(T0Count>=1000)
	{
		T0Count=0;
		
	}
}
*/

四、主函数

main.c

c 复制代码
/*by甘腾胜@20250423
【效果查看/操作演示】B站搜索"甘腾胜"或"gantengsheng"查看
【单片机】STC89C52RC
【频率】[email protected]
【外设】LCD1602、矩阵按键
【简单的原理分析】https://blog.csdn.net/gantengsheng/article/details/143581157
【注意】点阵屏旁边的跳线帽接三根排针中的右边两根
【操作】

	S1		S2		S3		S4
	S5		S6		S7		S8
	S9		S10		S11		S12
	S13		S14		S15		S16

S1~S9:数字1~9
S10:数字0
S11:清空/返回
S12:确认键

(1)输入错误可按S11(松手瞬间)清空后重新输入
(2)按S12(松手瞬间)表示确认完成输入
(3)猜对数字后按S11(松手瞬间)返回设置范围的界面
*/

#include <REGX52.H>	//51单片机头文件
#include "LCD1602.h"	//LCD1602液晶显示屏
#include "MatrixKeyScan.h"	//矩阵按键
#include "Timer0.h"	//定时器0
#include <STDLIB.H>	//随机函数

unsigned char KeyNum;	//存储获得的键码
unsigned char Mode;	//模式,0:显示游戏英文名,1:设置数字范围界面,2:猜数字过程,3:猜对后的屏幕显示
unsigned char OnceFlag=1;	//某些前提下只执行一次的标志(各模式中用来判断是否要初始化),1:执行,0:不执行
unsigned char InputLength;	//输入位数,范围:1~4
unsigned char LastInputLength;	//上一次的输入位数,范围:1~4,用来判断是否有按下数字键0~9
bit ConfirmFlag;	//确认按键按下的标志,1:确认按键按下,0:确认按键未按下
unsigned char NowNumber;	//刚刚输入的数字按键对应的数字,范围:0~9
bit SetMinFlag;	//正在设置数字范围最小值的标志,1:正在设置,0:没有在设置
bit SetMaxFlag;	//正在设置数字范围最大值的标志,1:正在设置,0:没有在设置
unsigned int MinNumber;	//所设置的数字范围的最小值,范围:1~9997
unsigned int MaxNumber;	//所设置的数字范围的最大值,范围:3~9999
unsigned char MinLength;	//最小值的位数,范围:1~4
unsigned char MaxLength;	//最大值的位数,范围:1~4
bit CheckFlag;	//设置完范围后检查范围是否合理的标志,1:检查,0:不检查
bit ReenterFlag;	//重新输入的标志,1:重新输入,0:不重新输入
unsigned int PlayerNumber;	//玩家输入的数字,范围:2~9998
unsigned char PlayerLength;	//玩家输入数字的位数,范围:1~4
unsigned int RandomNumber;	//产生的随机数,范围:MinNumber<RandomNumber<MaxNumber
unsigned char RandomLength;	//随机数的位数,范围:1~4
unsigned int GuessTimes;	//玩家猜测的次数
unsigned char GuessLength;	//玩家猜测的次数的位数,范围:1~4

/**
  * 函    数:延时函数,延时约xms毫秒
  * 参    数:xms 延时的时间,范围:0~65535
  * 返 回 值:无
  */
void Delay(unsigned int xms)	//[email protected]
{
	unsigned char i,j;
	while(xms--)
	{
		i=2;
		j=199;
		do
		{
			while(--j);
		}while(--i);
	}
}

/**
  * 函    数:主函数(有且仅有一个)
  * 参    数:无
  * 返 回 值:无
  * 说    明:主函数是程序执行的起点,负责执行整个程序的主要逻辑‌
  */
void main()
{
	Timer0_Init();  //定时器0初始化
	LCD_Init();	//LCD1602初始化

	while(1)
	{
		KeyNum=MatrixKey();	//获取键码
		
		if(KeyNum)	//如果有按键按下
		{
			if(Mode==0)	//如果是显示游戏名称的界面
			{
				if(KeyNum>=33 && KeyNum<=48)	//如果按下任意按键(松手瞬间)
				{
					Mode=1;
					OnceFlag=1;
				}
			}
			else if(Mode==1)	//如果是设置数字范围的界面
			{
				if(KeyNum>=33 && KeyNum<=42)	//如果按下"0~9"(松手瞬间),即按下S1~S10,S10为0
				{
					if(InputLength<4)	//限制输入的最大位数为4
					{
						if(InputLength!=0 || KeyNum!=42)	//第一个数字不能为0
						{
							NowNumber=(KeyNum-32)%10;
							InputLength++;
						}
					}
				}
				if(KeyNum==27)	//如果按下S11(长按超1s)
				{
					OnceFlag=1;	//重置
				}
				if(KeyNum==43)	//如果按下S11(松手瞬间),则清空,重新输入
				{
					ReenterFlag=1;
				}
				if(KeyNum==44)	//如果按下S12(松手瞬间),表示确认
				{
					if(InputLength){ConfirmFlag=1;}	//按了数字键,才能确认
				}
			}
			else if(Mode==2)	//如果是正在猜数字
			{
				if(KeyNum>=33 && KeyNum<=42)	//如果按下"0~9"(松手瞬间),即按下S1~S10,S10为0
				{
					if(InputLength<4)	//限制输入的最大位数为4
					{
						if(InputLength!=0 || KeyNum!=42)	//第一个数字不能为0
						{
							NowNumber=(KeyNum-32)%10;
							InputLength++;
						}
					}
				}
				if(KeyNum==27)	//如果按下S11(长按超1s)
				{
					Mode=1;	//返回设置范围的界面
					OnceFlag=1;
				}
				if(KeyNum==43)	//如果按下S11(松手瞬间),则清空,重新输入
				{
					ReenterFlag=1;
				}
				if(KeyNum==44)	//如果按下S12(松手瞬间),表示确认
				{
					if(InputLength){ConfirmFlag=1;}	//按了数字键,才能确认
				}
			}
			else if(Mode==3)	//如果是猜对之后的屏幕显示
			{
				if(KeyNum==43)	//如果按下S11(松手瞬间)
				{
					Mode=1;	//返回设置范围的界面
					OnceFlag=1;
				}
			}

		}

		if(Mode==0)	//显示游戏名称
		{
			if(OnceFlag)
			{
				OnceFlag=0;	//标志置0
				
				/*初始化*/
				LCD_Clear();	//LCD1602清屏
				LCD_ShowString(1,3,"Guess Number");	//显示游戏英文名
			}

		}

		else if(Mode==1)	//设置要猜的数字的范围
		{
			if(OnceFlag)
			{
				OnceFlag=0;

				//初始化
				LastInputLength=0;	//上一次输入的位数清零
				InputLength=0;	//输入的位数清零
				ConfirmFlag=0;	//确认标志清零
				CheckFlag=0;	//检查的标志置0
				ReenterFlag=0;	//重新输入的标志置0
				SetMinFlag=1;	//正在设置最小值的标志置1
				SetMaxFlag=0;	//正在设置最大值的标志置0
				MinNumber=0;	//最小值清零
				MaxNumber=0;	//最大值清零
				MinLength=0;	//最小值的位数清零
				MaxLength=0;	//最大值的位数清零
				GuessTimes=0;	//猜数字的次数清零
				LCD_Clear();	//清屏
				LCD_ShowString(1,1,"Set Range");
				LCD_ShowString(2,1,"Min:");
				LCD_ShowString(2,9,"Max:");	//显示英文提示
				LCD_SetCursor(2,5);	//设置光标在第2行第5列
				LCD_IsShowCursor(1);	//显示光标
			}
			if(LastInputLength!=InputLength)	//如果按了S1~S10(0~9),S10为0
			{
				LastInputLength=InputLength;	//更新上一次的输入长度
				if(SetMaxFlag)	//如果正在设置最大值
				{
					LCD_ShowNum(2,12+InputLength,NowNumber,1);	//将所按的数字显示在屏幕上
					MaxNumber*=10;MaxNumber+=NowNumber;	//将刚刚所按的数字键对应的数字保存到变量MaxNumber中
					MaxLength++;	//最大数字的位数增加一
				}
				if(SetMinFlag)	//如果正在设置最小值
				{
					LCD_ShowNum(2,4+InputLength,NowNumber,1);	//将所按的数字显示在屏幕上
					MinNumber*=10;MinNumber+=NowNumber;
					MinLength++;
				}
			}
			if(ConfirmFlag || InputLength==4)	//如果按了确认键,或者输入长度达到4
			{
				if(SetMaxFlag)	//如果是正在设置最大值
				{
					SetMaxFlag=0;	//正在设置最大值的标志置0
					ConfirmFlag=0;	//确认的标志置0
					LastInputLength=0;
					InputLength=0;

					CheckFlag=1;	//检查范围是否合理的标志置1
				}
				if(SetMinFlag)	//如果是正在设置最小值
				{
					SetMinFlag=0;	//正在设置最小值的标志置0
					ConfirmFlag=0;
					LastInputLength=0;
					InputLength=0;

					LCD_SetCursor(2,13);	//光标设置在第2行第13列
					SetMaxFlag=1;	//正在设置最大值的标志置1
				}
			}
			if(CheckFlag)	//如果检查范围是否合理的标志为真
			{
				CheckFlag=0;	//标志置0
				if(MinNumber+1<MaxNumber)	//如果最小值要比最大值小,且最小值和最大值要至少相差2
				{	//说明设置得合理
					Mode=2;	//切换到玩家输入数字猜测的界面
					OnceFlag=1;
				}
				else	//如果不合理
				{
					OnceFlag=1;	//重置,即初始化
					LCD_ShowString(1,14,"ERR");	//第一行后三个位置显示错误的英文的前三个字母
					Delay(1000);	//延时1s
				}
			}
			if(ReenterFlag)	//如果重新输入的标志为真
			{
				ReenterFlag=0;	//标志置0
				if(SetMinFlag)	//如果是正在设置最小值
				{
					LastInputLength=0;
					InputLength=0;
					MinNumber=0;
					MinLength=0;
					LCD_ShowString(2,5,"    ");	//清空显示
					LCD_SetCursor(2,5);	//光标设置在第2行第5列
				}
				if(SetMaxFlag)	//如果是正在设置最大值
				{
					LastInputLength=0;
					InputLength=0;
					MaxNumber=0;
					MaxLength=0;
					LCD_ShowString(2,13,"    ");	//清空显示
					LCD_SetCursor(2,13);	//光标设置在第2行第13列
				}
			}
		}

		else if(Mode==2)	//猜数字过程
		{
			if(OnceFlag)
			{
				OnceFlag=0;

				//初始化
				LastInputLength=0;
				InputLength=0;
				ConfirmFlag=0;
				srand(TL0);	//每次进入此if,以定时器低八位做随机数的种子
				//RandomNumber=(rand()%(MaxNumber-MinNumber-1)+MinNumber+1);	//这样也可以,但是发现产生的随机数很集中
				//按下面这样,利用定时器0的高低八位来使计算复杂一些,产生的随机数更加随机
				RandomNumber=((rand()+TH0*TL0+TL0/TH0)*(TH0-TL0)%(MaxNumber-MinNumber-1)+MinNumber+1);//产生一个所设置范围内的随机数(不包含最小值和最大值)
				LCD_Clear();
				LCD_ShowString(1,1,"Range:");
				LCD_ShowNum(1,8,MinNumber,MinLength);
				LCD_ShowChar(1,8+MinLength,'~');	//显示一个右箭头→
				LCD_ShowNum(1,9+MinLength,MaxNumber,MaxLength);
				LCD_SetCursor(2,1);	//设置光标在第2行第1列
			}
			if(LastInputLength!=InputLength)
			{
				LastInputLength=InputLength;
				LCD_ShowNum(2,InputLength,NowNumber,1);
				PlayerNumber*=10;PlayerNumber+=NowNumber;
				PlayerLength++;
			}
			if(ConfirmFlag || InputLength==4)
			{
				ConfirmFlag=0;
				LastInputLength=0;
				InputLength=0;
				GuessTimes++;	//每比较一次,猜的次数增加一

				if(PlayerNumber==RandomNumber)	//如果输入的数字跟随机数字相等
				{	//说明猜对了
					Mode=3;	//切换到下一模式
					OnceFlag=1;
				}
				else if(PlayerNumber<RandomNumber && PlayerNumber>MinNumber)	//如果输入数字小于随机数,且大于范围最小值
				{
					MinNumber=PlayerNumber;
					MinLength=PlayerLength;
				}
				else if(PlayerNumber>RandomNumber && PlayerNumber<MaxNumber)	//如果输入数字大于随机数,且小于范围最大值
				{
					MaxNumber=PlayerNumber;
					MaxLength=PlayerLength;
				}
				else	//如果不是以上情况,说明输入的数字不在范围内
				{
					GuessTimes--;	//输入错误不算次数
					LCD_ShowString(2,14,"ERR");	//第2行最后三个位置显示错误的英文的前三个字母
					Delay(1000);	//延时1s
					LCD_ShowString(2,14,"   ");	//清空三个英文字母的显示
				}
				PlayerNumber=0;	//玩家输入的数字清0
				PlayerLength=0;	//玩家输入数字的位数清零

				LCD_ShowString(1,8,"         ");	//第1行右半屏清空显示
				LCD_ShowNum(1,8,MinNumber,MinLength);
				LCD_ShowChar(1,8+MinLength,'~');
				LCD_ShowNum(1,9+MinLength,MaxNumber,MaxLength);	//根据玩家输入的数字显示缩小后的范围
				LCD_SetCursor(2,1);
				
				LastInputLength=0;
				InputLength=0;
				LCD_ShowString(2,1,"    ");	//清空玩家输入的数字
				LCD_SetCursor(2,1);	//设置光标在第2行第1列
			}
			if(ReenterFlag)
			{
				ReenterFlag=0;
				LastInputLength=0;
				InputLength=0;
				PlayerNumber=0;	//玩家输入的数字清0
				PlayerLength=0;	//玩家输入数字的位数清零
				LCD_ShowString(2,1,"    ");
				LCD_SetCursor(2,1);
			}

		}
		else if(Mode==3)	//猜中之后的屏幕显示
		{
			if(OnceFlag)
			{
				OnceFlag=0;

				LCD_Clear();

				LCD_ShowString(1,1,"Yes!");
				LCD_ShowString(1,6,"Number:");
				if(RandomNumber/1000){RandomLength=4;}
				else if(RandomNumber/100){RandomLength=3;}
				else if(RandomNumber/10){RandomLength=2;}
				else{RandomLength=1;}
				LCD_ShowNum(1,13,RandomNumber,RandomLength);

				LCD_ShowString(2,1,"Times:");
				if(GuessTimes/1000){GuessLength=4;}
				else if(GuessTimes/100){GuessLength=3;}
				else if(GuessTimes/10){GuessLength=2;}
				else{GuessLength=1;}
				LCD_ShowNum(2,7,GuessTimes,GuessLength);

				LCD_IsShowCursor(0);
			}
		}


	}
}

/**
  * 函    数:定时器0中断函数
  * 参    数:无
  * 返 回 值:无
  */
void Timer0_Routine() interrupt 1
{
	static unsigned char T0Count1;	//定时器0计数变量
	TL0=0x00;	//设置定时初值,定时10ms,[email protected]
	TH0=0xDC;	//设置定时初值,定时10ms,[email protected]
 	T0Count1++;
 	if(T0Count1>=2)	//每隔20ms检测一次按键
 	{
 		T0Count1=0;
		MatrixKey_Tick();
 	}
}

总结

根据设置的范围产生真随机数这一块,感觉还需要改进一下。

相关推荐
河湾边的一亩三分地14 分钟前
STM32 PulseSensor心跳传感器驱动代码
stm32·单片机·嵌入式硬件
mftang1 小时前
zephyr OS架构下构建Nordic MCU boot
单片机·嵌入式硬件
百里东风1 小时前
STM32外设-GPIO输出(不含复用)
c语言·stm32·单片机·嵌入式硬件
菜只因C1 小时前
深入探索 51 单片机:从入门到实践的全面指南
单片机·嵌入式硬件
不想被吃掉氩3 小时前
I.MX6U的GPIO配置和LED点灯实验。
单片机·嵌入式硬件
逼子格3 小时前
正弦波、方波、三角波和锯齿波信号发生器——Multisim电路仿真
单片机·嵌入式硬件·硬件工程师·multisim·电路仿真·信号发生器·占空比
二年级程序员4 小时前
ARM介绍及其体系结构
嵌入式硬件·arm
Ruannn(努力版)8 小时前
嵌入式GPIO 实验(流水灯程序,八段数码管显示程序)
单片机·嵌入式硬件
技术干货贩卖机9 小时前
0基础 | Proteus电路仿真 | 电机使用
单片机·嵌入式硬件·proteus·代码·0基础·proteus 仿真