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

打印 上一主题 下一主题

主题 2041|帖子 2041|积分 6123

系列文章目次


前言

用的是普中A2开发板,用到板上的矩阵按键,还必要外接一个LCD1602液晶显示屏。
【单片机】STC89C52RC
【频率】12T@11.0592MHz
   效果查看/操作演示:B站搜索“甘腾胜”或“gantengsheng”查看。
源代码下载:B站对应视频的简介有工程文件下载链接。
  一、效果展示





二、原理分析

1、数字的输入与清除
输入数字时,通过十进制的移位和加法,将刚输入的数字及时保存到int变量中。
2、随机数的产生
设置的最大范围是:1~9999,我发现当范围较大时,虽然每次获取键码时用了定时器0的低八位做种子,但发现产生的随机数比力会集,会集在其中的一个小范围内。然后我将随机数的产生的算式弄复杂一些,就比力随机了,但还是有一定的伪随机性。道友有更好的办法请跟我说一下,谢谢了!
3、随机数的范围
由于屏幕巨细有限,以是限定随机数的范围是1~9999,最多四位数。设定范围的时间要注意,最大值要比最小值大2以上,如果不公道,则必要重置后重新输入。
4、按键的检测
矩阵按键通过行列扫描检测,使用定时器0,每隔20ms检测一次,防止阻塞步伐运行。
三、各模块代码

1、LCD1602

h文件
  1. #ifndef __LCD1602_H__
  2. #define __LCD1602_H__
  3. void LCD_WriteCommand(unsigned char Command);
  4. void LCD_WriteData(unsigned char Data);
  5. void LCD_SetCursor(unsigned char Line,unsigned char Column);
  6. void LCD_Init();
  7. void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
  8. void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
  9. void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
  10. void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
  11. void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
  12. void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
  13. void LCD_Clear(void);
  14. void LCD_IsShowCursor(unsigned char State);
  15. void LCD_Show16Char(unsigned char Line,unsigned char Column,char *String);
  16. #endif
