文章目录
- VGA电路原理
- VGA扫描方式
- VGA时序图
- VGA各时序参数规定
- 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 、 48 、 640 、16 、 800
// vs :2 、 33 、 480 、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;elseh_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 ;elsev_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 20module 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;
endendmodule
波形分析
如下是行场同步以及数据有效显示信号:
可以看到第一行有效数据是从第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 ;
虽然正负极性不同,但最终引脚配置的时候一样