Verilog编写VGA控制器


关于VGA(视频图形阵列)驱动的博文数不胜数,虽然自己也一直在用该模块,但从未独立编写过这部分的代码,每次都是匆匆看一眼,今天整理一下~


VGA电路原理

VGA电路原理图如下,主要包括行场同步信号以及RGB数据的输出,无其他外部芯片,因此我们只需关注其显示原理和时序即可。
在这里插入图片描述

VGA扫描方式

1、显示器扫描包括逐行扫描和隔行扫描,其中隔行扫描是每隔一行扫一线,扫完一帧后返回来扫描剩余的线,隔行扫描的显示器闪烁快,容易引起视觉疲劳,因此一般采用逐行扫描的方式。

2、如下图所示,VGA采用逐行扫描的方式,从屏幕左上角一点开始,从左向右逐点扫描,每一行扫描完成后,回到下一行的起始位置继续扫描,从一行的结束位置到下一行的开始位置(虚线处),这个期间为行消隐期(简单理解,这个时间没不进行扫描),每一行开始和结束位置用行同步信号进行同步;当扫描完所有的行形成一帧,用场同步信号进行场同步,同样的,一帧完成后,扫描从一帧图像的右下角位置到下一帧左上角位置,紧接着再次扫描,此期间为场消隐期
在这里插入图片描述

VGA时序图

如下图,VGA时序主要包括两部分:行时序H 和 场时序V。
在这里插入图片描述

其中行同步时序主要包括:行同步脉冲(a) 、显示后沿段(b) 、显示数据有效段©和显示前沿段(d)这四个参数。同步脉冲、显示前后沿都在行消隐期间,当消隐有效时,RGB信号无效,从而屏幕上不显示数据。
在这里插入图片描述
场同步时序同理:
场同步脉冲(a) 、显示后沿段(b) 、显示数据有效段©和显示前沿段(d)。同步脉冲、显示前后沿都在场消隐期间,当消隐有效时,RGB信号无效,从而屏幕上不显示数据。
在这里插入图片描述

注意:
1、由于行场同步都是负极性,因此同步脉冲要求为负脉冲
2、行时序是以”像素”为单位的, 场时序是以”行”为单位的。
3、四个时序参数有特定规范。

VGA各时序参数规定

常用640*480@60hz帧,时钟频率为25Mhz,因此本文以此为例进行VGA显示驱动设计。
a:同步时间,描述同步信号中较短的电平的时间。
b:后沿+左边框
c:有效数据
d:右边框+前沿
e:总像素
在这里插入图片描述
给出一个例子:
在这里插入图片描述
在这里插入图片描述

分析:
行同步时序:一行对应像素总个数:800个像素,其中一行的显示有效数据为640个像素,每一行都有行同步信号,为96个低电平。
场同步时序:屏幕对应行的总数:525行,其中480行为显示有效数据,每行之间都有场同步信号,为2个低电平。

VGA驱动模块设计

1、确定端口
在这里插入图片描述

2、verilog代码编写

注意:简单进行数据测试,直接给输出vga_data = 16’hffff

//******VGA时序控制器*****//
//     640*480@60
//     参数:同步、显示后沿、有效数据、显示前沿、总
//     hs  :96  、 48640   、16     、  800
//     vs  :2   、 33480   、10     、  525
//     有效数据输出16‘hffff,否则16‘b0;
//***********//

module vga_ctrl(
    input vga_clk,
	 input rst_n,
	 
	 output de,  //数据有效信号
	 output hs,  //行同步信号
	 output vs,  //场同步信号
	 output vga_blank,//消隐信号
	 output [15:0] vga_data

);
reg [9:0] h_cnt; //列计数器
reg [9:0] v_cnt; //行计数器

wire [9:0] pix_y; //列坐标
wire [9:0] pix_x; //行坐标

//VGA时序参数定义

parameter HS = 96,
          H_back_proch = 48,
			 H_data = 640,
			 H_front_proch = 16,
			 H_total = 800,
			 
			 VS = 2,
          V_back_proch = 33,
          V_data = 480,
          V_front_proch = 10,
          V_total = 525;