复制代码
c文件
  1. #include <REGX52.H>
  2. /*引脚配置*/
  3. sbit LCD_RS=P2^6;
  4. sbit LCD_RW=P2^5;
  5. sbit LCD_EN=P2^7;
  6. #define LCD_DataPort P0
  7. /*函数定义*/
  8. /**
  9.   * 函    数:LCD1602私有延时函数,延时约50us
  10.   * 参    数:无
  11.   * 返 回 值:无
  12.   */
  13. void LCD_Delay50us(void)        //12T@11.0592MHz
  14. {
  15.         unsigned char i;
  16.         i=20;
  17.         while(--i);
  18. }
  19. /**
  20.   * 函    数:LCD1602私有延时函数,延时约2ms
  21.   * 参    数:无
  22.   * 返 回 值:无
  23.   */
  24. void LCD_Delay2ms(void)        //12T@11.0592MHz
  25. {
  26.         unsigned char i,j;
  27.         i=4;
  28.         j=146;
  29.         do
  30.         {
  31.                 while(--j);
  32.         }while(--i);
  33. }
  34. /**
  35.   * 函    数:LCD1602写指令
  36.   * 参    数:Command 要写入的指令
  37.   * 返 回 值:无
  38.   */
  39. void LCD_WriteCommand(unsigned char Command)
  40. {
  41.         LCD_RS=0;
  42.         LCD_RW=0;
  43.         LCD_DataPort=Command;
  44.         LCD_EN=1;
  45.         LCD_Delay50us();
  46.         LCD_EN=0;
  47.         LCD_Delay50us();
  48. }
  49. /**
  50.   * 函    数:LCD1602写数据
  51.   * 参    数:Data 要写入的数据
  52.   * 返 回 值:无
  53.   */
  54. void LCD_WriteData(unsigned char Data)
  55. {
  56.         LCD_RS=1;
  57.         LCD_RW=0;
  58.         LCD_DataPort=Data;
  59.         LCD_EN=1;
  60.         LCD_Delay50us();
  61.         LCD_EN=0;
  62.         LCD_Delay50us();
  63. }
  64. /**
  65.   * 函    数:LCD1602设置光标位置
  66.   * 参    数:Line 行位置,范围:1~2
  67.   * 参    数:Column 列位置,范围:1~16
  68.   * 返 回 值:无
  69.   */
  70. void LCD_SetCursor(unsigned char Line,unsigned char Column)
  71. {
  72.         if(Line==1)
  73.         {
  74.                 LCD_WriteCommand(0x80|(Column-1));
  75.         }
  76.         else if(Line==2)
  77.         {
  78.                 LCD_WriteCommand(0x80|(Column-1+0x40));
  79.         }
  80. }
  81. /**
  82.   * 函    数:LCD1602初始化函数
  83.   * 参    数:无
  84.   * 返 回 值:无
  85.   */
  86. void LCD_Init()
  87. {
  88.         LCD_WriteCommand(0x38);        //八位数据接口,两行显示,5*7点阵
  89.         LCD_WriteCommand(0x0C);        //显示开,光标关,闪烁关
  90.         LCD_WriteCommand(0x06);        //数据读写操作后,光标自动加一,画面不动
  91.         LCD_WriteCommand(0x01);        //光标复位,清屏
  92.         LCD_Delay2ms();        //清屏指令执行需要较长时间,需要较长的延时
  93. }
  94. /**
  95.   * 函    数:在LCD1602指定位置上显示一个字符
  96.   * 参    数:Line 行位置,范围:1~2
  97.   * 参    数:Column 列位置,范围:1~16
  98.   * 参    数:Char 要显示的字符
  99.   * 返 回 值:无
  100.   */
  101. void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
  102. {
  103.         LCD_SetCursor(Line,Column);
  104.         LCD_WriteData(Char);
  105. }
  106. /**
  107.   * 函    数:在LCD1602指定位置开始显示所给字符串
  108.   * 参    数:Line 起始行位置,范围:1~2
  109.   * 参    数:Column 起始列位置,范围:1~16
  110.   * 参    数:String 要显示的字符串
  111.   * 返 回 值:无
  112.   */
  113. void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
  114. {
  115.         unsigned char i;
  116.         LCD_SetCursor(Line,Column);
  117.         for(i=0;String[i]!='\0';i++)
  118.         {
  119.                 LCD_WriteData(String[i]);
  120.         }
  121. }
  122. /**
  123.   * 函    数:返回值=X的Y次方
  124.   */
  125. int LCD_Pow(int X,int Y)
  126. {
  127.         unsigned char i;
  128.         int Result=1;
  129.         for(i=0;i<Y;i++)
  130.         {
  131.                 Result*=X;
  132.         }
  133.         return Result;
  134. }
  135. /**
  136.   * 函    数:在LCD1602指定位置开始显示所给数字
  137.   * 参    数:Line 起始行位置,范围:1~2
  138.   * 参    数:Column 起始列位置,范围:1~16
  139.   * 参    数:Number 要显示的数字,范围:0~65535
  140.   * 参    数:Length 要显示数字的长度,范围:1~5
  141.   * 返 回 值:无
  142.   */
  143. void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
  144. {
  145.         unsigned char i;
  146.         LCD_SetCursor(Line,Column);
  147.         for(i=Length;i>0;i--)
  148.         {
  149.                 LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
  150.         }
  151. }
  152. /**
  153.   * 函    数:在LCD1602指定位置开始以有符号十进制显示所给数字
  154.   * 参    数:Line 起始行位置,范围:1~2
  155.   * 参    数:Column 起始列位置,范围:1~16
  156.   * 参    数:Number 要显示的数字,范围:-32768~32767
  157.   * 参    数:Length 要显示数字的长度,范围:1~5
  158.   * 返 回 值:无
  159.   */
  160. void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
  161. {
  162.         unsigned char i;
  163.         unsigned int Number1;
  164.         LCD_SetCursor(Line,Column);
  165.         if(Number>=0)
  166.         {
  167.                 LCD_WriteData('+');
  168.                 Number1=Number;
  169.         }
  170.         else
  171.         {
  172.                 LCD_WriteData('-');
  173.                 Number1=-Number;
  174.         }
  175.         for(i=Length;i>0;i--)
  176.         {
  177.                 LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
  178.         }
  179. }
  180. /**
  181.   * 函    数:在LCD1602指定位置开始以十六进制显示所给数字
  182.   * 参    数:Line 起始行位置,范围:1~2
  183.   * 参    数:Column 起始列位置,范围:1~16
  184.   * 参    数:Number 要显示的数字,范围:0~0xFFFF
  185.   * 参    数:Length 要显示数字的长度,范围:1~4
  186.   * 返 回 值:无
  187.   */
  188. void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
  189. {
  190.         unsigned char i,SingleNumber;
  191.         LCD_SetCursor(Line,Column);
  192.         for(i=Length;i>0;i--)
  193.         {
  194.                 SingleNumber=Number/LCD_Pow(16,i-1)%16;
  195.                 if(SingleNumber<10)
  196.                 {
  197.                         LCD_WriteData(SingleNumber+'0');
  198.                 }
  199.                 else
  200.                 {
  201.                         LCD_WriteData(SingleNumber-10+'A');
  202.                 }
  203.         }
  204. }
  205. /**
  206.   * 函    数:在LCD1602指定位置开始以二进制显示所给数字
  207.   * 参    数:Line 起始行位置,范围:1~2
  208.   * 参    数:Column 起始列位置,范围:1~16
  209.   * 参    数:Number 要显示的数字,范围:0~1111 1111 1111 1111
  210.   * 参    数:Length 要显示数字的长度,范围:1~16
  211.   * 返 回 值:无
  212.   */
  213. void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
  214. {
  215.         unsigned char i;
  216.         LCD_SetCursor(Line,Column);
  217.         for(i=Length;i>0;i--)
  218.         {
  219.                 LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
  220.         }
  221. }
  222. /**
  223.   * 函    数:LCD1602的光标复位,清屏
  224.   * 参    数:无
  225.   * 返 回 值:无
  226.   */
  227. void LCD_Clear(void)
  228. {
  229.         LCD_WriteCommand(0x01);
  230.         LCD_Delay2ms();
  231. }
  232. /**
  233.   * 函    数:LCD1602设置光标是否显示
  234.   * 参    数:State 光标状态,1:显示,0:不显示
  235.   * 返 回 值:无
  236.   */
  237. void LCD_IsShowCursor(unsigned char State)
  238. {
  239.         if(State){LCD_WriteCommand(0x0E);}
  240.         else{LCD_WriteCommand(0x0C);}
  241. }
