![]() |
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),我知道這是不對,但就是不知道怎么暫停然后再按一次繼續 |
增加一路外部中斷,定義一個全局變量,在所有貪吃蛇運動的執行操作函數中添加對該全局變量的判斷。 |