//列计数器

always @ (posedge vga_clk or negedge rst_n)
    if(!rst_n)
	     h_cnt <= 1'b0;
	 else if (h_cnt == (H_total - 1'b1) ) //一行所有像素扫描完成
        h_cnt <= 1'b0;
	 else
	     h_cnt <= h_cnt + 1'b1 ;


//行计数器

always @ (posedge vga_clk or negedge rst_n)
    if(!rst_n)
	     v_cnt <= 1'b0;
	 else if (v_cnt == (V_total - 1'b1) ) //一行所有像素扫描完成
        v_cnt <= 1'b0;
	 else if ( h_cnt == (H_total - 1'b1))
	     v_cnt <= v_cnt + 1'b1 ;
    else
	     v_cnt <= v_cnt ;

// 产生行场同步信号
assign hs = (h_cnt <= ( HS - 1'b1 )) ? 1'b0 : 1'b1;
assign vs = (v_cnt <= ( VS - 1'b1 )) ? 1'b0 : 1'b1;
//消隐信号
assign  vga_blank   =   hs & vs;
//产生数据有效信号
assign de =      ((h_cnt >= ( HS + H_back_proch )) 
              && (h_cnt <= ( HS + H_back_proch + H_data )) 
				  && (v_cnt >= ( VS + V_back_proch )) 
				  && (v_cnt <= ( VS + V_back_proch + V_data )) )
				  ? 1'b1 : 1'b0;

//有效显示区域的行列坐标
assign pix_x = (de == 1'b1 ) ? (v_cnt - (VS + V_back_proch -1'b1)) : 1'b0;
assign pix_y = (de == 1'b1 ) ? (h_cnt - (HS + H_back_proch -1'b1)) : 1'b0;

//有效数据输出
assign vga_data = (de == 1'b1 ) ?  16'hffff : 16'b0;

endmodule

tb测试:

`timescale 1ns/1ns
`define clk_period 20

module vga_ctrl_tb;

    reg vga_clk = 0;
	 reg rst_n ;
	 
	 wire de;  //数据有效信号
	 wire hs;  //行同步信号
	 wire vs; //场同步信号
	 wire vga_blank;//消隐信号
	 wire [15:0] vga_data;


vga_ctrl u1 (
    .vga_clk(vga_clk),
	 .rst_n(rst_n),

	 .de(de),  //数据有效信号
	 .hs(hs),  //行同步信号
	 .vs(vs),  //场同步信号
	 .vga_blank(vga_blank),//消隐信号
	 .vga_data(vga_data)

);

  always#(`clk_period/2)  vga_clk = ~vga_clk;

  initial begin 
     rst_n=1'b0;
	  #(`clk_period*20)   
     rst_n=1'b1;
	  #(`clk_period*500000)
     $stop;
end

endmodule 

波形分析

如下是行场同步以及数据有效显示信号:
可以看到第一行有效数据是从第35行,该行的是144个像素位置的,符合我们的vga时序参数。
在这里插入图片描述

可以看到如下是一幅图像在显示屏上的有效数据的开始结束位置,开始位置:h_cnt = 144,v_cnt = 35;
结束位置:h_cnt = 785,v_cnt = 515;

在这里插入图片描述
补充:关于行场同步信号极性问题

如下可看到有两种行场同步信号,它们高低电平的情况刚好相反,也就是极性不同,有正极性和负极性,信号中高电平时间长,低电平时间短就是负极性,反之就是正极性,上图为负极性,下图为正极性。

在这里插入图片描述
在这里插入图片描述
二者代码的区别:
上图:

assign  hsync = (cnt_h  <=  H_SYNC - 1'd1) ? 1'b0 : 1'b1 ;

下图:

assign  hsync = (cnt_h  <=  H_SYNC - 1'd1) ? 1'b1 : 1'b0  ;

虽然正负极性不同,但最终引脚配置的时候一样

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fighting_FPGA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值