复制代码
2、矩阵按键

h文件
  1. #ifndef __MATRIXKEYSCAN_H__
  2. #define __MATRIXKEYSCAN_H__
  3. unsigned char MatrixKey(void);
  4. void MatrixKey_Tick(void);
  5. #endif
复制代码
c文件
  1. #include <REGX52.H>
  2. #define Matrix_Port P1        //矩阵按键接口
  3. unsigned char KeyNumber;
  4. /**
  5.   * 函    数:获取矩阵按键键码
  6.   * 参    数:无
  7.   * 返 回 值:按下按键的键码,范围:0~48,0表示无按键按下
  8.   * 说    明:在下一次检测按键之前,第二次获取键码一定会返回0
  9.   */
  10. unsigned char MatrixKey(void)
  11. {
  12.         unsigned char KeyTemp=0;
  13.         KeyTemp=KeyNumber;
  14.         KeyNumber=0;
  15.         return KeyTemp;
  16. }
  17. /**
  18.   * 函    数:检测当前按下的按键,无消抖及松手检测
  19.   * 参    数:无
  20.   * 返 回 值:按下按键的键值,范围:0~16,无按键按下时返回值为0
  21.   */
  22. unsigned char Key_GetState()
  23. {
  24.         unsigned char KeyValue=0;
  25.         unsigned char i=0;
  26.        
  27.         Matrix_Port=0x0F;        //给所有行赋值0,列全为1
  28.         i=2;while(i--);        //适当延时,延时5us
  29.         if(Matrix_Port!=0x0F)
  30.         {
  31.                 Matrix_Port=0x0F;        //测试列
  32.                 i=2;while(i--);
  33.                 switch(Matrix_Port)        //所有行拉低,检测哪一列按下
  34.                 {
  35.                         case 0x07:KeyValue=1;break;
  36.                         case 0x0B:KeyValue=2;break;
  37.                         case 0x0D:KeyValue=3;break;
  38.                         case 0x0E:KeyValue=4;break;
  39.                         default:break;
  40.                 }
  41.                 Matrix_Port=0xF0;        //测试行
  42.                 i=2;while(i--);
  43.                 switch(Matrix_Port)        //所有列拉低,检测哪一行按下
  44.                 {
  45.                         case 0x70:KeyValue=KeyValue;break;
  46.                         case 0xB0:KeyValue=KeyValue+4;break;
  47.                         case 0xD0:KeyValue=KeyValue+8;break;
  48.                         case 0xE0:KeyValue=KeyValue+12;break;
  49.                         default:break;
  50.                 }
  51.         }
  52.         else
  53.         {
  54.                 KeyValue=0;
  55.         }
  56.         return KeyValue;
  57. }
  58. /**
  59.   * 函    数:矩阵按键驱动函数,在中断中调用
  60.   * 参    数:无
  61.   * 返 回 值:无
  62.   */
  63. void MatrixKey_Tick(void)
  64. {
  65.         static unsigned char NowState,LastState;
  66.         static unsigned int KeyCount;
  67.         LastState=NowState;        //更新上一次的键值
  68.         NowState=Key_GetState();        //获取当前的键值
  69.         //如果上个时间点按键未按下,这个时间点按键按下,则是按下瞬间
  70.         if(LastState==0)
  71.         {
  72.                 switch(NowState)
  73.                 {
  74.                         case 1:KeyNumber=1;break;
  75.                         case 2:KeyNumber=2;break;
  76.                         case 3:KeyNumber=3;break;
  77.                         case 4:KeyNumber=4;break;
  78.                         case 5:KeyNumber=5;break;
  79.                         case 6:KeyNumber=6;break;
  80.                         case 7:KeyNumber=7;break;
  81.                         case 8:KeyNumber=8;break;
  82.                         case 9:KeyNumber=9;break;
  83.                         case 10:KeyNumber=10;break;
  84.                         case 11:KeyNumber=11;break;
  85.                         case 12:KeyNumber=12;break;
  86.                         case 13:KeyNumber=13;break;
  87.                         case 14:KeyNumber=14;break;
  88.                         case 15:KeyNumber=15;break;
  89.                         case 16:KeyNumber=16;break;
  90.                         default:break;
  91.                 }
  92.         }
  93.        
  94.         //如果上个时间点按键按下,这个时间点按键还是按下,则是长按
  95.         if(LastState && NowState)
  96.         {
  97.                 KeyCount++;
  98.                 if(KeyCount%50==0)        //定时器中断函数中每隔20ms检测一次按键
  99.                 {        //长按后每隔1s返回一次长按的键码
  100.                         if     (LastState== 1 && NowState== 1){KeyNumber=17;}
  101.                         else if(LastState== 2 && NowState== 2){KeyNumber=18;}
  102.                         else if(LastState== 3 && NowState== 3){KeyNumber=19;}
  103.                         else if(LastState== 4 && NowState== 4){KeyNumber=20;}
  104.                         else if(LastState== 5 && NowState== 5){KeyNumber=21;}
  105.                         else if(LastState== 6 && NowState== 6){KeyNumber=22;}
  106.                         else if(LastState== 7 && NowState== 7){KeyNumber=23;}
  107.                         else if(LastState== 8 && NowState== 8){KeyNumber=24;}
  108.                         else if(LastState== 9 && NowState== 9){KeyNumber=25;}
  109.                         else if(LastState==10 && NowState==10){KeyNumber=26;}
  110.                         else if(LastState==11 && NowState==11){KeyNumber=27;}
  111.                         else if(LastState==12 && NowState==12){KeyNumber=28;}
  112.                         else if(LastState==13 && NowState==13){KeyNumber=29;}
  113.                         else if(LastState==14 && NowState==14){KeyNumber=30;}
  114.                         else if(LastState==15 && NowState==15){KeyNumber=31;}
  115.                         else if(LastState==16 && NowState==16){KeyNumber=32;}
  116.                 }
  117.         }
  118.         else
  119.         {
  120.                 KeyCount=0;
  121.         }
  122.         //如果上个时间点按键按下,这个时间点按键未按下,则是松手瞬间
  123.         if(NowState==0)
  124.         {
  125.                 switch(LastState)
  126.                 {
  127.                         case 1:KeyNumber=33;break;
  128.                         case 2:KeyNumber=34;break;
  129.                         case 3:KeyNumber=35;break;
  130.                         case 4:KeyNumber=36;break;
  131.                         case 5:KeyNumber=37;break;
  132.                         case 6:KeyNumber=38;break;
  133.                         case 7:KeyNumber=39;break;
  134.                         case 8:KeyNumber=40;break;
  135.                         case 9:KeyNumber=41;break;
  136.                         case 10:KeyNumber=42;break;
  137.                         case 11:KeyNumber=43;break;
  138.                         case 12:KeyNumber=44;break;
  139.                         case 13:KeyNumber=45;break;
  140.                         case 14:KeyNumber=46;break;
  141.                         case 15:KeyNumber=47;break;
  142.                         case 16:KeyNumber=48;break;
  143.                         default:break;
  144.                 }
  145.         }
  146. }
