verilog實現的VGA顯示自反彈移動小方塊

      本文描述如何實現一個以60px / sec初始方向斜向下45°移動的方塊(碰到邊界後根據反射原理反向移動)。方塊顏色由8個開關控制,方塊大小爲32px * 24px測試

   1 基本步驟:spa

編寫主體代碼:設計

1編寫掃描整個屏幕的模塊;調試

2編寫根據vsync信號和座標邊界判斷當前矩陣有效範圍座標,移動方向的模塊;code

3 編寫根據當前所掃描座標和矩陣座標範圍肯定對應顏色的模塊。接口

查看綜合電路圖,肯定是否符合設計邏輯。ip

編寫接口約束。將輸入輸出變量指定到開發板的端口。ci

編寫測試代碼,進行模擬測試.模擬測試能夠觀察波形圖來判斷設計是否正確。開發

將程序寫入開發板運行,檢查運行結果。文檔

  2 基本原理:

1)VGA引腳圖

通常經常使用引腳就是行同步信號,列同步信號,和8RGB引腳。實驗中也只用到了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)的。而後hsyncvsync0的階段也是Tpw階段,以前根據上面那幅圖Tpw階段是(0-96,0-2),運行的時候會出現方塊顏色爲漸變色的bug,不過根據下面這幅圖計算,計數座標的範圍就是(656-752 490-492)爲Tpw階段,這樣就正常了。

相關文章
相關標籤/搜索