suncat0504 发表于 2020-12-12 10:57 谢谢你的指点,我改对啦!再次感谢! |
楼主,我下载文件了,但是key.h中并没有扫描码的定义。需要修改Key.c这个文件 1、在sbit key_down=P3^6;后面加一行暂停键的定义接口,比如 sbit key_pause=P3^3; 这里使用的是P3.3口位,根据你的线路需要,自己改成一个空闲可用的IO口,不一定非得是P3口的。 2、在函数OSScanKey中大判断分支中加一块 else if (key_pause==0) return 60; 这里的60,就是之前说到的键盘扫描码 之前的修改中的?对应的扫描码要改成63,不是60,因为OSReadKey函数中对于短按键松开时,扫描码进行了+3的处理。比如上下左右与定义的扫描码是30,10,20,40;但在中断函数Timer0Int中判定条件是33,13,23,43. 保险起见,你也可以case 63:和case 60:都加上 case 60: // “暂停”按键标记值对应的处理分支 case 63: pause_flag^=1; // 开关暂停标志。按下单数次,标志=1;按下双数次,标志=0 KeyBuffer=PAUSE; // 设置暂停按键缓冲标志 break; |
源代码的注释不是很详实,但因为比较规范地使用英语单词作为变化量名和函数名等,比较容易分析。楼主在学习别人代码的同时,希望你也能规范化编码,提高自己的编程能力。祝你好运! |
1433049084 发表于 2020-12-10 21:12 看了看代码,大概需要该这几个地方: 1、在中断函数Timer0Int之上加一个全局变量 unsigned char pause_flag=0; 2、在Key.h文件中查找有没有和暂停有关的按键扫描码定义。比如PAUSE 如果没有,自己追加一个,数值必须和按键的扫描码一致 3、在“#define PASSSCORE 3//预定过关程序”这句后面加一行"#define PAUSE 6" 这里的6,你可以改成其他数值,你自己定,不和其它按键标记值冲突就行。 (已经用到的是1,2,3,4,5) 这个6是城中使用暂停按键对应的标记值 4、在函数Timer0Int中的switch判定分支中,追加一个case case ?: // “暂停”按键标记值对应的处理分支 pause_flag^=1; // 开关暂停标志。按下单数次,标志=1;按下双数次,标志=0 KeyBuffer=PAUSE; // 设置暂停按键缓冲标志 break; 其中,?代表的数值,是在Key.h中对应于暂停按键的按键扫描码 5、在函数GamePlay的switch(KeyBuffer)的case分之中,追加以下分支 case PAUSE: while(pause_flag); // 如果按下的是“暂停按键”,等待暂停标志=0 break; 大概就是这么个过程。 总结: 1、Timer0Int中断函数是用来检查键盘上哪个按键被按下了。按下后,根据案件的扫描码设置按键标记值。 2、函数GamePlay的switch(KeyBuffer)的case分之中,判断按键标记值。程序里已经有上下左右方向和游戏速度。现在追加暂停的处理 |
定义一个暂停按键的标志位,使暂定按键设置为最高优先级, |
加一个高级中断。暂停键出发中断,设置一个标志 |
suncat0504 发表于 2020-12-10 11:41 //12864并行接口参考程序,控制器st7920 #include "reg52.h" #include "Lcd12864.h" #include "Key.h" #define uchar unsigned char #define uint unsigned int //sbit stop=P3^3; static unsigned long Seed = 1; #define A 48271L #define M 2147483647L #define Q (M / A) #define R (M % A) /************************************ 伪随机发生器 *************************************/ double Random(void) { long TmpSeed; TmpSeed=A*(Seed%Q)-R*(Seed/Q); if(TmpSeed>=0) Seed=TmpSeed; else Seed=TmpSeed+M; return (double)Seed/M; } /************************************** 为伪随机数发生器播种 ***************************************/ void InitRandom(unsigned long InitVal) { Seed=InitVal; } //延时子程序 void delay(unsigned int t) { unsigned int i,j; for(i=0;i<t;i++) for(j=0;j<10;j++); } /********************************* 初始化MPU **********************************/ void InitCpu(void) { TMOD=0x0; TH0=0; TL0=0; TR0=1; ET0=1; EA=1; } #define N 25 struct Food { unsigned char x; unsigned char y; unsigned char yes; }food;//食物体结构 struct Snake { unsigned char x[N]; unsigned char y[N]; unsigned char node; unsigned char direction; unsigned char life; }snake;//蛇体结构 unsigned char Flag=0; unsigned char Score=0; unsigned char Speed=5; unsigned char KeyBuffer=0; #define FUNC 1 #define UP 2 #define DOWN 3 #define LEFT 4 #define RIGHT 5 #define PASSSCORE 3//预定过关程序 void Timer0Int(void) interrupt 1 { switch(OSReadKey()) { case 90: KeyBuffer=FUNC; if(++Speed>=10) Speed=1; Flag|=1<<1;//速度变化标志置1 break; case 13: KeyBuffer=DOWN; /*if(snake.direction!=2) snake.direction=1;*/ break; case 33: KeyBuffer=UP; /*if(snake.direction!=1) snake.direction=2;*/ break; case 23: KeyBuffer=RIGHT; /*if(snake.direction!=4) snake.direction=3;*/ break; case 43 : KeyBuffer=LEFT; /*if(snake.direction!=3) snake.direction=4;*/ break; default: break; } } /****************************** 画墙壁,初始化界面 *******************************/ void DrawBoard(void) { unsigned char n; for(n=0;n<31;n++) { Lcd_Rectangle(3*n,0,3*n+2,2,1); Lcd_Rectangle(3*n,60,3*n+2,62,1); } for(n=0;n<21;n++) { Lcd_Rectangle(0,3*n,2,3*n+2,1); Lcd_Rectangle(90,3*n,92,3*n+2,1); } Lcd_HoriLine(93,31,35,1); Lcd_HoriLine(93,63,35,1); } /*************************** 打印成绩 ****************************/ void PrintScore(void) { unsigned char Str[3]; Lcd_WriteStr(6,0,"成绩"); Str[0]=(Score/10)|0x30;//十位 Str[1]=(Score%10)|0x30;//个位 Str[2]=0; Lcd_WriteStr(7,1,Str); } /******************************** 打印速度级别 *********************************/ void PrintSpeed(void) { unsigned char Str[2]; Lcd_WriteStr(6,2,"级别"); Str[0]=Speed|0x30; Str[1]=0; Lcd_WriteStr(7,3,Str); } /*********************************** 游戏结束处理 ************************************/ void GameOver(void) { unsigned char n; Lcd_Rectangle(food.x,food.y,food.x+2,food.y+2,0);//消隐出食物 for(n=1;n<snake.node;n++) { Lcd_Rectangle(snake.x[n],snake.y[n],snake.x[n]+2,snake.y[n]+2,0);//消隐食物,蛇头已到墙壁内,故不消去 } if(snake.life==0)//如果蛇还活着 Lcd_WriteStr( 1,2,"过关"); else //如果蛇死了 Lcd_WriteStr(2,1,"输了"); Lcd_WriteStr(1,2,"游戏结束"); } /******************************** 游戏的具体过程,也是贪吃蛇算法的关键部分 *********************************/ void GamePlay(void) { unsigned char n; InitRandom(TL0); food.yes=1;//表示需要出现新事物,0表示已经存在食物 snake.life=0;//表示蛇还活着 snake.direction=DOWN; snake.x[0]=6;snake.y[0]=6; snake.x[1]=3;snake.y[1]=6; snake.node=2; PrintScore(); PrintSpeed(); while(1) { if(food.yes==1) { while(1) { food.x=Random()*85+3; food.y=Random()*55+3;//获得随机数 while(food.x%3!=0) food.x++; while(food.y%3!=0) food.y++; for(n=0;n<snake.node;n++)//判断产生的食物坐标是否和蛇身重合 { if((food.x==snake.x[n])&&(food.y==snake.y[n])) break; } if(n==snake.node) { food.yes=0; break;//产生有效的食物坐标 } } } if(food.yes==0) { Lcd_Rectangle(food.x,food.y,food.x+2,food.y+2,1); } for(n=snake.node-1;n>0;n--) { snake.x[n]=snake.x[n-1]; snake.y[n]=snake.y[n-1]; } switch(snake.direction) { case DOWN:snake.x[0]+=3;break; case UP:snake.x[0]-=3;break; case RIGHT:snake.y[0]-=3;break; case LEFT:snake.y[0]+=3;break; default:break; } for(n=3;n<snake.node;n++)//从第三节开始判断蛇头是否咬到自己 { if(snake.x[n]==snake.x[0]&&snake.y[n]==snake.y[0]) { GameOver(); snake.life=1; break; } } if(snake.x[0]<3||snake.x[0]>=90||snake.y[0]<3||snake.y[0]>=60)//判蛇头是否撞到墙壁 { GameOver(); snake.life=1; } if(snake.life==1) break;//蛇死,则跳出while(1)循环 if(snake.x[0]==food.x&&snake.y[0]==food.y)//判蛇是否吃到食物 { Lcd_Rectangle(food.x,food.y,food.x+2,food.y+2,0);//消隐食物 snake.x[snake.node]=200; snake.y[snake.node]=200;//产生蛇新的节坐标先放在看不见的位置 snake.node++;//蛇节数加1 food.yes=1;//食物标志置1 if(++Score>=PASSSCORE) { PrintScore(); GameOver(); break; } PrintScore(); } for(n=0;n<snake.node;n++) { Lcd_Rectangle(snake.x[n],snake.y[n],snake.x[n]+2,snake.y[n]+2,1); }//根据蛇的节数画出蛇 delay(Speed*1000); Lcd_Rectangle(snake.x[snake.node-1],snake.y[snake.node-1],snake.x[snake.node-1]+2,snake.y[snake.node-1]+2,0); switch(KeyBuffer) { case FUNC: KeyBuffer=0; if(++Speed>=10) Speed=1; PrintSpeed(); break; case DOWN: KeyBuffer=0; if(snake.direction!=UP) snake.direction=DOWN; break; case UP: KeyBuffer=0; if(snake.direction!=DOWN) snake.direction=UP; break; case RIGHT: KeyBuffer=0; if(snake.direction!=LEFT) snake.direction=RIGHT; break; case LEFT: KeyBuffer=0; if(snake.direction!=RIGHT) snake.direction=LEFT; break; default: break; } }//结束while(1) } void main() { InitCpu();//初始化CPU Lcd_Reset(); //初始化LCD屏 Lcd_Clear(0);//清屏 DrawBoard();//画界面 GamePlay();//玩游戏 GameOver();//游戏结束 while(1);//要想重玩,只能重启 } //主程序就是这些,由于才开始学,好多都看不懂,请教了! |
定义一个暂停按键的标志位,使暂定按键设置为最高优先级,如果暂停键按下中断触发,状态标志位为1,此时只处理暂停键的程序,其他按键均无效,再次按下状态标志位为0;多看看按键中断这块程序你可能做的会容易好多 |
1433049084 发表于 2020-12-10 11:09 那你是如何添加蛇一定方向的处理的?是不是用按键检索方式来做的?是的话,那就不用中断也行。 我估计你的程序处理逻辑是这样的: while(1) { 如果是按键“上”:向上移动并显示蛇身体... 如果是按键“下”:向下移动并显示蛇身体... .... } 那么改成: uchar pause_flag=0 while(1) { 如果是按键“暂停”:pause_flag^=1; if(pause_flag==0) { 如果是按键“上”:向上移动并显示蛇身体... 如果是按键“下”:向下移动并显示蛇身体... .... } } 这样的话,最开始时 pause_flag=0的缘故,蛇的移动依旧被正常执行。 第一次按下暂停键的时候,pause_flag变成了1,蛇的移动处理全都不会被执行了。 再次按暂停键的时候,pause_flag又变回了0,蛇的移动处理又都能被执行了。 pause按键的判定处理一定要加防抖及检查是否送来了的处理,否则,会出现这种情况: 按着暂停键不松开,pause_flag会被执行无数次,松开”暂停“键的时候,pause_flag是0还是1就不确定了 防抖及松开判定,以按键按下时IO口得到的是0为前提条件: if (key_pause==0) { 延时20毫秒 if (key_pause==0) { pause_flag^=1; while(key_pause==0); // 等待按键松开 } } if (pause_flag==0) { 你以前的处理; 。。。 } |
suncat0504 发表于 2020-12-9 23:46 我只会添加一个外部中断,然后while(1),我知道这是不对,但就是不知道怎么暂停然后再按一次继续 |
增加一路外部中断,定义一个全局变量,在所有贪吃蛇运动的执行操作函数中添加对该全局变量的判断。 |