复制代码
3、定时器0

h文件
  1. #ifndef __TIMER0_H__
  2. #define __TIMER0_H__
  3. void Timer0_Init(void);
  4. #endif
复制代码
c文件
  1. #include <REGX52.H>
  2. /**
  3.   * 函    数:定时器0初始化
  4.   * 参    数:无
  5.   * 返 回 值:无
  6.   */
  7. void Timer0_Init(void)
  8. {       
  9. //        AUXR&=0x7F;        //定时器时钟12T模式(STC89C52RC是12T单片机,无需设置)
  10.         TMOD&=0xF0;        //设置定时器模式(高四位不变,低四位清零)
  11.         TMOD|=0x01;        //设置定时器模式(通过低四位设为16位不自动重装)
  12.         TL0=0x00;        //设置定时初值,定时10ms,12T@11.0592MHz
  13.         TH0=0xDC;        //设置定时初值,定时10ms,12T@11.0592MHz
  14.         TF0=0;        //清除TF0标志
  15.         TR0=1;        //定时器0开始计时
  16.         ET0=1;        //打开定时器0中断允许
  17.         EA=1;        //打开总中断
  18.         PT0=0;        //当PT0=0时,定时器0为低优先级,当PT0=1时,定时器0为高优先级
  19. }
  20. /*定时器中断函数模板
  21. void Timer0_Routine() interrupt 1        //定时器0中断函数
  22. {
  23.         static unsigned int T0Count;        //定义静态变量
  24.         TL0=0x00;        //设置定时初值,定时10ms,12T@11.0592MHz
  25.         TH0=0xDC;        //设置定时初值,定时10ms,12T@11.0592MHz
  26.         T0Count++;
  27.         if(T0Count>=1000)
  28.         {
  29.                 T0Count=0;
  30.                
  31.         }
  32. }
  33. */
复制代码
四、主函数

