FPGA驅動VGA顯示器_VGA電阻匹配網絡法_VGA彩條實驗_VGA彩條flowing_VGA顯示器時序圖_VGA顯示器原理_verilog_源碼_testbench_現象

VGA電阻網絡分壓法硬件電路:https://blog.csdn.net/mr_liu_666/article/details/102761178

首先給出各部分的源碼,有興趣的朋友可以去下面看VGA的原理。

先是VGA驅動模塊:

//學習&參考源:正點原子、開拓者FPGA
module vga_driver(
    input           vga_clk,      //VGA驅動時鐘
    input           sys_rst_n,    //復位信號
    //VGA接口                          
    output          vga_hs,       //行同步信號
    output          vga_vs,       //場同步信號
    output  [15:0]  vga_rgb,      //紅綠藍三原色輸出
    
    input   [15:0]  pixel_data,   //像素點數據
    output  [ 9:0]  pixel_xpos,   //像素點橫座標
    output  [ 9:0]  pixel_ypos    //像素點縱座標    
    );                             
                                                        
//parameter define  
parameter  H_SYNC   =  10'd96;    //行同步
parameter  H_BACK   =  10'd48;    //行顯示後沿
parameter  H_DISP   =  10'd640;   //行有效數據
parameter  H_FRONT  =  10'd16;    //行顯示前沿
parameter  H_TOTAL  =  10'd800;   //行掃描週期

parameter  V_SYNC   =  10'd2;     //場同步
parameter  V_BACK   =  10'd33;    //場顯示後沿
parameter  V_DISP   =  10'd480;   //場有效數據
parameter  V_FRONT  =  10'd10;    //場顯示前沿
parameter  V_TOTAL  =  10'd525;   //場掃描週期
          
//reg define                                     
reg  [9:0] cnt_h;
reg  [9:0] cnt_v;

//wire define
wire       vga_en;
wire       data_req; 

