名稱(chēng):吃豆人游戲VGA顯示VHDL代碼basys3開(kāi)發(fā)板設(shè)計(jì)報(bào)告(代碼在文末下載)
軟件:VIVADO
語(yǔ)言:VHDL
代碼功能:
本設(shè)計(jì)是一個(gè)基于VGA的吃豆人游戲,玩家可以控制屏幕上的吃豆人吃散落在屏幕各個(gè)方位的豆子,同時(shí)屏幕上還有2個(gè)魔鬼,如果吃豆人和魔鬼相遇,則游戲失敗。
設(shè)計(jì)的基本要求是魔鬼的移動(dòng)方式固定,且用固定得到圓形或者方形來(lái)表示不同的對(duì)象??紤]到該實(shí)現(xiàn)方案較為簡(jiǎn)單,為使顯示更加美觀,在屏幕上添加墻壁,劃分出不同的路線。吃豆人和魔鬼也不使用固定圓形或者方形,而是用較為形象的圖案,可以體現(xiàn)出眼睛嘴巴等特征。
同時(shí)為了記錄游戲得分,添加數(shù)碼管顯示部分。
系統(tǒng)控制魔鬼在所劃分的道路上巡邏,玩家通過(guò)Basys3板子上的上下左右按鍵控制吃豆人吃豆子以及規(guī)避魔鬼。
FPGA代碼Verilog/VHDL代碼資源下載:www.hdlcode.com
本代碼已在Basys3開(kāi)發(fā)板驗(yàn)證,開(kāi)發(fā)板如下,其他開(kāi)發(fā)板可以修改管腳適配:
1. 方案選擇
本設(shè)計(jì)是一個(gè)基于VGA的吃豆人游戲,玩家可以控制屏幕上的吃豆人吃散落在屏幕各個(gè)方位的豆子,同時(shí)屏幕上還有2個(gè)魔鬼,如果吃豆人和魔鬼相遇,則游戲失敗。
設(shè)計(jì)的基本要求是魔鬼的移動(dòng)方式固定,且用固定得到圓形或者方形來(lái)表示不同的對(duì)象。
考慮到該實(shí)現(xiàn)方案較為簡(jiǎn)單,為使顯示更加美觀,在屏幕上添加墻壁,劃分出不同的路線。
吃豆人和魔鬼也不使用固定圓形或者方形,而是用較為形象的圖案,可以體現(xiàn)出眼睛嘴巴等特征。
同時(shí)為了記錄游戲得分,添加數(shù)碼管顯示部分。
系統(tǒng)控制魔鬼在所劃分的道路上巡邏,玩家通過(guò)Basys3板子上的上下左右按鍵控制吃豆人吃豆子以及規(guī)避魔鬼。
2. 系統(tǒng)設(shè)計(jì)
系統(tǒng)的整體設(shè)計(jì)框圖如下圖所示,輸入端口包括時(shí)鐘、復(fù)位、四個(gè)上下左右按鍵。輸出端口包括VGA的HSYNC、VSYNC信號(hào),顏色RGB信號(hào)以及用于數(shù)碼管控制的位選(bit_sel)和段選信號(hào)(semgnet)。
系統(tǒng)包括分頻模塊(clk_wizard)、按鍵模塊(io)、圖像控制模塊(picture_ctrl)、VGA時(shí)序控制模塊(vga_driver)和數(shù)碼管控制模塊(ssd_controller)。其中分頻模塊用于分頻產(chǎn)生其他模塊的工作時(shí)鐘,按鍵模塊實(shí)現(xiàn)按鍵消抖功能,并將按鍵信號(hào)輸出給圖像控制模塊。圖像控制模塊實(shí)現(xiàn)吃豆人圖像界面的生成功能,內(nèi)部程序描繪墻體、吃豆人、豆子、魔鬼等畫(huà)面,并可通過(guò)按鍵控制吃豆人移動(dòng)。VGA時(shí)序控制模塊實(shí)現(xiàn)VGA控制時(shí)序,使圖像控制模塊生成的湖,面可以通過(guò)VGA顯示器顯示出來(lái)。數(shù)碼管控制模塊用于控制板子上的數(shù)碼管顯示,顯示內(nèi)容為所得分?jǐn)?shù)。
3. 模塊設(shè)計(jì)
3.1 時(shí)鐘分頻模塊
Basys3板子上帶有100MHz的晶振,將該時(shí)鐘信號(hào)最為系統(tǒng)的外部輸入時(shí)鐘。本文設(shè)計(jì)的VGA顯示分辨率為800x600@60Hz,對(duì)應(yīng)的VGA時(shí)鐘為40MHz,因此選擇40MHz為本系統(tǒng)各個(gè)模塊的工作時(shí)鐘。時(shí)鐘分頻模塊實(shí)現(xiàn)將外部輸入的100MHz信號(hào)分頻為40MHz。分頻方法為調(diào)用Vivado自帶的時(shí)鐘分頻IP核,將IP核輸出頻率設(shè)置為40M,如下圖所示。
3.2 按鍵模塊
按鍵模塊實(shí)現(xiàn)按鍵消抖功能,并最終將按鍵信號(hào)轉(zhuǎn)換為對(duì)應(yīng)的移動(dòng)信號(hào)和方向信號(hào)。Basys3板子上提供了5個(gè)機(jī)械回彈式的按鍵。機(jī)械式的按鍵都會(huì)有按鍵抖動(dòng)的現(xiàn)象,為了不產(chǎn)生這種現(xiàn)象而作的措施就是按鍵消抖,按鍵模塊框圖如下圖所示。
代碼的設(shè)計(jì)過(guò)程為首先以40M時(shí)鐘信號(hào)不斷的檢測(cè)按鍵,當(dāng)檢測(cè)到鍵值變化后,開(kāi)始計(jì)數(shù)器計(jì)數(shù),當(dāng)計(jì)數(shù)值滿(mǎn)足延遲要求后,重新檢測(cè)鍵值,并將鍵值的上升沿信號(hào)輸出,即最終效果為按下一次按鍵后,對(duì)應(yīng)輸出一個(gè)時(shí)鐘周期的高電平信號(hào)。
然后對(duì)4個(gè)消抖后的按鍵信號(hào)減小判斷,使用s_Direction信號(hào)記錄按鍵對(duì)應(yīng)的方向,當(dāng)上方向鍵按下時(shí)記為“1000”,下方向記為“0100”,左方向記為“0001”,有方向記為“0010”。并且每次有按鍵按下將s_Move信號(hào)拉高,表示需要移動(dòng)。
3.3 數(shù)碼管控制模塊
數(shù)碼管控制模塊如下圖所示,該模塊用于顯示游戲分?jǐn)?shù)。
使用開(kāi)發(fā)板的7段數(shù)碼管顯示,每個(gè)數(shù)碼管輸入為7位,對(duì)應(yīng)下圖中的abcdefg7段,當(dāng)輸入0時(shí)對(duì)應(yīng)的段點(diǎn)亮,當(dāng)輸入為1時(shí),對(duì)應(yīng)的段滅。
根據(jù)上圖可以觀察到,若要顯示數(shù)字0,需要G滅,ABCDEF亮,也就是對(duì)應(yīng)編碼為“1000000”,其中從左到右依次對(duì)應(yīng)GFEDCBA。以此類(lèi)推可以得到0~9的所有編碼。
3.4 VGA時(shí)序控制模塊
模塊實(shí)現(xiàn)VGA時(shí)序的控制,用于產(chǎn)生VGA的CLK信號(hào),hsync信號(hào)和vsync信號(hào),模塊框圖如下圖所示。
本文設(shè)計(jì)的VGA時(shí)序?yàn)?00x600刷新頻率為60Hz。每場(chǎng)對(duì)應(yīng)628個(gè)行周期(628=4+23+600+1),其中600為顯示行。下圖為VGA模塊實(shí)現(xiàn)的時(shí)序圖:
HSYNC Signal 是用來(lái)控制“列填充”, 而一個(gè)HSYNC Signal 可以分為4個(gè)段,也就是 a (同步段) , b(后廊段),c(激活段),d(前廊段)。HSYNC Signal 的a 是拉低的128 個(gè)列像素,b是拉高的88個(gè)列像素,至于c 是拉高的 800 個(gè)列像素,而最后的 d 是拉高的 40 個(gè)列像素。 一列總共有1056 個(gè)列像素。VSYNC Signal 是用來(lái)控制“行掃描”。而一個(gè) VSYNC Signal 同樣可以分為 4個(gè)段, 也是 o (同步段) , p(后廊段),q(激活段),r(前廊段)。VSYNC Signal 的o 是拉低的4個(gè)行像素 ,p是拉高的23個(gè)行像素,至于q 是拉高的 600 個(gè)行像素,而最后的 r 是拉高的 1 個(gè)行像素。
3.5 圖像控制模塊
圖像控制模塊實(shí)現(xiàn)吃豆人圖像界面的生成功能,內(nèi)部程序描繪墻體、吃豆人、豆子、魔鬼等畫(huà)面,并可通過(guò)按鍵控制吃豆人移動(dòng),模塊框圖如下:
為描繪較為復(fù)雜的吃豆人、魔鬼、豆子等圖案,本設(shè)計(jì)使用ROM IP核存儲(chǔ)對(duì)應(yīng)的圖案,即事先將所繪制的圖案轉(zhuǎn)換為對(duì)應(yīng)0和1數(shù)字,再將該數(shù)字圖案存儲(chǔ)在coe文件中,當(dāng)需要顯示該圖像時(shí)不需要再寫(xiě)代碼,而只需要調(diào)用該圖像的IP核即可。下圖為魔鬼對(duì)應(yīng)的coe文件內(nèi)容。
上圖中,我將圖案的輪廓描繪出來(lái)了,數(shù)字1代表屏幕上顯示的內(nèi)容,數(shù)字0代表不顯示,因此上圖中魔鬼的眼睛和嘴巴出現(xiàn)了。其他的吃豆人、豆子等圖案也采用這種方法實(shí)現(xiàn)。圖中的墻壁由于形狀比較規(guī)范,直接使用代碼設(shè)計(jì)。設(shè)計(jì)方法為通過(guò)約束垂直方向的起始和結(jié)束位置以及水平方向的起始和結(jié)束位置,實(shí)現(xiàn)描繪出一個(gè)矩形的功能。
4. 代碼調(diào)試遇到的問(wèn)題
在調(diào)試過(guò)程中,遇到了一些問(wèn)題,首先在設(shè)計(jì)圖案時(shí),原本的設(shè)計(jì)的都通過(guò)代碼直接描述,但是實(shí)現(xiàn)過(guò)程中由于不規(guī)范的圖形使用代碼描述時(shí)實(shí)在過(guò)于復(fù)雜,導(dǎo)致代碼可讀性差且任意出錯(cuò),后面通過(guò)查找資料,了解到使用coe文件的方法,就很方便的完成了異形圖形的設(shè)計(jì)。在使用數(shù)碼管顯示分?jǐn)?shù)時(shí),一開(kāi)始沒(méi)有區(qū)分顯示的個(gè)位和十位,導(dǎo)致最終在數(shù)碼管顯示時(shí)按十六進(jìn)制顯示了,不符合日常習(xí)慣,后面通過(guò)修改代碼,使分?jǐn)?shù)的十位和個(gè)位分別計(jì)數(shù),改成十進(jìn)制顯示,方便觀看。
部分代碼展示:
library?IEEE; use?IEEE.STD_LOGIC_1164.ALL; use?IEEE.NUMERIC_STD.ALL; entity?vga_driver?is Port( pic_clk????:?in??std_logic;--628*1056*60?40MHz. p_Reset????:?in??std_logic; p_Color????:?in??std_logic_vector(2?downto?0); H_position?????:?out?integer; V_position?????:?out?integer; VGA_hsync_o????:?out?std_logic; VGA_vsync_o????:?out?std_logic; VGA_R???:?out?std_logic_vector(3?downto?0); VGA_G?:?out?std_logic_vector(3?downto?0); VGA_B??:?out?std_logic_vector(3?downto?0) ); end?vga_driver; architecture?Behavioral?of?vga_driver?is --?Timing?Constants --?For?polarity?'0'?means?negative?polarity --?Horizontal?Axis constant?HD????????:?integer???:=?800;?--?Visiable?Area constant?HFP???????:?integer???:=?40;?--?Front?Porch constant?HSP???????:?integer???:=?128;?--?Sync?Pulse constant?HBP???????:?integer???:=?88;?--?Back?porch constant?HPOLARITY?:?std_logic?:=?'1';?--?Polartity constant?HTOTAL????:?integer???:=?HD?+?HFP?+?HSP?+?HBP;?--?Whole?Line --?Vertical?Axis constant?VD????????:?integer???:=?600;?--?Visiable?Area constant?VFP???????:?integer???:=?1;?--?Front?Porch constant?VSP???????:?integer???:=?4;?--?Sync?Pulse constant?VBP???????:?integer???:=?23;?--?Back?porch constant?VPOLARITY?:?std_logic?:=?'1';?--?Polartity constant?VTOTAL????:?integer???:=?VD?+?VFP?+?VSP?+?VBP;?--?Whole?Line signal?s_HPos?????:?integer; signal?s_VPos?????:?integer; signal?s_videoOn??:?std_logic; signal?s_VGARed???:?std_logic_vector(3?downto?0); signal?s_VGAGreen?:?std_logic_vector(3?downto?0); signal?s_VGABlue??:?std_logic_vector(3?downto?0); begin --?Horizontal?Position?Counter HPCounter?:?process(pic_clk,?p_Reset) begin if?(p_Reset?=?'1')?then s_HPos?<=?0; elsif?(rising_edge(pic_clk))?then if?(s_HPos?=?HTOTAL)?then s_HPos?<=?0; else s_HPos?<=?s_HPos?+?1; end?if; end?if; end?process; H_position?<=?s_HPos; --?Vertical?Position?Counter VPCounter?:?process(pic_clk,?p_Reset) begin if?(p_Reset?=?'1')?then s_VPos?<=?0; elsif?(rising_edge(pic_clk))?then if?(s_HPos?=?HTOTAL)?then if?(s_VPos?=?VTOTAL)?then s_VPos?<=?0; else s_VPos?<=?s_VPos?+?1; end?if; end?if; end?if; end?process; V_position?<=?s_VPos; --?Horizontal?Synchronisation HSync?:?process(pic_clk,?p_Reset) begin if?(p_Reset?=?'1')?then VGA_hsync_o?<=?HPOLARITY; elsif?(rising_edge(pic_clk))?then if?((s_HPos?<?HD?+?HFP)?OR?(s_HPos?>?HD?+?HFP?+?HSP))?then VGA_hsync_o?<=?not?HPOLARITY; else VGA_hsync_o?<=?HPOLARITY; end?if; end?if; end?process; --?Vertical?Synchronisation VSync?:?process(pic_clk,?p_Reset) begin if?(p_Reset?=?'1')?then VGA_vsync_o?<=?VPOLARITY; elsif?(rising_edge(pic_clk))?then if?((s_VPos?<?VD?+?VFP)?OR?(s_VPos?>?VD?+?VFP?+?VSP))?then VGA_vsync_o?<=?not?VPOLARITY; else VGA_vsync_o?<=?VPOLARITY; end?if; end?if; end?process; --?Video?On videoOn?:?process(pic_clk,?p_Reset) begin if?(p_Reset?=?'1')?then s_videoOn?<=?'0'; elsif?(rising_edge(pic_clk))?then if?(s_HPos?<=?HD?and?s_VPos?<=?VD)?then s_videoOn?<=?'1'; else s_videoOn?<=?'0'; end?if; end?if; end?process; colorOutput?:?process(pic_clk,?p_Reset) begin if?(p_Reset?=?'1')?then s_VGARed???<=?(others?=>?'0'); s_VGAGreen?<=?(others?=>?'0'); s_VGABlue??<=?(others?=>?'0'); elsif?(rising_edge(pic_clk))?then if?(s_VideoOn?=?'1')?then s_VGARed???<=?p_Color(2)?&?p_Color(2)?&?p_Color(2)?&?p_Color(2); s_VGAGreen?<=?p_Color(1)?&?p_Color(1)?&?p_Color(1)?&?p_Color(1); s_VGABlue??<=?p_Color(0)?&?p_Color(0)?&?p_Color(0)?&?p_Color(0); else s_VGARed???<=?(others?=>?'0'); s_VGAGreen?<=?(others?=>?'0'); s_VGABlue??<=?(others?=>?'0'); end?if; end?if; end?process; VGA_R???<=?s_VGARed; VGA_G?<=?s_VGAGreen; VGA_B??<=?s_VGABlue; end?Behavioral;
點(diǎn)擊鏈接獲取代碼文件:http://www.hdlcode.com/index.php?m=home&c=View&a=index&aid=288