main.c
  1. /*by甘腾胜@20250423
  2. 【效果查看/操作演示】B站搜索“甘腾胜”或“gantengsheng”查看
  3. 【单片机】STC89C52RC
  4. 【频率】12T@11.0592MHz
  5. 【外设】LCD1602、矩阵按键
  6. 【简单的原理分析】https://blog.csdn.net/gantengsheng/article/details/143581157
  7. 【注意】点阵屏旁边的跳线帽接三根排针中的右边两根
  8. 【操作】
  9.         S1                S2                S3                S4
  10.         S5                S6                S7                S8
  11.         S9                S10                S11                S12
  12.         S13                S14                S15                S16
  13. S1~S9:数字1~9
  14. S10:数字0
  15. S11:清空/返回
  16. S12:确认键
  17. (1)输入错误可按S11(松手瞬间)清空后重新输入
  18. (2)按S12(松手瞬间)表示确认完成输入
  19. (3)猜对数字后按S11(松手瞬间)返回设置范围的界面
  20. */
  21. #include <REGX52.H>        //51单片机头文件
  22. #include "LCD1602.h"        //LCD1602液晶显示屏
  23. #include "MatrixKeyScan.h"        //矩阵按键
  24. #include "Timer0.h"        //定时器0
  25. #include <STDLIB.H>        //随机函数
  26. unsigned char KeyNum;        //存储获得的键码
  27. unsigned char Mode;        //模式,0:显示游戏英文名,1:设置数字范围界面,2:猜数字过程,3:猜对后的屏幕显示
  28. unsigned char OnceFlag=1;        //某些前提下只执行一次的标志(各模式中用来判断是否要初始化),1:执行,0:不执行
  29. unsigned char InputLength;        //输入位数,范围:1~4
  30. unsigned char LastInputLength;        //上一次的输入位数,范围:1~4,用来判断是否有按下数字键0~9
  31. bit ConfirmFlag;        //确认按键按下的标志,1:确认按键按下,0:确认按键未按下
  32. unsigned char NowNumber;        //刚刚输入的数字按键对应的数字,范围:0~9
  33. bit SetMinFlag;        //正在设置数字范围最小值的标志,1:正在设置,0:没有在设置
  34. bit SetMaxFlag;        //正在设置数字范围最大值的标志,1:正在设置,0:没有在设置
  35. unsigned int MinNumber;        //所设置的数字范围的最小值,范围:1~9997
  36. unsigned int MaxNumber;        //所设置的数字范围的最大值,范围:3~9999
  37. unsigned char MinLength;        //最小值的位数,范围:1~4
  38. unsigned char MaxLength;        //最大值的位数,范围:1~4
  39. bit CheckFlag;        //设置完范围后检查范围是否合理的标志,1:检查,0:不检查
  40. bit ReenterFlag;        //重新输入的标志,1:重新输入,0:不重新输入
  41. unsigned int PlayerNumber;        //玩家输入的数字,范围:2~9998
  42. unsigned char PlayerLength;        //玩家输入数字的位数,范围:1~4
  43. unsigned int RandomNumber;        //产生的随机数,范围:MinNumber<RandomNumber<MaxNumber
  44. unsigned char RandomLength;        //随机数的位数,范围:1~4
  45. unsigned int GuessTimes;        //玩家猜测的次数
  46. unsigned char GuessLength;        //玩家猜测的次数的位数,范围:1~4
  47. /**
  48.   * 函    数:延时函数,延时约xms毫秒
  49.   * 参    数:xms 延时的时间,范围:0~65535
  50.   * 返 回 值:无
  51.   */
  52. void Delay(unsigned int xms)        //12T@11.0592MHz
  53. {
  54.         unsigned char i,j;
  55.         while(xms--)
  56.         {
  57.                 i=2;
  58.                 j=199;
  59.                 do
  60.                 {
  61.                         while(--j);
  62.                 }while(--i);
  63.         }
  64. }
  65. /**
  66.   * 函    数:主函数(有且仅有一个)
  67.   * 参    数:无
  68.   * 返 回 值:无
  69.   * 说    明:主函数是程序执行的起点,负责执行整个程序的主要逻辑‌
  70.   */
  71. void main()
  72. {
  73.         Timer0_Init();  //定时器0初始化
  74.         LCD_Init();        //LCD1602初始化
  75.         while(1)
  76.         {
  77.                 KeyNum=MatrixKey();        //获取键码
  78.                
  79.                 if(KeyNum)        //如果有按键按下
  80.                 {
  81.                         if(Mode==0)        //如果是显示游戏名称的界面
  82.                         {
  83.                                 if(KeyNum>=33 && KeyNum<=48)        //如果按下任意按键(松手瞬间)
  84.                                 {
  85.                                         Mode=1;
  86.                                         OnceFlag=1;
  87.                                 }
  88.                         }
  89.                         else if(Mode==1)        //如果是设置数字范围的界面
  90.                         {
  91.                                 if(KeyNum>=33 && KeyNum<=42)        //如果按下“0~9”(松手瞬间),即按下S1~S10,S10为0
  92.                                 {
  93.                                         if(InputLength<4)        //限制输入的最大位数为4
  94.                                         {
  95.                                                 if(InputLength!=0 || KeyNum!=42)        //第一个数字不能为0
  96.                                                 {
  97.                                                         NowNumber=(KeyNum-32)%10;
  98.                                                         InputLength++;
  99.                                                 }
  100.                                         }
  101.                                 }
  102.                                 if(KeyNum==27)        //如果按下S11(长按超1s)
  103.                                 {
  104.                                         OnceFlag=1;        //重置
  105.                                 }
  106.                                 if(KeyNum==43)        //如果按下S11(松手瞬间),则清空,重新输入
  107.                                 {
  108.                                         ReenterFlag=1;
  109.                                 }
  110.                                 if(KeyNum==44)        //如果按下S12(松手瞬间),表示确认
  111.                                 {
  112.                                         if(InputLength){ConfirmFlag=1;}        //按了数字键,才能确认
  113.                                 }
  114.                         }
  115.                         else if(Mode==2)        //如果是正在猜数字
  116.                         {
  117.                                 if(KeyNum>=33 && KeyNum<=42)        //如果按下“0~9”(松手瞬间),即按下S1~S10,S10为0
  118.                                 {
  119.                                         if(InputLength<4)        //限制输入的最大位数为4
  120.                                         {
  121.                                                 if(InputLength!=0 || KeyNum!=42)        //第一个数字不能为0
  122.                                                 {
  123.                                                         NowNumber=(KeyNum-32)%10;
  124.                                                         InputLength++;
  125.                                                 }
  126.                                         }
  127.                                 }
  128.                                 if(KeyNum==27)        //如果按下S11(长按超1s)
  129.                                 {
  130.                                         Mode=1;        //返回设置范围的界面
  131.                                         OnceFlag=1;
  132.                                 }
  133.                                 if(KeyNum==43)        //如果按下S11(松手瞬间),则清空,重新输入
  134.                                 {
  135.                                         ReenterFlag=1;
  136.                                 }
  137.                                 if(KeyNum==44)        //如果按下S12(松手瞬间),表示确认
  138.                                 {
  139.                                         if(InputLength){ConfirmFlag=1;}        //按了数字键,才能确认
  140.                                 }
  141.                         }
  142.                         else if(Mode==3)        //如果是猜对之后的屏幕显示
  143.                         {
  144.                                 if(KeyNum==43)        //如果按下S11(松手瞬间)
  145.                                 {
  146.                                         Mode=1;        //返回设置范围的界面
  147.                                         OnceFlag=1;
  148.                                 }
  149.                         }
  150.                 }
  151.                 if(Mode==0)        //显示游戏名称
  152.                 {
  153.                         if(OnceFlag)
  154.                         {
  155.                                 OnceFlag=0;        //标志置0
  156.                                
  157.                                 /*初始化*/
  158.                                 LCD_Clear();        //LCD1602清屏
  159.                                 LCD_ShowString(1,3,"Guess Number");        //显示游戏英文名
  160.                         }
  161.                 }
  162.                 else if(Mode==1)        //设置要猜的数字的范围
  163.                 {
  164.                         if(OnceFlag)
  165.                         {
  166.                                 OnceFlag=0;
  167.                                 //初始化
  168.                                 LastInputLength=0;        //上一次输入的位数清零
  169.                                 InputLength=0;        //输入的位数清零
  170.                                 ConfirmFlag=0;        //确认标志清零
  171.                                 CheckFlag=0;        //检查的标志置0
  172.                                 ReenterFlag=0;        //重新输入的标志置0
  173.                                 SetMinFlag=1;        //正在设置最小值的标志置1
  174.                                 SetMaxFlag=0;        //正在设置最大值的标志置0
  175.                                 MinNumber=0;        //最小值清零
  176.                                 MaxNumber=0;        //最大值清零
  177.                                 MinLength=0;        //最小值的位数清零
  178.                                 MaxLength=0;        //最大值的位数清零
  179.                                 GuessTimes=0;        //猜数字的次数清零
  180.                                 LCD_Clear();        //清屏
  181.                                 LCD_ShowString(1,1,"Set Range");
  182.                                 LCD_ShowString(2,1,"Min:");
  183.                                 LCD_ShowString(2,9,"Max:");        //显示英文提示
  184.                                 LCD_SetCursor(2,5);        //设置光标在第2行第5列
  185.                                 LCD_IsShowCursor(1);        //显示光标
  186.                         }
  187.                         if(LastInputLength!=InputLength)        //如果按了S1~S10(0~9),S10为0
  188.                         {
  189.                                 LastInputLength=InputLength;        //更新上一次的输入长度
  190.                                 if(SetMaxFlag)        //如果正在设置最大值
  191.                                 {
  192.                                         LCD_ShowNum(2,12+InputLength,NowNumber,1);        //将所按的数字显示在屏幕上
  193.                                         MaxNumber*=10;MaxNumber+=NowNumber;        //将刚刚所按的数字键对应的数字保存到变量MaxNumber中
  194.                                         MaxLength++;        //最大数字的位数增加一
  195.                                 }
  196.                                 if(SetMinFlag)        //如果正在设置最小值
  197.                                 {
  198.                                         LCD_ShowNum(2,4+InputLength,NowNumber,1);        //将所按的数字显示在屏幕上
  199.                                         MinNumber*=10;MinNumber+=NowNumber;
  200.                                         MinLength++;
  201.                                 }
  202.                         }
  203.                         if(ConfirmFlag || InputLength==4)        //如果按了确认键,或者输入长度达到4
  204.                         {
  205.                                 if(SetMaxFlag)        //如果是正在设置最大值
  206.                                 {
  207.                                         SetMaxFlag=0;        //正在设置最大值的标志置0
  208.                                         ConfirmFlag=0;        //确认的标志置0
  209.                                         LastInputLength=0;
  210.                                         InputLength=0;
  211.                                         CheckFlag=1;        //检查范围是否合理的标志置1
  212.                                 }
  213.                                 if(SetMinFlag)        //如果是正在设置最小值
  214.                                 {
  215.                                         SetMinFlag=0;        //正在设置最小值的标志置0
  216.                                         ConfirmFlag=0;
  217.                                         LastInputLength=0;
  218.                                         InputLength=0;
  219.                                         LCD_SetCursor(2,13);        //光标设置在第2行第13列
  220.                                         SetMaxFlag=1;        //正在设置最大值的标志置1
  221.                                 }
  222.                         }
  223.                         if(CheckFlag)        //如果检查范围是否合理的标志为真
  224.                         {
  225.                                 CheckFlag=0;        //标志置0
  226.                                 if(MinNumber+1<MaxNumber)        //如果最小值要比最大值小,且最小值和最大值要至少相差2
  227.                                 {        //说明设置得合理
  228.                                         Mode=2;        //切换到玩家输入数字猜测的界面
  229.                                         OnceFlag=1;
  230.                                 }
  231.                                 else        //如果不合理
  232.                                 {
  233.                                         OnceFlag=1;        //重置,即初始化
  234.                                         LCD_ShowString(1,14,"ERR");        //第一行后三个位置显示错误的英文的前三个字母
  235.                                         Delay(1000);        //延时1s
  236.                                 }
  237.                         }
  238.                         if(ReenterFlag)        //如果重新输入的标志为真
  239.                         {
  240.                                 ReenterFlag=0;        //标志置0
  241.                                 if(SetMinFlag)        //如果是正在设置最小值
  242.                                 {
  243.                                         LastInputLength=0;
  244.                                         InputLength=0;
  245.                                         MinNumber=0;
  246.                                         MinLength=0;
  247.                                         LCD_ShowString(2,5,"    ");        //清空显示
  248.                                         LCD_SetCursor(2,5);        //光标设置在第2行第5列
  249.                                 }
  250.                                 if(SetMaxFlag)        //如果是正在设置最大值
  251.                                 {
  252.                                         LastInputLength=0;
  253.                                         InputLength=0;
  254.                                         MaxNumber=0;
  255.                                         MaxLength=0;
  256.                                         LCD_ShowString(2,13,"    ");        //清空显示
  257.                                         LCD_SetCursor(2,13);        //光标设置在第2行第13列
  258.                                 }
  259.                         }
  260.                 }
  261.                 else if(Mode==2)        //猜数字过程
  262.                 {
  263.                         if(OnceFlag)
  264.                         {
  265.                                 OnceFlag=0;
  266.                                 //初始化
  267.                                 LastInputLength=0;
  268.                                 InputLength=0;
  269.                                 ConfirmFlag=0;
  270.                                 srand(TL0);        //每次进入此if,以定时器低八位做随机数的种子
  271.                                 //RandomNumber=(rand()%(MaxNumber-MinNumber-1)+MinNumber+1);        //这样也可以,但是发现产生的随机数很集中
  272.                                 //按下面这样,利用定时器0的高低八位来使计算复杂一些,产生的随机数更加随机
  273.                                 RandomNumber=((rand()+TH0*TL0+TL0/TH0)*(TH0-TL0)%(MaxNumber-MinNumber-1)+MinNumber+1);//产生一个所设置范围内的随机数(不包含最小值和最大值)
  274.                                 LCD_Clear();
  275.                                 LCD_ShowString(1,1,"Range:");
  276.                                 LCD_ShowNum(1,8,MinNumber,MinLength);
  277.                                 LCD_ShowChar(1,8+MinLength,'~');        //显示一个右箭头→
  278.                                 LCD_ShowNum(1,9+MinLength,MaxNumber,MaxLength);
  279.                                 LCD_SetCursor(2,1);        //设置光标在第2行第1列
  280.                         }
  281.                         if(LastInputLength!=InputLength)
  282.                         {
  283.                                 LastInputLength=InputLength;
  284.                                 LCD_ShowNum(2,InputLength,NowNumber,1);
  285.                                 PlayerNumber*=10;PlayerNumber+=NowNumber;
  286.                                 PlayerLength++;
  287.                         }
  288.                         if(ConfirmFlag || InputLength==4)
  289.                         {
  290.                                 ConfirmFlag=0;
  291.                                 LastInputLength=0;
  292.                                 InputLength=0;
  293.                                 GuessTimes++;        //每比较一次,猜的次数增加一
  294.                                 if(PlayerNumber==RandomNumber)        //如果输入的数字跟随机数字相等
  295.                                 {        //说明猜对了
  296.                                         Mode=3;        //切换到下一模式
  297.                                         OnceFlag=1;
  298.                                 }
  299.                                 else if(PlayerNumber<RandomNumber && PlayerNumber>MinNumber)        //如果输入数字小于随机数,且大于范围最小值
  300.                                 {
  301.                                         MinNumber=PlayerNumber;
  302.                                         MinLength=PlayerLength;
  303.                                 }
  304.                                 else if(PlayerNumber>RandomNumber && PlayerNumber<MaxNumber)        //如果输入数字大于随机数,且小于范围最大值
  305.                                 {
  306.                                         MaxNumber=PlayerNumber;
  307.                                         MaxLength=PlayerLength;
  308.                                 }
  309.                                 else        //如果不是以上情况,说明输入的数字不在范围内
  310.                                 {
  311.                                         GuessTimes--;        //输入错误不算次数
  312.                                         LCD_ShowString(2,14,"ERR");        //第2行最后三个位置显示错误的英文的前三个字母
  313.                                         Delay(1000);        //延时1s
  314.                                         LCD_ShowString(2,14,"   ");        //清空三个英文字母的显示
  315.                                 }
  316.                                 PlayerNumber=0;        //玩家输入的数字清0
  317.                                 PlayerLength=0;        //玩家输入数字的位数清零
  318.                                 LCD_ShowString(1,8,"         ");        //第1行右半屏清空显示
  319.                                 LCD_ShowNum(1,8,MinNumber,MinLength);
  320.                                 LCD_ShowChar(1,8+MinLength,'~');
  321.                                 LCD_ShowNum(1,9+MinLength,MaxNumber,MaxLength);        //根据玩家输入的数字显示缩小后的范围
  322.                                 LCD_SetCursor(2,1);
  323.                                
  324.                                 LastInputLength=0;
  325.                                 InputLength=0;
  326.                                 LCD_ShowString(2,1,"    ");        //清空玩家输入的数字
  327.                                 LCD_SetCursor(2,1);        //设置光标在第2行第1列
  328.                         }
  329.                         if(ReenterFlag)
  330.                         {
  331.                                 ReenterFlag=0;
  332.                                 LastInputLength=0;
  333.                                 InputLength=0;
  334.                                 PlayerNumber=0;        //玩家输入的数字清0
  335.                                 PlayerLength=0;        //玩家输入数字的位数清零
  336.                                 LCD_ShowString(2,1,"    ");
  337.                                 LCD_SetCursor(2,1);
  338.                         }
  339.                 }
  340.                 else if(Mode==3)        //猜中之后的屏幕显示
  341.                 {
  342.                         if(OnceFlag)
  343.                         {
  344.                                 OnceFlag=0;
  345.                                 LCD_Clear();
  346.                                 LCD_ShowString(1,1,"Yes!");
  347.                                 LCD_ShowString(1,6,"Number:");
  348.                                 if(RandomNumber/1000){RandomLength=4;}
  349.                                 else if(RandomNumber/100){RandomLength=3;}
  350.                                 else if(RandomNumber/10){RandomLength=2;}
  351.                                 else{RandomLength=1;}
  352.                                 LCD_ShowNum(1,13,RandomNumber,RandomLength);
  353.                                 LCD_ShowString(2,1,"Times:");
  354.                                 if(GuessTimes/1000){GuessLength=4;}
  355.                                 else if(GuessTimes/100){GuessLength=3;}
  356.                                 else if(GuessTimes/10){GuessLength=2;}
  357.                                 else{GuessLength=1;}
  358.                                 LCD_ShowNum(2,7,GuessTimes,GuessLength);
  359.                                 LCD_IsShowCursor(0);
  360.                         }
  361.                 }
  362.         }
  363. }
  364. /**
  365.   * 函    数:定时器0中断函数
  366.   * 参    数:无
  367.   * 返 回 值:无
  368.   */
  369. void Timer0_Routine() interrupt 1
  370. {
  371.         static unsigned char T0Count1;        //定时器0计数变量
  372.         TL0=0x00;        //设置定时初值,定时10ms,12T@11.0592MHz
  373.         TH0=0xDC;        //设置定时初值,定时10ms,12T@11.0592MHz
  374.         T0Count1++;
  375.         if(T0Count1>=2)        //每隔20ms检测一次按键
  376.         {
  377.                 T0Count1=0;
  378.                 MatrixKey_Tick();
  379.         }
  380. }
复制代码
总结

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

王柳

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表