FPGA驱动VGA显示器_VGA电阻匹配网络法_VGA彩条实验_VGA彩条flowing_VGA显示器时序图_VGA显示器原理_verilog_源码_testbench_现象

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/Mr_liu_666/article/details/102764062

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源码:工程链接https://download.csdn.net/download/mr_liu_666/11928995

`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彩条闪动就做好了:

猜你喜欢

转载自blog.csdn.net/Mr_liu_666/article/details/102764062