基于FPGA的五子棋游戲設(shè)計(jì)
本文基于FPGA設(shè)計(jì)五子棋游戲,使用按鍵輸入,使用VGA接口輸出。五子棋的棋具與圍棋相同,棋子分為黑白兩色,棋盤為10×10,棋子放置于棋盤線交叉點(diǎn)上。兩人對(duì)局,各執(zhí)一色,輪流下一子,先將橫、豎或斜線的5個(gè)或5個(gè)以上同色棋子連成不間斷的一排者為勝。
VGA技術(shù)介紹:
VGA 接口就是顯卡上輸出模擬信號(hào)的接口,也叫 D-Sub 接口。VGA 接口是一種 D
型口,上面共有 15 針空,分成三排,每排五個(gè)。VGA 接口是目前中低端電腦配置上的主流口。
VGA顯示中,F(xiàn)PGA需要產(chǎn)生5個(gè)信號(hào):R、G、B三基色信號(hào),行同步信號(hào)HS,場(chǎng)同步信號(hào)VS。以上接口的5個(gè)孔對(duì)應(yīng)著我們FPGA中產(chǎn)生的5個(gè)重要的信號(hào),其中R、G、B是數(shù)據(jù)信號(hào);HS、VS是控制信號(hào)。下面是VGA 顯示模塊與CRT 顯示器的控制框圖:
像素是產(chǎn)生各種顏色的基本單元。根據(jù)物理學(xué)中的混色原理,三色發(fā)光的亮度比例適當(dāng),可呈現(xiàn)白色。適當(dāng)?shù)恼{(diào)整發(fā)光比例可以出現(xiàn)不同的顏色。三基色混色原理示意圖如下圖所示:
對(duì)于顯示器來(lái)說,RGB三個(gè)信號(hào)其實(shí)是模擬信號(hào),其電平的高低,可以表示顏色的深淺。利用這個(gè)原理,我們就可以產(chǎn)生豐富的色彩。為了控制電壓的高低,我們就必須用到DA芯片。例如下圖中,F(xiàn)PGA產(chǎn)生RGB三種信號(hào),這時(shí)RGB都是多位的數(shù)字信號(hào)。DA芯片根據(jù)數(shù)字信號(hào)的值,產(chǎn)生不同電壓的模擬信號(hào)rgb。
DE1-SoC板上有一個(gè)15針D-SUB連接器,用于VGA輸出。VGA同步信號(hào)直接從Cyclone V SoC FPGA和Analog器件ADV7123三路10位高速視頻DAC(僅使用較高的8位)轉(zhuǎn)換從數(shù)字到模擬的信號(hào)代表三種基本顏色(紅色,綠色和藍(lán)色)。 它可以支持高達(dá)SXGA標(biāo)準(zhǔn)(1280 * 1024),信號(hào)以100MHz傳輸。 下圖顯示FPGA和VGA之間連接的信號(hào)。
顯示器采用光柵掃描方式,即轟擊熒光屏的電子束在 CRT 屏幕上從左到右(受水平同步信號(hào) HSYNC 控制)、從上到下(受垂直同步信號(hào) VSYNC 控制)做有規(guī)律的移動(dòng)。電子束采用光柵掃描方式,從屏幕左上角一點(diǎn)開始,向右逐點(diǎn)進(jìn)行掃描,形成一條水平線;到達(dá)最右端后,又回到下一條水平線的左端,重復(fù)上面的過程;當(dāng)電子束完成右下角一點(diǎn)的掃描后,形成一幀。此后,電子束又回到左上方起點(diǎn),開始下一幀的掃描。這種方法也就是常說的逐行掃描顯示。
完成一行掃描的時(shí)間稱為水平掃描時(shí)間,其倒數(shù)稱為行頻率;完成一幀(整屏)掃描的時(shí)間稱為垂直掃描時(shí)間,其倒數(shù)稱為場(chǎng)頻率,即刷新一屏的頻率,常見的有60Hz,75Hz等等。標(biāo)準(zhǔn)的VGA顯示的場(chǎng)頻60Hz。
行時(shí)序和場(chǎng)時(shí)序都需要同步脈沖(Sync a)、顯示后沿(Back porch b)、顯示時(shí)序段(Display interval c)和顯示前沿(Front porch d)四部分。VGA工業(yè)標(biāo)準(zhǔn)顯示模式要求:行同步,場(chǎng)同步都為負(fù)極性,即同步脈沖要求是負(fù)脈沖。
VGA支持的規(guī)格
我們以第一個(gè)分辨率640/480來(lái)分析,其刷新速率是60Hz,每幅圖像有525行,每行有800個(gè)值。也就是說完成一幅圖像約是1s/60=16.6ms,完成一行約為16.6ms/525=31.75us,完成一個(gè)像素傳送約來(lái)31.75us /800=40ns。因此為了方便設(shè)計(jì),接口的時(shí)候設(shè)為25MHz最方便,每個(gè)時(shí)鐘送一個(gè)數(shù)據(jù)。
VGA時(shí)序設(shè)計(jì)
根據(jù)上節(jié)的介紹,VGA時(shí)序要求如下
VGA時(shí)序代碼設(shè)計(jì)
/**********VGA驅(qū)動(dòng)**********/
always@(posedge vga_clk or negedge rst_n)begin
if(!rst_n)begin
hcount <= 10'd0;
end
else if(hcount == 10'd799)begin
hcount <= 10'd0;
end
else begin
hcount <= hcount+1'b1;
end
end
always@(posedge vga_clk or negedge rst_n)begin
if(!rst_n) begin
vcount <= 10'd0;
end
else if(hcount == 10'd799)begin
if(vcount==524)
vcount <= 0;
else
vcount <= vcount+1;
end
end
always@(posedge vga_clk or negedge rst_n)begin
if(!rst_n)begin
hsync <= 1;
end
else if(hcount==10'd95)begin
hsync <= 1'b1;
end
else if(hcount==10'd799)begin
hsync <= 1'b0;
end
end
always@(posedge vga_clk or negedge rst_n)begin
if(!rst_n)begin
vsync <= 1'b1;
end
else if(vcount<10'd2)begin
vsync <= 1'b0;
end
else begin
vsync <= 1'b1;
end
end
VGA時(shí)序仿真結(jié)果
五子棋設(shè)計(jì)
為了節(jié)約FPGA資源,本文設(shè)計(jì)并編寫了10x10 五子棋的專用算法。棋子半徑大小20個(gè)像素。光標(biāo)當(dāng)前的棋子所在位置使用青色表示,因?yàn)槠聊槐尘吧呛谏?,棋盤背景色是白色,所以黑白棋子使用紫色和黃色代替。
棋盤設(shè)計(jì):
設(shè)10x10數(shù)組reg [9:0] PLAYED [9:0]; 數(shù)組中每一位代表棋盤的一個(gè)交叉點(diǎn)。數(shù)組的某位為0時(shí),代表無(wú)子狀態(tài); 為l時(shí),代表有棋子;
設(shè)10x10數(shù)組reg [9:0] CHESS_COLOR [9:0];表示棋子的顏色。
產(chǎn)生棋盤格的代碼如下:
//產(chǎn)生豎長(zhǎng)條
always@(posedge vga_clk )
begin
if(hcount<=200||hcount>=640)begin
v_flag <= 1'b1 ;
v_dat <= 12'h000;//hei
end else if(hcount ==240||hcount ==280||hcount ==320||hcount ==360||hcount ==400||hcount ==440||hcount ==480||hcount ==520||hcount==560||hcount==600) begin
v_flag <= 1'b1 ;
v_dat <= 12'h000;//hei
end else begin
v_flag <= 1'b0 ;
v_dat <= 12'hfff;//bai
end
end
//產(chǎn)生橫長(zhǎng)條
always@(posedge vga_clk )
begin
if(vcount<=50||vcount>=490) begin
h_flag <= 1'b1 ;
h_dat <= 12'h000;//hei
end else if(vcount ==90||vcount ==130||vcount ==170||vcount==210 ||vcount==250||vcount ==290||vcount ==330||vcount==370 ||vcount==410||vcount==450) begin
h_flag <= 1'b1 ;
h_dat <= 12'h000;
end else begin
h_flag <= 1'b0 ;
h_dat <= 12'hfff;
end
end
2)當(dāng)前棋子光標(biāo)提示設(shè)計(jì):
當(dāng)前棋子位置提示顯示數(shù)據(jù)為x_data,顯示青色。V_dat和H_dat是橫豎線的,顯示為黑色,y_dat為棋子顏色。
/
/顯示模塊
always@(posedge vga_clk)
begin
if(dat_act)begin
if(over==0 && over2==0 && debug_flag==0 ) begin
if(curchess_flag)
disp_rgb <=x_dat; //
else if(v_flag)
disp_rgb <=v_dat; //
else if(h_flag)
disp_rgb <=h_dat; //
else if(chess_flag)
disp_rgb <=y_dat; //
else
disp_rgb <=12'hfff; //
end else
disp_rgb <=z_dat; //游戲結(jié)束則顯示另外畫面
end
else begin
disp_rgb = 12'h000; //VGA區(qū)域之外
end
end
3)按鈕上、下、左、右、落子:
always@(posedge sysclk or negedge rst_n)begin
if(!rst_n)begin
PLAYED[0]<=10'd0;
PLAYED[1]<=10'd0;
PLAYED[2]<=10'd0;
PLAYED[3]<=10'd0;
PLAYED[4]<=10'd0;
PLAYED[5]<=10'd0;
PLAYED[6]<=10'd0;
PLAYED[7]<=10'd0;
PLAYED[8]<=10'd0;
PLAYED[9]<=10'd0;
CHESS_COLOR[0]<=10'd0;
CHESS_COLOR[1]<=10'd0;
CHESS_COLOR[2]<=10'd0;
CHESS_COLOR[3]<=10'd0;
CHESS_COLOR[4]<=10'd0;
CHESS_COLOR[5]<=10'd0;
CHESS_COLOR[6]<=10'd0;
CHESS_COLOR[7]<=10'd0;
CHESS_COLOR[8]<=10'd0;
CHESS_COLOR[9]<=10'd0;
end
else
begin
case(key_en_tem)
5'b00100:begin
if (ball_x_pos== 10'd240)
ball_x_pos<=10'd600;
else
ball_x_pos<=ball_x_pos-10'd40;
end
5'b01000:begin
if (ball_x_pos==10'd600)
ball_x_pos<=10'd240;
else
ball_x_pos<= ball_x_pos+10'd40;
end
5'b00001: begin
if (ball_y_pos== 10'd90)
ball_y_pos<=10'd450;
else
ball_y_pos<=ball_y_pos-10'd40;
end
5'b00010: begin
if(ball_y_pos== 10'd450)
ball_y_pos<=10'd90;
else
ball_y_pos<=ball_y_pos+10'd40;
end
5'b10000: begin
if(PLAYED[current_y][current_x]==0)
begin
PLAYED[current_y][current_x]<=1;
CHESS_COLOR[current_y][current_x]<=change_color;
end
end
(4)判勝:
橫豎正斜反斜四個(gè)方向出現(xiàn)連續(xù)落子,且顏色一樣,輸出勝利提示。
```c
//輸贏狀態(tài)判斷1
always@(posedge vga_clk )
if(!rst_n)begin
over<=0;
end
else
begin
if ( (scan_x<=5&&CHESS_COLOR[scan_y][scan_x]==1&&CHESS_COLOR[scan_y][scan_x+1]==1&&CHESS_COLOR[scan_y][scan_x+2]==1&&CHESS_COLOR[scan_y][scan_x+3]==1&&CHESS_COLOR[scan_y][scan_x+4]==1)//heng
&& (PLAYED[scan_y][scan_x]==1&&PLAYED[scan_y][scan_x+1]==1&&PLAYED[scan_y][scan_x+2]==1&&PLAYED[scan_y][scan_x+3]==1&&PLAYED[scan_y][scan_x+4]==1) )
begin
over<=1;
end
else if( (scan_y<=5&&CHESS_COLOR[scan_y][scan_x]==1&&CHESS_COLOR[scan_y+1][scan_x]==1&&CHESS_COLOR[scan_y+2][scan_x]==1&&CHESS_COLOR[scan_y+3][scan_x]==1&&CHESS_COLOR[scan_y+4][scan_x]==1)//shu
&& (PLAYED[scan_y][scan_x]==1&&PLAYED[scan_y+1][scan_x]==1&&PLAYED[scan_y+2][scan_x]==1&&PLAYED[scan_y+3][scan_x]==1&&PLAYED[scan_y+4][scan_x]==1) )
begin
over<=1;
end
else if( (scan_x<=5&&scan_y<=5&&CHESS_COLOR[scan_y][scan_x]==1&&CHESS_COLOR[scan_y+1][scan_x+1]==1&&CHESS_COLOR[scan_y+2][scan_x+2]==1&&CHESS_COLOR[scan_y+3][scan_x+3]==1&&CHESS_COLOR[scan_y+4][scan_x+4]==1)//chenggong
&& (PLAYED[scan_y][scan_x]==1&&PLAYED[scan_y+1][scan_x+1]==1&&PLAYED[scan_y+2][scan_x+2]==1&&PLAYED[scan_y+3][scan_x+3]==1&&PLAYED[scan_y+4][scan_x+4]==1) )
over<=1;
else if ( (scan_y<=5&&scan_x>=4&&CHESS_COLOR[scan_y][scan_x]==1&&CHESS_COLOR[scan_y+1][scan_x-1]==1&&CHESS_COLOR[scan_y+2][scan_x-2]==1&&CHESS_COLOR[scan_y+3][scan_x-3]==1&&CHESS_COLOR[scan_y+4][scan_x-4]==1)
&& (PLAYED[scan_y][scan_x]==1&&PLAYED[scan_y+1][scan_x-1]==1&&PLAYED[scan_y+2][scan_x-2]==1&&PLAYED[scan_y+3][scan_x-3]==1&&PLAYED[scan_y+4][scan_x-4]==1) )
over<=1;
end
```c
//輸贏狀態(tài)判斷2 yellow
always@(posedge vga_clk )
if(!rst_n)begin
over2<=0;
debug_flag<=0;
end
else
begin
if( (scan_x<=5&&CHESS_COLOR[scan_y][scan_x]==0&&CHESS_COLOR[scan_y][scan_x+1]==0&&CHESS_COLOR[scan_y][scan_x+2]==0&&CHESS_COLOR[scan_y][scan_x+3]==0&&CHESS_COLOR[scan_y][scan_x+4]==0)//heng
&& (PLAYED[scan_y][scan_x]==1&&PLAYED[scan_y][scan_x+1]==1&&PLAYED[scan_y][scan_x+2]==1&&PLAYED[scan_y][scan_x+3]==1&&PLAYED[scan_y][scan_x+4]==1) )
over2<=1;
else if( (scan_y<=5&&CHESS_COLOR[scan_y][scan_x]==0&&CHESS_COLOR[scan_y+1][scan_x]==0&&CHESS_COLOR[scan_y+2][scan_x]==0&&CHESS_COLOR[scan_y+3][scan_x]==0&&CHESS_COLOR[scan_y+4][scan_x]==0)//shu
&& (PLAYED[scan_y][scan_x]==1&&PLAYED[scan_y+1][scan_x]==1&&PLAYED[scan_y+2][scan_x]==1&&PLAYED[scan_y+3][scan_x]==1&&PLAYED[scan_y+4][scan_x]==1) )
begin
over2<=1;
debug_flag<=1;
end
else if( (scan_x<=5&&scan_y<=5&&CHESS_COLOR[scan_y][scan_x]==0&&CHESS_COLOR[scan_y+1][scan_x+1]==0&&CHESS_COLOR[scan_y+2][scan_x+2]==0&&CHESS_COLOR[scan_y+3][scan_x+3]==0&&CHESS_COLOR[scan_y+4][scan_x+4]==0)//chenggong
&& (PLAYED[scan_y][scan_x]==1&&PLAYED[scan_y+1][scan_x+1]==1&&PLAYED[scan_y+2][scan_x+2]==1&&PLAYED[scan_y+3][scan_x+3]==1&&PLAYED[scan_y+4][scan_x+4]==1) )
over2<=1;
else if ( (scan_y<=5&&scan_x>=4&&CHESS_COLOR[scan_y][scan_x]==0&&CHESS_COLOR[scan_y+1][scan_x-1]==0&&CHESS_COLOR[scan_y+2][scan_x-2]==0&&CHESS_COLOR[scan_y+3][scan_x-3]==0&&CHESS_COLOR[scan_y+4][scan_x-4]==0)
&& (PLAYED[scan_y][scan_x]==1&&PLAYED[scan_y+1][scan_x-1]==1&&PLAYED[scan_y+2][scan_x-2]==1&&PLAYED[scan_y+3][scan_x-3]==1&&PLAYED[scan_y+4][scan_x-4]==1) )
over2<=1;
end
(5)win畫面:在任一方勝利后VGA顯示win三個(gè)字母畫面輸出勝利提示。根據(jù)棋子顏色win顯示不同顏色以顯示文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-753664.html
//產(chǎn)生win畫面1
always@(posedge vga_clk )
begin
if(((hcount>=240&&hcount<=260)||(hcount>=300&&hcount<=320)||(hcount>=360&&hcount<=380)||(hcount>=420&&hcount<=440)||(hcount>=480&&hcount<=500)||(hcount>=560&&hcount<=580))&&(vcount >=200&&vcount <=360))begin
z_flag <= 1'b1;
if(over)
z_dat <= 12'hf0f;
else
z_dat <= 12'hff0;
end else if(hcount>=260&&hcount<=380&&vcount>=340&&vcount <=360) begin
z_flag <= 1'b1;
if(over)
z_dat <= 12'hf0f;
else
z_dat <= 12'hff0;
end else if(hcount>=500&&hcount<=560&&vcount>=200&&vcount <=220) begin
z_flag <= 1'b1;
if(over)
z_dat <= 12'hf0f;
else
z_dat <= 12'hff0;
end else begin
z_flag <= 1'b0;
z_dat <= 12'hfff;
end
end
遇到的問題:
1 棋子顏色顯示不穩(wěn)定,下棋時(shí)棋子顏色隨機(jī)產(chǎn)生不受控制。原因懷疑是時(shí)鐘一開始是分頻產(chǎn)生的25M,后面換成pll鎖相環(huán)后問題果然解決。
一開始的時(shí)鐘產(chǎn)生方法:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-753664.html
always@(posedge sysclk)
begin
vga_clk <= ~vga_clk;
end
優(yōu)化后的:
pll pll(
.inclk0(sysclk),
.c0(vga_clk)
);
到了這里,關(guān)于基于FPGA的五子棋游戲設(shè)計(jì)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!