游戲介紹
游戲規(guī)則
利用FPGA矢空,以640*480的分辨率使用VGA顯示航罗,玩家利用按鍵操作位于屏幕左側(cè)的方塊移動,來躲避從屏幕右側(cè)向左邊移動的留有一定間隙的障礙物妇多。
游戲要求
畫面及操作盡量連續(xù)伤哺,游戲結(jié)束時玩家操作的物體變成紅色,按下重新開始后復(fù)位游戲者祖,隨著時間變長加速以提高難度立莉。
基本上整個游戲就像是以前的飛機小游戲,為了增加可玩性七问,我將游戲設(shè)置為方塊自動降落蜓耻,外部只有一個按鍵,實現(xiàn)方塊的向上移動械巡,去躲避向左移動的擋板刹淌。
設(shè)計分析
模塊設(shè)定
首先游戲要有顯示畫面,所以少不了vga的顯示模塊讥耗;其次是要控制方塊的移動有勾,需要鍵盤的輸入模塊,最后是關(guān)于游戲的邏輯控制古程,這需要一個控制模塊蔼卡。
那這些模塊都需要什么哪些信號呢?接著分析下挣磨。
鍵盤模塊
鍵盤模塊作為整個避障游戲系統(tǒng)的輸入雇逞,它主要是為其他模塊提供信號,對信號的處理并不多茁裙。
輸入 | 功能描述 |
---|---|
clk | 時鐘 |
reset | 復(fù)位 |
up | 使方塊上升 |
輸出 | 功能描述 |
up_key_press | 方塊上升信號 |
down_key_press | 方塊下降信號 |
前面說了塘砸,為了增加可玩性,所以我在系統(tǒng)內(nèi)部設(shè)置了讓方塊自動下落晤锥,所以輸入只有up,但是輸出時會有down_key_press掉蔬。
關(guān)于鍵盤的輸入輸出就是如此,至于模塊如何實現(xiàn)這些功能矾瘾,分析階段不解釋眉踱,在下文中會陸陸續(xù)續(xù)講解。
VGA模塊
vga模塊的功能就是將數(shù)據(jù)顯示霜威,原理在下文我會講解谈喳,弄懂它的時序后,問題不會太大戈泼,一開始可以嘗試先顯示個彩條之類的婿禽,測試下赏僧,找下感覺。
輸入 | 功能描述 |
---|---|
clk | 時鐘 |
reset | 復(fù)位 |
輸出 | 功能描述 |
dat_act | 數(shù)據(jù)有效標(biāo)志位 |
hc | 行掃描計數(shù)器 |
vc | 列掃描計數(shù)器 |
hsync | 行同步信號 |
vsync | 場同步信號 |
控制模塊
控制模塊這個部分是整個游戲的規(guī)則的設(shè)定扭倾,可以說淀零,游戲怎么玩完全由這個模塊決定,根據(jù)游戲的描述和要求膛壹,我們是要控制一個方塊去躲避不斷向左移動的擋板驾中,所以這個擋板和方塊怎么“弄出來”就是關(guān)鍵了。
怎么弄出來呢?
首先要有個概念模聋,我們所看到的VGA圖像都是一個個像素點組成肩民,使用640*480 的顯示模式,這個規(guī)定相當(dāng)于為我們規(guī)定了橫縱坐標(biāo)的定義域链方,在這個二維屏幕上持痰。方塊和遮擋板就可以用數(shù)學(xué)式子“畫”出來,例如邊長為兩個像素點一個方塊就是:0<x<2 ,0<y<2祟蚀。
友情提醒:FPAG中能用正數(shù)就盡量不要用負(fù)數(shù)工窍,數(shù)字在FPGA中是用補碼表示的,負(fù)數(shù)的補碼往往與我們的思維邏輯有點出入前酿,容易導(dǎo)致出錯患雏。
輸入 | 功能描述 |
---|---|
clk | 時鐘 |
reset | 復(fù)位 |
up_key_press | 方塊上升信號 |
down_key_press | 方塊的下降信號 |
hc | 行掃描計數(shù)器 |
vc | 列掃描計數(shù)器 |
dat_act | 數(shù)據(jù)有效標(biāo)志位(用于消隱) |
輸出 | 功能描述 |
disp_RGB | 顯示所需的數(shù)據(jù) |
總體的設(shè)計圖
方案設(shè)計
鍵盤模塊
鍵盤模塊的要點在于消抖,和控制方塊移動罢维。
消抖
無論是什么器件淹仑,鍵盤的消抖都是老套路,分為硬件消抖和軟件消抖言津,硬件消抖如使用RS觸發(fā)器實現(xiàn)或者是加電容實現(xiàn)攻人,一般是制作板子的時候考慮加上去的取试,平時我們使用現(xiàn)成的板子悬槽,大多數(shù)都是使用軟件消抖。
鍵盤產(chǎn)生抖動是機械特性瞬浓,在我們按下按鍵是接觸點的電壓波形大致如下圖:
從圖可以看出初婆,按下時會有一段上下波動的波形,松開時也有一段猿棉。
軟件消抖常用的方法是延時磅叛,作用就是避開這一段“抖動”的波形,達到消抖的目的萨赁。
if(counter <= T) //按的時間不夠長
begin
counter = counter + 1'b1;
up_key_press <= 0;
end
else //按下足夠久了弊琴,認(rèn)為是真的按下
begin
counter <= 0;
up_key_press <= 1;
end
硬件消抖這里也稍微拓展下。
RS觸發(fā)器實現(xiàn)
圖中兩個“與非”門構(gòu)成一個RS觸發(fā)器杖爽。當(dāng)按鍵未按下時敲董,輸出為0;當(dāng)鍵按下時紫皇,輸出為1。此時即使用按鍵的機械性能腋寨,使按鍵因彈性抖動而產(chǎn)生瞬時斷開(抖動跳開B)聪铺,只要按鍵不返回原始狀態(tài)A,雙穩(wěn)態(tài)電路的狀態(tài)不改變萄窜,輸出保持為0铃剔,不會產(chǎn)生抖動的波形。也就是說查刻,即使B點的電壓波形是抖動的键兜,但經(jīng)雙穩(wěn)態(tài)電路之后,其輸出為正規(guī)的矩形波赖阻。這一點通過分析RS觸發(fā)器的工作過程很容易得到驗證蝶押。
至于其他的硬件消抖電路,如:用電容構(gòu)成的積分電路實現(xiàn)火欧,采用D觸發(fā)器實現(xiàn)棋电,這里不再拓展,如有興趣苇侵,可自行查閱資料赶盔。
控制移動
這個功能的實現(xiàn),應(yīng)該說不難榆浓。知道要改變哪個參數(shù)能使它移動于未,改變它就可以實現(xiàn)了,在這個游戲系統(tǒng)中陡鹃,控制方塊上下移動是改變 move_y 這個參數(shù)烘浦,左右移動是改變move_x,不過在后面測試游戲時萍鲸,我覺得左右移動沒有必要加上去闷叉,就把它去了,具體的操作看代碼吧。
VGA模塊
實現(xiàn)這個游戲脊阴,我認(rèn)為最重要的知識就是VGA的顯示原理了握侧。數(shù)據(jù)怎么顯示在屏幕的?640和480指的又是什么嘿期?我們先看它的原理品擎。
VGA原理
VGA從掃描方式上分行掃描和場掃描兩種,掃描就是一個電子槍(CRT)备徐,啾啾啾的掃萄传,水平方向叫行掃描,垂直方向叫場掃描蜜猾,這個電子槍它又可發(fā)出三種顏色光秀菱,分別是R(紅色)西设,G(綠色),B(藍色)答朋,光的三原色都有贷揽,原則上三原色按照比例不同搭配,那你想要什么顏色就可以給你什么顏色梦碗,但實際上呢禽绪,VGA中紅,綠洪规,藍的輸入線分別是3,3,2根印屁;也就是說紅色有2^3=8種,同理斩例,綠色八種雄人,藍色四種,相互搭配念赶,便有8 X 8 X 4=256種搭配础钠,也就是VGA能顯示256種顏色。
掃描過程是怎樣的叉谜?
以行掃描為例:
從圖可以看出旗吁,電子槍從左往右掃射一行回頭再到下一行,直至最后完成一幀畫面停局,又重頭開始很钓。那什么時候掉頭,什么時候算是完成一幀畫面董栽,這就有個區(qū)域了码倦,區(qū)域怎么定,是由顯示模式?jīng)Q定的锭碳,看下圖袁稽。
我們可以看到有多種顯示模式,不同的顯示模式所需的時鐘頻率可是不一樣的工禾,如果細(xì)心查看运提,就會注意到蝗柔,行時序的c區(qū)和列時序的q區(qū)恰好是640和480這兩個熟悉的數(shù)字闻葵,其實這就是顯示時序段的范圍。
VGA中定義行時序和場時序都需要同步脈沖(Sync a)癣丧、顯示后沿(Back porch b)槽畔、顯示時序段(Display interval c)和顯示前沿(Front porch d)四部分。只有在顯示時序段胁编,也就是C區(qū)才可以信號顯示出來厢钧,其他區(qū)域鳞尔,你就算給VGA信號,你也看不到早直。
行時序
場時序
那么我們怎么知道寥假,電子槍(CRT)有沒有掃描到顯示時序段呢?方法是加入行同步計數(shù)器和列同步計數(shù)器用霞扬,反正時序是固定的糕韧,行同步計數(shù)器是掃一下計數(shù)器加一,列計數(shù)器是一行掃完計數(shù)器加一喻圃,兩者都掃到C區(qū)了萤彩,也就是計數(shù)器都達到一定數(shù)值(a區(qū)長度+B區(qū)長度),表明屏幕可以顯示信號斧拍,我就給信號雀扶,要黑色,rgb全給0肆汹,要白色愚墓,全給1,反正就是給信號昂勉,這和在坐標(biāo)軸上畫圖的感覺是一樣一樣的转绷。
控制模塊
控制模塊就像這個游戲系統(tǒng)的控制中心一般,制定了關(guān)于這個游戲的一切規(guī)則硼啤。
主要有那么幾個要點:
- 將鍵盤的長脈沖變?yōu)橐粋€個沖擊信號议经,不然以FPGA本身的頻率,按一下谴返,移動得太快煞肾,方塊就“上天” 了,障礙物移動也會快到你看不到。
- “畫”方塊和障礙物(擋板)嗓袱,并設(shè)定參數(shù)讓給它們可以移動籍救。
- 由于擋板的垂直方向出現(xiàn)的位置要有隨機性,所以需要產(chǎn)生隨機數(shù)渠抹。
- 設(shè)置游戲失敗的情況赖晶,方塊與擋板“撞上”這個時機的設(shè)置必須是程序完成映凳。
長脈沖變多個短脈沖
這不難想也不難實現(xiàn),就是計數(shù)器加到一定程度變?yōu)闃?biāo)志位變?yōu)?,然后計數(shù)器清零颜屠,標(biāo)志位也變?yōu)?桶略,一定時間內(nèi)要短脈沖多點跪呈,計數(shù)器的計數(shù)值就小點嚷量,反之,大一點。
//// 板塊移動速度控制 ////
reg move;
reg [32:0]counter;
reg [30:0]T_move;
always@(posedge clk,negedge reset)
begin
if(!reset)
begin
T_move = 30'd10_000_00;
counter <= 0;
move <=0;
end
else
begin
if(counter >= T_move)
begin
move = 1;
if(T_move == 100_000)
T_move <=T_move;
else
T_move = T_move-10;
counter = 0;
end
else
begin
move = 0;
if(!stop)
counter= counter + 1;
else
counter = 0;
end
end
end
“畫”方塊和擋板
關(guān)于如何“畫”荆几,前面舉過畫方塊的例子吓妆,就是把行列計數(shù)器當(dāng)做 x,y。x給個區(qū)域吨铸,y給個區(qū)域行拢,再給個顏色,就畫出來了诞吱。至于移動呢剂陡,移動就代表著位置是個變量,設(shè)定一個x,y都是變量的點狐胎,然后以這個點為中心畫出你要的方塊或者擋板鸭栖,改變這個點的x,y便是將它移動。
以擋板從右向左移動為例:
產(chǎn)生隨機數(shù)
產(chǎn)生偽隨機數(shù)的方法最常見的是利用一種線性反饋移位寄存器(LFSR),它是由n個D觸發(fā)器和若干個異或門組成的握巢,如下圖:
[圖片上傳失敗...(image-fadcea-1636785474160)]
實際上這個有規(guī)律可循的晕鹊,只不過D觸發(fā)器一多,顯得很亂暴浦,很像隨機產(chǎn)生的樣子溅话,但確實不是真正意義上的隨機數(shù),是個偽隨機數(shù)歌焦,但在這里使用足夠了的飞几。
但這種方法也有bug,就是高位它不容易變化的時候,擋板垂直方向就不夠分散独撇,舉個例子屑墨,以8個D觸發(fā)器組成的為例,數(shù)字范圍從0~1111_1111,如果高位變化不大纷铣,如從1110_0000變成1110_0101,高位不怎么變化的話卵史,整個數(shù)字大小實際上就是改變一點點,圖像表現(xiàn)為前后兩個擋板垂直位置上相差幾個像素點搜立,這就顯得過于集中以躯,而且這種辦法無法生成 0 這個數(shù)字。
為了將擋板"離散一點"啄踊,我就將豎直方向的長減去擋板的長度后得到的空隙分段化忧设,分為8段,這樣擋板之間的距離要么相等颠通,不然都會有一段距離址晕,顯得離散些。怎么實現(xiàn)呢蒜哀?
每個D觸發(fā)器都存有一個數(shù)字斩箫,我從中隨機抽取三個數(shù)字,做一個case語句的選擇撵儿,8段8種情況選擇乘客。這樣隨機性增加,擋板也更離散淀歇。
/////// 隨機數(shù) //////////
reg [7:0] rand_num;
parameter seed = 8'b1111_1111;
always@(posedge clk or negedge reset)
begin
if(!reset)
rand_num <= seed;
else
begin
rand_num[0] <= rand_num[1] ;
rand_num[1] <= rand_num[2] + rand_num[7];
rand_num[2] <= rand_num[3] + rand_num[7];
rand_num[3] <= rand_num[4] ;
rand_num[4] <= rand_num[5] + rand_num[7];
rand_num[5] <= rand_num[6] + rand_num[7];
rand_num[6] <= rand_num[7] ;
rand_num[7] <= rand_num[0] + rand_num[7];
end
end
wire [2:0]choose;
reg [8:0]type;
assign choose = {rand_num[3],rand_num[6],rand_num[2]};
always@(posedge clk )
begin
case(choose)
0:type = 0;
1:type = 40;
2:type = 80;
3:type = 120;
4:type = 160;
5:type = 200;
6:type = 240;
7:type = 280;
endcase
end
////////////////////////////////////////////////////////
游戲失敗設(shè)置
游戲失敗是撞上了易核,那 撞上 在數(shù)學(xué)上表示是什么呢?
答案是方塊和擋板的坐標(biāo)有交叉浪默。
方塊和擋板之間都有坐標(biāo)的區(qū)域牡直,只要找到它們會交叉的情況,就說明這個時候是撞上了纳决。原理就是如此碰逸,具體的可以自己動筆算下。
友情提醒:加減時注意盡量不要出現(xiàn)負(fù)數(shù)的情況阔加,因為 數(shù)字用補碼表示的原因饵史,在FPGA中,直接比較 胜榔,-1=ffff_ffff 可是大于0的胳喷。
wire die1,die2,die3,die4;
//游戲失敗定義,方塊與擋板"碰撞"
//失敗情況討論夭织,共設(shè)置四塊擋板吭露,四種情況
assign die1=((rand<move_y + border)&&(move_y < rand+long)&&(push < move_x+border) && (move_x < push + ban ));
assign die2=((rand1<move_y + border)&&(move_y < rand1+long)&&(push1 < move_x+border) && (move_x < push1 + ban ));
assign die3=((rand2<move_y + border)&&(move_y < rand2+long)&&(push2 < move_x+border) && (move_x < push2 + ban ));
assign die4=((rand3<move_y + border)&&(move_y < rand3+long)&&(push3 < move_x+border) && (move_x < push3 + ban ));
wire false;
assign false = die1||die2||die3||die4;
代碼展示
鍵盤模塊
module key(clk,reset,up,up_key_press,down_key_press);
input clk;
input reset;
input up;
output reg up_key_press;
output reg down_key_press;
parameter T = 30'd10_000_00; //控制方塊移動速度
////////// up 按鍵 /////////////
reg [30:0] counter;
reg [30:0] counter2;
always@(posedge clk,negedge reset )
begin
if(!reset)
begin
counter <= 0;
counter2 <= 0;
up_key_press <= 0;
down_key_press <= 0;
end
else
begin
if(up)
begin
if(counter <= T)
begin
counter = counter + 1'b1;
up_key_press <= 0;
end
else
begin
counter <= 0;
up_key_press <= 1;
end
end
else //下降按鈕
begin
if(counter2 <= T)
begin
counter2 = counter2 + 1'b1;
down_key_press <= 0;
end
else
begin
counter2 <= 0;
down_key_press <= 1;
end
end
end
end
endmodule
VGA模塊
module vga( clk,reset,hsync, vsync,hc,vc,dat_act);
input clk; //系統(tǒng)輸入時鐘 100MHz
input reset;
output hsync; //VGA 行同步信號
output vsync; //VGA 場同步信號
output dat_act;
output [9:0]hc ,vc; //轉(zhuǎn)成640*480的模式
reg [9:0] hcount; //VGA 行掃描計數(shù)器
reg [9:0] vcount; //VGA 場掃描計數(shù)器
reg flag;
wire hcount_ov;
wire vcount_ov;
wire hsync;
wire vsync;
reg vga_clk=0;
reg cnt_clk=0; //分頻計數(shù)
//VGA 行、場掃描時序參數(shù)表
parameter hsync_end = 10'd95,
hdat_begin = 10'd143,
hdat_end = 10'd783,
hpixel_end = 10'd799,
vsync_end = 10'd1,
vdat_begin = 10'd34,
vdat_end = 10'd514,
vline_end = 10'd524;
//分頻
always @(posedge clk)
begin
if(cnt_clk == 1)
begin
vga_clk <= ~vga_clk;
cnt_clk <= 0;
end
else
cnt_clk <= cnt_clk +1;
end
//************************VGA 驅(qū)動部分*******************************//行掃描
always @(posedge vga_clk)
begin
if (hcount_ov)
hcount <= 10'd0;
else
hcount <= hcount + 10'd1;
end
assign hcount_ov = (hcount == hpixel_end);
//場掃描
always @(posedge vga_clk)
begin
if (hcount_ov)
begin
if (vcount_ov)
vcount <= 10'd0;
else
vcount <= vcount + 10'd1;
end
end
assign vcount_ov = (vcount == vline_end);
//數(shù)據(jù)尊惰、同步信號輸
assign dat_act = ((hcount >= hdat_begin) && (hcount < hdat_end))&& ((vcount >= vdat_begin) && (vcount < vdat_end));
assign hsync = (hcount > hsync_end);
assign vsync = (vcount > vsync_end);
//計數(shù)器轉(zhuǎn)成640 x 480的樣式讲竿,方便開發(fā)
assign hc = hcount - hdat_begin;
assign vc = vcount - vdat_begin;
endmodule
控制模塊
module control( clk,reset, disp_RGB,hc,vc,dat_act,up_key_press,down_key_press );
input clk; //系統(tǒng)輸入時鐘 100MHz
input reset;
input dat_act;
input [9:0]hc,vc;
input up_key_press;
input down_key_press;
output [2:0]disp_RGB; //VGA 數(shù)據(jù)輸出
reg [2:0]data;
reg vga_clk=0;
reg cnt_clk=0; //分頻計數(shù)
//分頻
always @(posedge clk)
begin
if(cnt_clk == 1)
begin
vga_clk <= ~vga_clk;
cnt_clk <= 0;
end
else
cnt_clk <= cnt_clk +1;
end
//定義正方形小塊的邊長
parameter border = 40;
//定義擋板的寬度
parameter ban = 20;
//定義擋板的長度
parameter long = 200;
//定義擋板的間隔
parameter magin = 160;
//VGA掃描,畫出擋板和方塊弄屡,并設(shè)置擋板移動的移動變量push
reg [10:0] push,push1,push2,push3;
reg stop;//用于停止游戲
//小方塊移動數(shù)據(jù)存儲器
parameter move_x = 50; //方塊的初始位置
reg [9:0]move_y;
/////// 隨機數(shù) //////////
reg [7:0] rand_num;
parameter seed = 8'b1111_1111;
always@(posedge clk or negedge reset)
begin
if(!reset)
rand_num <= seed;
else
begin
rand_num[0] <= rand_num[1] ;
rand_num[1] <= rand_num[2] + rand_num[7];
rand_num[2] <= rand_num[3] + rand_num[7];
rand_num[3] <= rand_num[4] ;
rand_num[4] <= rand_num[5] + rand_num[7];
rand_num[5] <= rand_num[6] + rand_num[7];
rand_num[6] <= rand_num[7] ;
rand_num[7] <= rand_num[0] + rand_num[7];
end
end
wire [2:0]choose;
reg [8:0]type;
assign choose = {rand_num[3],rand_num[6],rand_num[2]};
always@(posedge clk )
begin
case(choose)
0:type = 0;
1:type = 40;
2:type = 80;
3:type = 120;
4:type = 160;
5:type = 200;
6:type = 240;
7:type = 280;
default: type = 280;
endcase
end
////////////////////////////////////////////////////////
//// 板塊移動速度控制 ////
reg move;
reg [32:0]counter;
reg [30:0]T_move;
always@(posedge clk,negedge reset)
begin
if(!reset)
begin
T_move = 30'd10_000_00;
counter <= 0;
move <=0;
end
else
begin
if(counter >= T_move)
begin
move = 1;
if(T_move == 100_000)
T_move <=T_move;
else
T_move = T_move-10;
counter = 0;
end
else
begin
move = 0;
if(!stop)
counter= counter + 1;
else
counter = 0;
end
end
end
reg [8:0]rand,rand1,rand2,rand3;
always@(posedge clk or negedge reset)
begin
if (!reset)
begin
push<=640; //初始位置設(shè)定
push1 <= 640+ magin;
push2 <= 640 + magin + magin;
push3 <= 640 + magin + magin + magin;
end
else if (move)
begin
if(push == 0)
begin
push <= 640;
rand <=type; //第一塊板子的位置設(shè)定
end
else
begin
push <= push-1'b1;
end
if(push1 == 0)
begin
push1 <= 640;
rand1 <=type; //第二塊板子的位置設(shè)定
end
else
begin
push1 <= push1-1'b1;
end
if(push2 == 0)
begin
push2 <= 640;
rand2 <=type; //第三塊板子的位置設(shè)定
end
else
begin
push2<= push2-1'b1;
end
if(push3 == 0)
begin
push3 <= 640;
rand3 <=type;
//第四塊板子的位置設(shè)定
end
else
begin
push3 <= push3-1'b1;
end
end
else
begin
push <= push;
push1 <= push1;
push2 <= push2;
push3 <= push3;
end
end
wire die1,die2,die3,die4;
//游戲失敗定義戴卜,方塊與擋板"碰撞"
//失敗情況討論,共設(shè)置四塊擋板琢岩,四種情況
assign die1=((rand<move_y + border)&&(move_y < rand+long)&&(push < move_x+border) && (move_x < push + ban ));
assign die2=((rand1<move_y + border)&&(move_y < rand1+long)&&(push1 < move_x+border) && (move_x < push1 + ban ));
assign die3=((rand2<move_y + border)&&(move_y < rand2+long)&&(push2 < move_x+border) && (move_x < push2 + ban ));
assign die4=((rand3<move_y + border)&&(move_y < rand3+long)&&(push3 < move_x+border) && (move_x < push3 + ban ));
wire false;
assign false = die1||die2||die3||die4;
//描述運動投剥,“畫圖”
always@(posedge vga_clk,negedge reset)
begin
if(!reset)
begin
data <= 0;
stop <= 0;
end
else
begin
if (hc>move_x &&(hc<(move_x+border)&&(vc>move_y)&&(vc<move_y+border))) //小方塊
begin
if(!false)
begin
data <= 3'h3; //黃色
stop <= 0;
end
else
begin
data <= 3'h1; //紅色
stop <=1;
end
end
else
if ((hc>push) && (hc<=push+ban) && (vc>=rand) && (vc<=rand+long))
begin
data <= 3'h2; //第一根橫條
end
else if ((hc>push1) && (hc<=push1+ban) && (vc>=rand1) && (vc<=rand1+long))
begin
data <= 3'h2; //第二根橫條
end
else if ((hc>push2) && (hc<=push2+ban) && (vc>=rand2) && (vc<=rand2+long))
begin
data <= 3'h2; //第三根橫條
end
else if ((hc>push3) && (hc<=push3+ban) && (vc>=rand3) && (vc<=rand3+long))
begin
data <= 3'h2; //第四根橫條
end else
data <= 0;
end
end
/////// 方塊移動控制 ////////////
always@(posedge clk or negedge reset)
begin
if (!reset)
begin
move_y <= 240;
end
else if (up_key_press)
begin
if(move_y == 0)
begin
move_y <= move_y;
end
else
begin
move_y <= move_y-1'b1;
end
end
else if (down_key_press)
begin
if(move_y>440)
begin
move_y <= move_y;
end
else
begin
move_y <= move_y+1'b1;
end
end
end
// 信號輸出
assign disp_RGB = (dat_act) ? data : 3'h00;
endmodule
TOP模塊
module top(clk,reset,up,hsync,vsync,disp_RGB);
input clk;
input reset;
input up;
output hsync; //VGA 行同步信號
output vsync; //VGA 場同步信號
output [2:0]disp_RGB; //VGA 數(shù)據(jù)輸出
wire dat_act;
wire up_key_press;
wire down_key_press;
wire [9:0]hc,vc;
key U1(clk,reset,up,up_key_press,down_key_press);
control U2( clk,reset, disp_RGB,hc,vc,dat_act,up_key_press,down_key_press );
vga U3( clk,reset,hsync, vsync,hc,vc,dat_act);
endmodule
寫在后面的話
關(guān)于這個小游戲的講解就到這里,有任何疑問可以在評論處指出或者聯(lián)系我担孔,我會及時更新江锨,文章若有錯誤,懇請讀者在評論區(qū)指出斧正糕篇,我會修改啄育。
歡迎大家在評論區(qū)與我交流,學(xué)習(xí)拌消。