本文描述如何實現一個以60px / sec初始方向斜向下45°移動的方塊(碰到邊界後根據反射原理反向移動)。方塊顏色由8個開關控制,方塊大小爲32px * 24px。測試
1 基本步驟:spa
編寫主體代碼:設計
1編寫掃描整個屏幕的模塊;調試
2編寫根據vsync信號和座標邊界判斷當前矩陣有效範圍座標,移動方向的模塊;code
3 編寫根據當前所掃描座標和矩陣座標範圍肯定對應顏色的模塊。接口
查看綜合電路圖,肯定是否符合設計邏輯。ip
編寫接口約束。將輸入輸出變量指定到開發板的端口。ci
編寫測試代碼,進行模擬測試.模擬測試能夠觀察波形圖來判斷設計是否正確。開發
將程序寫入開發板運行,檢查運行結果。文檔
2 基本原理:
1)VGA引腳圖
通常經常使用引腳就是行同步信號,列同步信號,和8個RGB引腳。實驗中也只用到了VGA的這幾個引腳。RGB引腳控制顏色信號是0 ~0.7v 。 0v就是全黑。0.7V就是全亮,代碼中二進制1爲高電平,0爲低電平。
HS爲水平有效信號,VS爲垂直有效信號。在創建階段垂直信號有2個時鐘,水平信號有96個時鐘,這個時段對應有效信號要設爲0。以後要設爲1。
2)VGA時序邏輯:
能夠看出水平掃描和垂直掃描週期中各個階段要花費的時鐘週期。
能夠得知水平信號是和25MHz時鐘同步計數的,垂直信號是用水平信號的週期計數的。
以後發現這張表有不對的地方,就是Ts開始的地方應該是在下表所示的位置。
3) VGA counter 的電路邏輯:
3 源代碼
.v
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 21:08:50 12/07/2014 // Design Name: // Module Name: VGA // Project Name: // Target Devices: // Tool versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module VGA( input clk, rst, input [7:0] rect_color, output reg hsync,vsync, output reg [7:0] color ); reg [9:0] hgrid = 0; // 800 TS , x reg [9:0] vgrid = 0; // 521 Ts , y reg clk_4fp = 0; reg [1:0] count = 0; //分頻 25M Hz always @ (posedge clk or posedge rst) begin if(rst) count <= 0; else count <= count + 1; end always @ (posedge clk or posedge rst) begin if(rst) clk_4fp <= 0; else begin if (count[1] == 1) clk_4fp <= 1; else clk_4fp <= 0; end end // 掃描整個屏幕,包括非顯示區,肯定何時 always @ (posedge clk_4fp or posedge rst) begin if(rst) begin hgrid <= 0; vgrid <= 0; end else begin //根據basic VGA controller的電路圖,在水平方向掃描一次後,使得垂直方向開始掃描 // 由於水平方向是時鐘計數的,垂直方向是根據水平方向的脈衝計數的 if(hgrid >= 800) begin hgrid <= 0; vgrid <= (vgrid >= 521? 0 : vgrid + 1'b1); end else hgrid <= hgrid + 1'b1; end end //設置行選,列選信號有效。 因爲有創建的Tpw時間,因此要把Tpw(脈衝寬度)時間段內的座標視爲無效 always @(posedge clk_4fp or posedge rst) begin if(rst) begin hsync <= 0; vsync <= 0; end else begin if(hgrid < 752 &&hgrid >= 656) // 脈衝內爲0 (800 - Tbp -Tpw) ~ (800 - Tbp) hsync <= 0; else hsync <= 1; if(vgrid < 492 && vgrid >= 490 ) // 脈衝內爲0 (521 - Tbp -Tpw) ~ (521 - Tbp) vsync <= 0; else vsync <= 1; end end //////////////////////////////////////////// // 顯示移動矩形 parameter WIDTH = 32, //矩形長 HEIGHT = 24, //矩形寬 // 顯示區域的邊界 DISV_TOP = 10'd480, // display top bound DISV_DOWN =10'd0, // display down bound DISH_LEFT = 10'd0, // display left bound DISH_RIGHT = 10'd640; // display right bound //初始矩形的位置,在顯示區的左下角 reg [9:0] topbound = DISV_DOWN + HEIGHT; reg [9:0] downbound = DISV_DOWN ; reg [9:0] leftbound = DISH_LEFT ; reg [9:0] rightbound = DISH_LEFT + WIDTH ; //初始方向爲東南方向 reg [1:0] movexy = 2'b11; /* 根據時間選擇不一樣範圍座標的像素顯示顏色,使其成爲一個移動的矩形。 因爲是60/s, vsync的Ts剛好是移動1px所花的時間,因此用vsync信號的上升沿判斷 */ //確立每個像素時鐘裏矩形的座標範圍 always @ (posedge vsync or posedge rst) begin if(rst) begin topbound = DISV_DOWN + HEIGHT ; downbound = DISV_DOWN; leftbound = DISH_LEFT; rightbound = DISH_LEFT + WIDTH ; movexy = 2'b11; end else begin //碰到邊界,改變方向 case(movexy[1:0]) 2'b11: begin // 東南 if (topbound == DISV_TOP && rightbound < DISH_RIGHT ) movexy = 2'b10; else if (topbound < DISV_TOP && rightbound == DISH_RIGHT ) movexy = 2'b01; else if (topbound == DISV_TOP && rightbound == DISH_RIGHT ) movexy = 2'b00; end 2'b10: begin // 東北 if (downbound == DISV_DOWN&& rightbound < DISH_RIGHT ) movexy = 2'b11; else if (downbound > DISV_DOWN && rightbound == DISH_RIGHT ) movexy = 2'b00; else if (downbound == DISV_DOWN && rightbound == DISH_RIGHT ) movexy = 2'b01; end 2'b00: begin // 西北 if (downbound == DISV_DOWN && leftbound > DISH_LEFT ) movexy = 2'b01; else if (downbound > DISV_DOWN && leftbound == DISH_LEFT ) movexy = 2'b10; else if (downbound == DISV_DOWN && leftbound == DISH_LEFT ) movexy = 2'b11; end 2'b01: begin // 西南 if (topbound == DISV_TOP && leftbound > DISH_LEFT ) movexy = 2'b00; else if (topbound < DISV_TOP && leftbound == DISH_LEFT ) movexy = 2'b11; else if (topbound == DISV_TOP && leftbound == DISH_LEFT ) movexy = 2'b10; end default: movexy = 2'b11; endcase topbound <= topbound + ( movexy[0]? 1 : -1 ); downbound <= downbound + ( movexy[0]? 1 : -1 ); leftbound <= leftbound + ( movexy[1]? 1 : -1 ); rightbound <= rightbound + ( movexy[1]? 1 : -1 ); end end // 肯定掃描到哪個像素該顯示什麼顏色 always @(posedge clk_4fp or posedge rst) begin if(rst) color <= 8'b0000_0000; else begin if (hgrid >= DISH_LEFT && hgrid <= DISH_RIGHT && vgrid >= DISV_DOWN && vgrid <= DISV_TOP) begin if(hgrid >= leftbound && hgrid <= rightbound && vgrid >= downbound && vgrid <= topbound) color <= rect_color; else color <= 8'b0000_0011; //黑色 end else begin color <= 8'b0000_0000; end end end endmodule
.ucf
NET "clk" LOC = "V10"; NET "rst" CLOCK_DEDICATED_ROUTE = FALSE; NET "color[7]" LOC = "U7"; NET "color[6]" LOC = "V7"; NET "color[5]" LOC = "N7"; NET "color[4]" LOC = "P8"; NET "color[3]" LOC = "T6"; NET "color[2]" LOC = "V6"; NET "color[1]" LOC = "R7"; NET "color[0]" LOC = "T7"; NET "rect_color[7]" LOC = "T5"; NET "rect_color[6]" LOC = "V8"; NET "rect_color[5]" LOC = "U8"; NET "rect_color[4]" LOC = "N8"; NET "rect_color[3]" LOC = "M8"; NET "rect_color[2]" LOC = "V9"; NET "rect_color[1]" LOC = "T9"; NET "rect_color[0]" LOC = "T10"; NET "hsync" LOC = "N6"; NET "vsync" LOC = "P7";
4 後記
在調試過程當中出現的顯示區域邊界與顯示屏邊界不能重合的問題,這些主要就是這四個參數怎麼計算的問題:
DISV_TOP = 10'd480, // display top bound
DISV_DOWN =10'd0, // display down bound
DISH_LEFT = 10'd0, // display left bound
DISH_RIGHT = 10'd640; // display right bound
這四個參數最開始是依據下面的圖和參數來算的,但是發現不對。按照這樣算的話有效顯示的計數座標範圍是(144-784, 31-511),以後發現這樣計算顯示就會有問題。方塊能夠正常移動反彈,就是邊界顯示有問題,因而就不斷去調整顯示區的邊界座標,發現是和pdf說明文檔中另外一幅圖吻合的。真是個坑。
正確的TS應該是如下這幅圖中「Total Horizon time」這一段,也就是說有效顯示區的計數範圍是(0-640 ,0-480)的。而後hsync和vsync爲0的階段也是Tpw階段,以前根據上面那幅圖Tpw階段是(0-96,0-2),運行的時候會出現方塊顏色爲漸變色的bug,不過根據下面這幅圖計算,計數座標的範圍就是(656-752, 490-492)爲Tpw階段,這樣就正常了。