//VGA行場同步信號
/*正如我所說,行計數器對應行,在前96個像素時鐘,或者說96個“無用像素點”時,行同步時鐘輸出低電平,達到“同步”的目的,除此之外,在48個“無用像素點”,640個有用像素和16個“無用像素”時,行同步時鐘都是高電平,場同步時鐘也是一個道理*/
assign vga_hs  = (cnt_h <= H_SYNC - 1'b1) ? 1'b0 : 1'b1;
assign vga_vs  = (cnt_v <= V_SYNC - 1'b1) ? 1'b0 : 1'b1;

//使能RGB565數據輸出
/*其實“有用像素點”的數據什麼時候輸出都行,但是最好還是在有用像素點到來(有用時鐘到來)的時候再輸出,畢竟如果是顯示圖片或者是彩條,忘記這一點,豎線就會變成斜線,而且會有很多數據丟失(顯示到屏幕外面去了),這一句的意思就是在640*480的範圍內才允許有用數據輸出*/
assign vga_en  = (((cnt_h >= H_SYNC+H_BACK) && (cnt_h < H_SYNC+H_BACK+H_DISP))
                 &&((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
                 ?  1'b1 : 1'b0;
                 
//RGB565數據輸出                 
assign vga_rgb = vga_en ? pixel_data : 16'd0;

//請求像素點顏色數據輸入 
/*這句的意思也是在640*480的範圍內的時候纔會去讀數據*/               
assign data_req = (((cnt_h >= H_SYNC+H_BACK-1'b1) && (cnt_h < H_SYNC+H_BACK+H_DISP-1'b1))
                  && ((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
                  ?  1'b1 : 1'b0;

//像素點座標               
/*只有在640*480之內讀數據的時候纔給出橫縱座標,其實也就是橫縱週期*/
assign pixel_xpos = data_req ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 10'd0;
assign pixel_ypos = data_req ? (cnt_v - (V_SYNC + V_BACK - 1'b1)) : 10'd0;

//行計數器對像素時鐘計數
/*VGA_CLK是鎖相環分頻分出來的,頻率是25M*/
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        cnt_h <= 10'd0;                                  
    else begin
        if(cnt_h < H_TOTAL - 1'b1)                                               
            cnt_h <= cnt_h + 1'b1;                               
        else 
            cnt_h <= 10'd0;  
    end
end

//場計數器對行計數
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        cnt_v <= 10'd0;                                  
    else if(cnt_h == H_TOTAL - 1'b1) begin
        if(cnt_v < V_TOTAL - 1'b1)                                               
            cnt_v <= cnt_v + 1'b1;                               
        else 
            cnt_v <= 10'd0;  
    end
end

endmodule 

然後是彩條(數據產生)模塊:

//學習&參考源:正點原子、開拓者FPGA
/*模塊的意義就是在驅動模塊給出橫縱座標之後返回一個RGB值以顯示*/
module vga_display(
    input             vga_clk,                  //VGA驅動時鐘
    input             sys_rst_n,                //復位信號
    
    input      [ 9:0] pixel_xpos,               //像素點橫座標
    input      [ 9:0] pixel_ypos,               //像素點縱座標    
    output reg [15:0] pixel_data                //像素點數據
    );    
    
parameter  H_DISP = 10'd640;                    //分辨率——行
parameter  V_DISP = 10'd480;                    //分辨率——列
localparam WHITE  = 16'b11101_111011_11101;     //RGB565 白色
localparam BLACK  = 16'b00000_000000_00000;     //RGB565 黑色
localparam RED    = 16'b11101_000000_00000;     //RGB565 紅色
localparam GREEN  = 16'b00000_111011_00000;     //RGB565 綠色
localparam BLUE   = 16'b00000_000000_11101;     //RGB565 藍色

reg [23:0] counter_Change;
reg [23:0] counter_Compare;


/*這部分是彩條閃爍部分,通過對主時鐘分頻,獲得一個數值不斷變化的counter_Compare值,將它的18位到5位作爲顏色輸出值,變色頻率就是25M/1M/32 = 0.78次/S*/
always @(posedge vga_clk)
begin
	if(counter_Compare < 24'hffffff)
	  begin
	    if(counter_Change % 24'd1_000_000 == 0)//主時鐘變1M次,顏色變一次
		    counter_Compare <= counter_Compare + 1'b1;
    end    
	else 
	  counter_Compare <= 24'd0;
end   

always @(posedge vga_clk)
begin		
  if(counter_Change < 24'd2_000_000)
    counter_Change <= counter_Change + 1'b1;
	else 
		counter_Change <=  24'b0;
		
end


    
	 
//*****************************************************
//**                    main code
//*****************************************************
//根據當前像素點座標指定當前像素點顏色數據,在屏幕上顯示彩條
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        pixel_data <= 16'd0;                                  
    else begin
        /*爲了使顏色更有規律又各不相同,採用了基色減去隨機色的辦法,這樣顏色的就會在基色附近變動,不至於變得太遠,失去規律*/
        if((pixel_xpos >= 0) && (pixel_xpos <= (H_DISP/5)*1))                                              
            pixel_data <= WHITE - counter_Compare[17:4] % WHITE;                               
        else if((pixel_xpos >= (H_DISP/5)*1) && (pixel_xpos < (H_DISP/5)*2))
            pixel_data <= RED - counter_Compare[17:4] % RED;  
        else if((pixel_xpos >= (H_DISP/5)*2) && (pixel_xpos < (H_DISP/5)*3))
            pixel_data <= GREEN - counter_Compare[17:4] % GREEN;  
        else if((pixel_xpos >= (H_DISP/5)*3) && (pixel_xpos < (H_DISP/5)*4))
            pixel_data <= BLUE - counter_Compare[17:4] % BLUE;  
        else 
			begin
/*這裏把最後一個豎條分爲了5個橫格,變化頻率更慢*/
				if((pixel_ypos >= 0) && (pixel_ypos <= (V_DISP/5)*1))                                              
					pixel_data <= WHITE - counter_Compare[22:7] % 11;                               
				else if((pixel_ypos >= (V_DISP/5)*1) && (pixel_ypos < (V_DISP/5)*2))
					pixel_data <= WHITE - counter_Compare[22:7] % 19;  
				else if((pixel_ypos >= (V_DISP/5)*2) && (pixel_ypos < (V_DISP/5)*3))
					pixel_data <= WHITE - counter_Compare[22:7] % 29;  
				else if((pixel_ypos >= (V_DISP/5)*3) && (pixel_ypos < (V_DISP/5)*4))
					pixel_data <= WHITE - counter_Compare[22:7] % 37;
				else
					pixel_data <= WHITE - counter_Compare[22:7] % 47;
			end
    end
end

endmodule 

頂層文件:

//學習&參考源:正點原子、開拓者FPGA
//待PLL輸出穩定之後,停止復位
assign rst_n_w = sys_rst_n && locked_w;
   
vga_pll	u_vga_pll(                  //時鐘分頻模塊
	.inclk0         (sys_clk),    
	.areset         (~sys_rst_n),
    
	.c0             (vga_clk_w),    //VGA時鐘 25M
	.locked         (locked_w)
	); 

vga_driver u_vga_driver(
    .vga_clk        (vga_clk_w),    
    .sys_rst_n      (rst_n_w),    

    .vga_hs         (vga_hs),       
    .vga_vs         (vga_vs),       
    .vga_rgb        (vga_rgb),      
    
    .pixel_data     (pixel_data_w), 
    .pixel_xpos     (pixel_xpos_w), 
    .pixel_ypos     (pixel_ypos_w)
    ); 
    
vga_display u_vga_display(
    .vga_clk        (vga_clk_w),
    .sys_rst_n      (rst_n_w),
    
    .pixel_xpos     (pixel_xpos_w),
    .pixel_ypos     (pixel_ypos_w),
    .pixel_data     (pixel_data_w)
    );   
    
endmodule 

PLL鎖相環這個IP請自行編輯吧,頻率爲25M(對於640 * 480 * 60Hz)。

相應的testbench源碼:

`timescale 10ns/10ns

module vga_drivertb(); 
         
parameter T = 4;//12.5ns	

reg		sys_clk;        //系統時鐘
reg		sys_rst_n;      //復位信號
//reg  	[15:0]	pixel_data;  //像素點數據  

wire          vga_hs;      //行同步信號
wire          vga_vs;       //場同步信號
wire  [15:0]  vga_rgb;     //紅綠藍三原色輸出
wire  [9:0]  pixel_xpos;   //像素點橫座標
wire  [9:0]  pixel_ypos;    //像素點縱座標    

integer i;   

initial 
begin
	sys_clk = 'b0;
	sys_rst_n = 'b1;
/*	for(i = 0; i < 16'hFFFF ; i = i + 1)
	begin
		#(5*T) pixel_data <= i;
	end
*/	
end

always 
begin
	#(T/2) sys_clk = ~sys_clk;
end

    
vga_driver vga_driver0(
    .vga_clk(sys_clk),      //VGA驅動時鐘
    .sys_rst_n(sys_rst_n),    //復位信號
    //VGA接口                          
    .vga_hs(vga_hs),       //行同步信號
    .vga_vs(vga_vs),       //場同步信號
    .vga_rgb(vga_rgb),      //紅綠藍三原色輸出
    
 //   .pixel_data(pixel_data),   //像素點數據
    .pixel_xpos(pixel_xpos),   //像素點橫座標
    .pixel_ypos(pixel_ypos)    //像素點縱座標    
    );   
    
endmodule 

被觀察的driver模塊:

//****************************************Copyright (c)***********************************//
//技術支持:www.openedv.com
//淘寶店鋪:http://openedv.taobao.com 
//關注微信公衆平臺微信號:"正點原子",免費獲取FPGA & STM32資料。
//版權所有,盜版必究。
//Copyright(C) 正點原子 2018-2028
//All rights reserved	                               
//----------------------------------------------------------------------------------------
// File name:           vga_driver
// Last modified Date:  2018/1/30 11:12:36
// Last Version:        V1.1
// Descriptions:        vga驅動
//----------------------------------------------------------------------------------------
// Created by:          正點原子
// Created date:        2018/1/29 10:55:56
// Version:             V1.0
// Descriptions:        The original version
//
//----------------------------------------------------------------------------------------
// Modified by:		    正點原子
// Modified date:	    2018/1/30 11:12:36
// Version:			    V1.1
// Descriptions:	    vga驅動
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module vga_driver(
    input           vga_clk,      //VGA驅動時鐘
    input           sys_rst_n,    //復位信號
    //VGA接口                          
    output          vga_hs,       //行同步信號
    output          vga_vs,       //場同步信號
    output  [15:0]  vga_rgb,      //紅綠藍三原色輸出
    
//    input   [15:0]  pixel_data,   //像素點數據
    output  [ 9:0]  pixel_xpos,   //像素點橫座標
    output  [ 9:0]  pixel_ypos    //像素點縱座標    
    );                             
                                                        
//parameter define  
parameter  H_SYNC   =  10'd96;    //行同步
parameter  H_BACK   =  10'd48;    //行顯示後沿
parameter  H_DISP   =  10'd640;   //行有效數據
parameter  H_FRONT  =  10'd16;    //行顯示前沿
parameter  H_TOTAL  =  10'd800;   //行掃描週期

parameter  V_SYNC   =  10'd2;     //場同步
parameter  V_BACK   =  10'd33;    //場顯示後沿
parameter  V_DISP   =  10'd480;   //場有效數據
parameter  V_FRONT  =  10'd10;    //場顯示前沿
parameter  V_TOTAL  =  10'd525;   //場掃描週期
localparam WHITE  = 16'b11111_111111_11111;     //RGB565 白色
localparam BLACK  = 16'b00000_000000_00000;     //RGB565 黑色
localparam RED    = 16'b11111_000000_00000;     //RGB565 紅色
localparam GREEN  = 16'b00000_111111_00000;     //RGB565 綠色
localparam BLUE   = 16'b00000_000000_11111;     //RGB565 藍色
	   
//reg define                                     
reg  [9:0] cnt_h;
reg  [9:0] cnt_v;


reg  [15:0] pixel_data;
//wire define
wire       vga_en;
wire       data_req; 

//*****************************************************
//**                    main code
//*****************************************************
//VGA行場同步信號
assign vga_hs  = (cnt_h <= H_SYNC - 1'b1) ? 1'b0 : 1'b1;
assign vga_vs  = (cnt_v <= V_SYNC - 1'b1) ? 1'b0 : 1'b1;

//使能RGB565數據輸出
assign vga_en  = (((cnt_h >= H_SYNC+H_BACK) && (cnt_h < H_SYNC+H_BACK+H_DISP))
                 &&((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
                 ?  1'b1 : 1'b0;
                 
//RGB565數據輸出                 
assign vga_rgb = vga_en ? pixel_data : 16'd0;

//請求像素點顏色數據輸入                
assign data_req = (((cnt_h >= H_SYNC+H_BACK-1'b1) && (cnt_h < H_SYNC+H_BACK+H_DISP-1'b1))
                  && ((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
                  ?  1'b1 : 1'b0;

//像素點座標                
assign pixel_xpos = data_req ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 10'd0;
assign pixel_ypos = data_req ? (cnt_v - (V_SYNC + V_BACK - 1'b1)) : 10'd0;

//行計數器對像素時鐘計數
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        cnt_h <= 10'd0;                                  
    else begin
        if(cnt_h < H_TOTAL - 1'b1)                                               
            cnt_h <= cnt_h + 1'b1;                               
        else 
            cnt_h <= 10'd0;  
    end
end

//場計數器對行計數
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        cnt_v <= 10'd0;                                  
    else if(cnt_h == H_TOTAL - 1'b1) begin
        if(cnt_v < V_TOTAL - 1'b1)                                               
            cnt_v <= cnt_v + 1'b1;                               
        else 
            cnt_v <= 10'd0;  
    end
end

always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        pixel_data <= 16'd0;                                  
    else begin
        if((pixel_xpos >= 0) && (pixel_xpos <= (H_DISP/5)*1))                                              
            pixel_data <= WHITE;                               
        else if((pixel_xpos >= (H_DISP/5)*1) && (pixel_xpos < (H_DISP/5)*2))
            pixel_data <= BLACK;  
        else if((pixel_xpos >= (H_DISP/5)*2) && (pixel_xpos < (H_DISP/5)*3))
            pixel_data <= RED;  
        else if((pixel_xpos >= (H_DISP/5)*3) && (pixel_xpos < (H_DISP/5)*4))
            pixel_data <= GREEN;  
        else 
            pixel_data <= BLUE;  
    end
end



endmodule 

連接了硬件電路之後,FPGA端的寫法就是寫一個提供行時鐘、幀時鐘和16bits顏色數據的輸出接口,VGA顯示原理如下(黑白圖來自開拓者FPGA):

可見除了顏色和共地端以外,VGA接口還要求行同步和場同步(幀同步),地址嗎0 1 2 3 沒有用上。

以下的是行同步適中的時序圖(橫軸時間,縱軸0 1),行同步的意思就是在每一個HSYNC的週期裏面,顯示器的一行被刷新了,也就是一個週期把640個RGB像素點打到屏幕上(如果顯示器是640*480的),同步和顯示後沿、前沿期間的  數據  是沒有意義的。

以下的是場同步適中的時序圖(橫軸時間,縱軸0 1),行同步的意思就是在每一個VSYNC的週期裏面,顯示器的一幀被刷新了,也就是一個週期把480行圖像打到屏幕上(如果顯示器是640*480的),也就是一個VSYNC週期同步和顯示後沿、前沿期間的  數據  是沒有意義的。

所以他的刷新方式就是:

兩個VSYNC和HSYNC都是由一個主時鐘分頻出來的,主時鐘就是像素時鐘,一個主時鐘一個像素點,800個主時鐘就是一行,800*525個主時鐘就是一幀。因爲像素點和時鐘是對應的,所以圖像的橫縱也完全可以和像素時鐘一個個週期對應起來,如下圖,一幀顯示需要800*525個週期(像素點),在中間橙色以外的像素點可以輸出,但是不會顯示,只有橫縱640*480的像素點纔會完整的顯示在屏幕上。

不同的顯示器對應不同的像素時鐘和時序,分辨率和刷新速度都是影響時鐘的因素:

綜上,一個VGA彩條閃動就做好了:

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章