VGA接口

一、简介

VGAVideo Graphics Array)即视频图形阵列,是IBM1987年推出的使用模拟信号的一种视频传输标准,在当时具有分辨率高、显示速率快、颜色丰富等优点,在彩色显示器领域得到了广泛的应用。这个标准对于现今的个人电脑市场已经十分过时。即使如此,VGA仍然是最多制造商所共同支持的一个标准,个人电脑在加载自己的特殊驱动程序之前,都必须支持VGA的标准。

1. 不支持热插拔

VGA接口跟HDMI接口一样是不支持热插拔的。热插拔是指一般带电状态下对于接插件的插入或是拔除,并不只是针对有电源接口或者带供电的接口的接插件,而是所有。在运行状态时,插拔会产生耦合电流,电流不稳造成硬件烧坏,导致笔记本的接口端的保护受到冲击。就像U盘不能再一个时间段多次在一个端口插拔使用一样。各种电器的外露端子都会有金属的部分,它们都是要求接地的,但是不同的电器之间的地并不一定相同,比如一台DVD的地和一台电视机的接地都是相对于本身系统而言。

当端子插入时,首先要建立共同的地来对传输的信号作参考,这就要依靠端子和传输线上的金属部分了,金属部分接地同时也是对信号的屏蔽和保护。两个地相接触一瞬间,会有很高的尖峰脉冲产生,这种脉冲如果不加以滤除可能会直达芯片并将其损坏。另外还有一种是ESD,即静电损坏,这种更难以避免,因为在电子产品上,只能去防护,ESD的持续时间会更短US级别。所以正规的电子产品对于金属端子的接地有比较高的要求,同时在信号线上增加ESD防护器件来避免热插拔的损坏。但实际上很多厂家为了节省成本而偷工减料,或者是对热插拔的防护意识不够导致设计不合理,使得用户会出现热插拔损坏电器的现象产生。

2.  VGA不能传输音频

因为视频是VGA信号,而音频信号不是,所以VGA不能传输音频,只能传输视频。相信这就是为什么这几年极度的需求创新转换器的原因。VGA不支持音频传输也是给很多消费者带来烦恼,这最好的办法其实就是购买一款转换器,VGAHDMI或者HDMIVGA,达到视频传输的同时还支持音频信号的输出,一举两得。但是不要只想着转换器的输入与输出成问题,同时想想音频输出口,3.5mm是音频输出信号的重要连接线。购买时可以考虑想转换器有没有带3.5mm的音频输出口,然后另外购买一条音频线。

二、接口

VGA接口是一种D型接口,上面共有15针孔,分成三排,每排五个。 其中比较重要的是3RGB彩色分量信号和2根扫描同步信号HSYNCVSYNC针。其引脚编号图如下图所示:

其中每个管脚的详细定义如下表所示

管脚

名称

定义

1

RED

红基色(75Ω,0.7Vp-p)

2

GREEN

绿基色(75Ω,0.7Vp-p)

3

BLUE

蓝基色(75Ω,0.7Vp-p)

4

ID2

地址码(显示器标识位2)

5

GND

6

RGND

红色地

7

GGND

绿色地

8

BGND

蓝色地

9

KEY

保留

10

SGND

同步信号地

11

ID0

地址码(显示器标识位0)

12

ID1

地址码(显示器标识位1)

13

HSYNC

行同步信号

14

VSYNC

场同步信号

15

ID3

地址码(显示器标识位3)

由于VGA是模拟接口,所以需要把8位的红绿蓝像素信号转化为峰峰值0.7V的电压大小信号进行输出 

常见的VGA分辨率如下图所示

三、时序

1. 极性为positive

VGA显示主要取决于RGB三原色。根据RGB位宽的不同,VGA显示的效果也不同,常见有24bitR/G/B8bit)、16bitR 5bitG 6bitB 6bit)、12bitR/G/B4bit)。

对于分辨率600×400@60Hz,该参数显示的是VGA有效数据参数,即VGA显示刷新率60帧,每帧600×400个像素点。VGA接口实际传输的数据比此数值要大。

VGA显示器逐行扫描原理:逐行扫描是扫描从屏幕左上角一点开始,从左至右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置。在这期间,CRT对电子束进行消隐,每行结束时,用行同步信号HS进行同步;当扫描完所有的行,形成一帧,用场同步信号VS进行帧同步,并使扫描回到屏幕左上方,同时进行场消隐,开始下一帧。

从逐行扫描的原理中,我们可以了解,当一行扫描完成后,切换到下一行时,需要行同步信号及行消隐时间;当一帧扫描完成后,切换到下一帧时,需要场同步信号及场消隐时间,这样就形成了VGA扫描时序,如下图所示:

Sync为同步信号,Back Porch为显示后肩时间,Active Video为有效视频,Front Porch为显示前肩时间。Sync+Back Porch+Active Video+Front Porch构成一个完整行周期。

下图定义了一帧图像的时序定义。图中场信号的定义与行信号定义类似。行信号以像素点为单位,场信号以行为单位。VGA图像时序的编写即是对图4中各个时序段进行定时。

下面以1920×1080@60Hz分辨率介绍VGA时序定义。在VESA表准中给出了该分辨率的参数定义,如下图所示

在图5中,需要注意蓝色标记的HS/VS信号极性,这在VESA标准中有规定要求,图3只是给出了其中一种HS/VS信号极性,其他3种请参考VESA标准。

2. 极性为negetive

在VGA视频传输标准中,视频图像被分解为红、绿、蓝三色信号,在行同步(HSYNC)和场同步(VSYNC)信号的同步下分别在三个独立通道传输。VGA在传输过程中的同步时序分为行时序和场时序,如下图所示。

从上面两幅图中我们可以看到VGA传输过程中的行同步时序和场同步时序非常类似,一行或一场(又称一帧)数据都分为四个部分:低电平同步脉冲(消隐期,相当于还原扫描坐标)、显示后沿(消隐后肩,相当于准备开始扫描)、有效数据段(行显示期,扫描中,数据有效区域)以及显示前沿(行消隐前肩,完成扫描,相当于准备同步)

行同步信号HSYNC在一个行扫描周期中完成一行图像的显示,其中在a段维持一段时间的低电平用于数据同步,其余时间拉高;在有效数据期间(c段),红绿蓝三原色数据通道上输出一行图像信号,其余时间数据无效。

与之类似,场同步信号在在一个场扫描周期中完成一帧图像的显示,不同的是行扫描周期的基本单位是像素点时钟,即完成一个像素点显示所需要的时间;而场扫描周期的基本单位是完成一行图像显示所需要的时间。

根据分辨率和刷新速率的不同,VGA又分为640*480@75、800*600@60等。不同的VGA显示时序是类似的,仅存在参数上的差异,如下图所示。

(由上图明显可直接读出完成一行需要的行同步时钟周期数e和完成一帧需要的列同步时钟数s)

有效显示区域效果如下:

(像素)时钟VGA_CLK计算公式:HS_total×VS_total×fps

其中,HS_total为VGA_HS信号的一个周期内包含的VGA_CLK信号周期个数,即行周期数;VS_total为VGA_VS信号的一个周期内包含的VGA_HS信号周期个数,即列周期数;fps为每秒刷新率。分辨率640*480p@60Hz时,HS_total为800,VS_total为525, fps为60。

因此VGA_CLK信号的频率为800×525×60Hz=25.2MHz。

(以RGB888、640*480p@60Hz为例,有效区域内每个VGA_CLK输出一个像素数据(24位并行);每个VGA_HS包含800个VGA_CLK周期,显示一行的像素数据;每个VGA_VS包含525个VGA_CLK周期,显示一帧的像素数据)

三、总结

以640*480为例,如上图所示。其中行同步的数字是像素时钟的周期,列同步的数字是行同步的周期数。

在行同步和列同步都为高电平的有效时间段,每个像素时钟周期传输颜色数据(3*8bit的数字信号转化为3个电压大小信号,最终输出3个模拟信号)。

四、代码实现

module vga_driver
#(
    parameter       RED_WIDTH       =    8   , // VGA红色分量位宽
                    BLUE_WIDTH      =    8   , // VGA绿色分量位宽
                    GREEN_WIDTH     =    8   , // VGA蓝色分量位宽
                    PIXEL           =   25     // 代表时钟是25MHz,那么分辨率就是640*480@60 
)
(
    input                             clk       , // 分辨率为640*480时,时钟为25.175MHz,近似使用25M即可
    input                             rstn      , // 系统复位
/*
    input                             data      , // 要显示的内容数据
*/
    output reg [   RED_WIDTH - 1 : 0] red       , // VGA红色分量
    output reg [ GREEN_WIDTH - 1 : 0] green     , // VGA绿色分量
    output reg [  BLUE_WIDTH - 1 : 0] blue      , // VGA蓝色分量
    output                            hs        , // VGA行同步信号
    output                            vs          // VGA场同步信号
);

// 行时序各个参数定义
reg         [7:0]   h_sync_pulse     ;  // 行低电平同步脉冲(消隐期,相当于还原扫描坐标)
reg         [7:0]   h_back_porch     ;  // 行显示后沿(消隐后肩,相当于准备开始扫描)
reg        [10:0]   h_active_time    ;  // 行有效数据段(行显示期,扫描中,数据有效区域)
reg         [6:0]   h_front_porch    ;  // 行显示前沿(行消隐前肩,完成扫描,相当于准备同步)
reg        [10:0]   h_line_total     ;  // 一行所需时钟周期总数

//场时序各个参数定义
reg         [2:0]   v_sync_pulse     ;  // 场低电平同步脉冲(消隐期,相当于还原扫描坐标)
reg         [5:0]   v_back_porch     ;  // 场显示后沿(消隐后肩,相当于准备开始扫描)
reg        [10:0]   v_active_time    ;  // 场有效数据段(行显示期,扫描中,数据有效区域)
reg         [3:0]   v_front_porch    ;  // 场显示前沿(行消隐前肩,完成扫描,相当于准备同步)
reg        [10:0]   v_frame_total    ;  // 一场所需时钟周期总数


// 分辨率为640*480时行时序各个参数定义
parameter           H_SYNC_PULSE_640    =   96  ,
                    H_BACK_PORCH_640    =   48  ,
                    H_ACTIVE_TIME_640   =   640 ,
                    H_FRONT_PORCH_640   =   16  ,
                    H_LINE_TOTAL_640    =   800 ;

// 分辨率为640*480时场时序各个参数定义
parameter           V_SYNC_PULSE_480    =   2   ,
                    V_BACK_PORCH_480    =   33  ,
                    V_ACTIVE_TIME_480   =   480 ,
                    V_FRONT_PORCH_480   =   10  ,
                    V_FRAME_TOTAL_480   =   525 ;           


reg     [11:0]      hs_cnt           ;  // 行时序计数器(根据H_LINE_TOTAL决定)
reg     [11:0]      vs_cnt           ;  // 场时序计数器(根据V_FRAME_TOTAL决定)

wire                active_flag      ;  // 激活标志,当这个信号为1时RGB的数据可以显示在屏幕上
wire     [8:0]      color_bar_width  ;  // 测试8个纯色列的宽度

//
// 功能:设置分辨率
//
always @(posedge clk or negedge rstn) begin
    if(!rstn) begin
        h_sync_pulse     <=    8'b0   ;
        h_back_porch     <=    8'b0   ;
        h_active_time    <=   11'b0   ;
        h_front_porch    <=    7'b0   ;
        h_line_total     <=   11'b0   ;
        v_sync_pulse     <=    3'b0   ;
        v_back_porch     <=    6'b0   ;
        v_active_time    <=   11'b0   ;
        v_front_porch    <=    4'b0   ;
        v_frame_total    <=   11'b0   ;
    end
    else begin
        case(PIXEL)
            25: begin
                h_sync_pulse     <=   H_SYNC_PULSE_640    ;
                h_back_porch     <=   H_BACK_PORCH_640    ;
                h_active_time    <=   H_ACTIVE_TIME_640   ;
                h_front_porch    <=   H_FRONT_PORCH_640   ;
                h_line_total     <=   H_LINE_TOTAL_640    ;
                v_sync_pulse     <=   V_SYNC_PULSE_480    ;
                v_back_porch     <=   V_BACK_PORCH_480    ;
                v_active_time    <=   V_ACTIVE_TIME_480   ;
                v_front_porch    <=   V_FRONT_PORCH_480   ;
                v_frame_total    <=   V_FRAME_TOTAL_480   ;
            end
            default: begin
                h_sync_pulse     <=    8'b0   ;
                h_back_porch     <=    8'b0   ;
                h_active_time    <=   11'b0   ;
                h_front_porch    <=    7'b0   ;
                h_line_total     <=   11'b0   ;
                v_sync_pulse     <=    3'b0   ;
                v_back_porch     <=    6'b0   ;
                v_active_time    <=   11'b0   ;
                v_front_porch    <=    4'b0   ;
                v_frame_total    <=   11'b0   ;
            end
        endcase
    end
end


//
// 功能:产生行时序
//
always @(posedge clk or negedge rstn) begin
    if(!rstn)
        hs_cnt    <=  12'b0   ;
    else if(hs_cnt == h_line_total - 1'b1)
        hs_cnt    <=  12'b0   ;
    else
        hs_cnt    <=  hs_cnt + 1'b1  ;
end

assign hs =   (hs_cnt < h_sync_pulse) ? 1'b0 : 1'b1    ;

//
// 功能:产生场时序
//
always @(posedge clk or negedge rstn) begin
    if(!rstn)
        vs_cnt <=  12'd0   ;
    else if(vs_cnt == v_frame_total - 1'b1)
        vs_cnt <=  12'd0   ;
    else if(hs_cnt == h_line_total - 1'b1)
        vs_cnt <=  vs_cnt + 1'b1  ;
    else
        vs_cnt <=  vs_cnt  ;
end

assign vs =   (vs_cnt < v_sync_pulse) ? 1'b0 : 1'b1    ;

//
// 时序计数器必须在有效数据段范围内才可以显示
//
assign active_flag =  (hs_cnt >= (h_sync_pulse + h_back_porch                 )) &&
                      (hs_cnt <= (h_sync_pulse + h_back_porch + h_active_time )) &&
                      (vs_cnt >= (v_sync_pulse + v_back_porch                 )) &&
                      (vs_cnt <= (v_sync_pulse + v_back_porch + v_active_time )) ;


//
// 功能:把显示器屏幕分成8个纵列,显示8种颜色,每个纵列的宽度相等
//
assign color_bar_width =   h_active_time >> 3  ;

always @(posedge clk or negedge rstn) begin
    if(!rstn)
        begin
            red   <=   {RED_WIDTH{1'b0}}    ;
            green <= {GREEN_WIDTH{1'b0}}    ;
            blue  <=  {BLUE_WIDTH{1'b0}}    ;
        end
    else if(active_flag)
        begin
            if(hs_cnt < (h_sync_pulse + h_back_porch + color_bar_width)) // 红色彩条
                begin
                    red   <=  ~  {RED_WIDTH{1'b0}}    ; // 红色彩条把红色分量全部给1,绿色和蓝色给0
                    green <=   {GREEN_WIDTH{1'b0}}    ;
                    blue  <=    {BLUE_WIDTH{1'b0}}    ;
                end
            else if(hs_cnt < (h_sync_pulse + h_back_porch + color_bar_width*2)) // 绿色彩条
                begin
                    red   <=     {RED_WIDTH{1'b0}}    ;
                    green <=  ~{GREEN_WIDTH{1'b0}}    ; // 绿色彩条把绿色分量全部给1,红色和蓝色分量给0
                    blue  <=    {BLUE_WIDTH{1'b0}}    ;
                end
            else if(hs_cnt < (h_sync_pulse + h_back_porch + color_bar_width*3)) // 蓝色彩条
                begin
                    red   <=     {RED_WIDTH{1'b0}}    ;
                    green <=   {GREEN_WIDTH{1'b0}}    ;
                    blue  <=  ~ {BLUE_WIDTH{1'b0}}    ; // 蓝色彩条把蓝色分量全部给1,红色和绿分量给0
                end
            else if(hs_cnt < (h_sync_pulse + h_back_porch + color_bar_width*4)) // 白色彩条
                begin
                    red   <=  ~  {RED_WIDTH{1'b0}}    ; // 白色彩条是有红绿蓝三基色混合而成
                    green <=  ~{GREEN_WIDTH{1'b0}}    ; // 所以白色彩条要把红绿蓝三个分量全部给1
                    blue  <=  ~ {BLUE_WIDTH{1'b0}}    ;
                end
            else if(hs_cnt < (h_sync_pulse + h_back_porch + color_bar_width*5)) // 黑色彩条
                begin
                    red   <=     {RED_WIDTH{1'b0}}    ; // 黑色彩条就是把红绿蓝所有分量全部给0
                    green <=   {GREEN_WIDTH{1'b0}}    ;
                    blue  <=    {BLUE_WIDTH{1'b0}}    ;
                end
            else if(hs_cnt < (h_sync_pulse + h_back_porch + color_bar_width*6)) // 黄色彩条
                begin
                    red   <=  ~  {RED_WIDTH{1'b0}}    ; // 黄色彩条是有红绿两种颜色混合而成
                    green <=  ~{GREEN_WIDTH{1'b0}}    ; // 所以黄色彩条要把红绿两个分量给1
                    blue  <=    {BLUE_WIDTH{1'b0}}    ; // 蓝色分量给0
                end
            else if(hs_cnt < (h_sync_pulse + h_back_porch + color_bar_width*7)) // 紫色彩条
                begin
                    red   <=  ~  {RED_WIDTH{1'b0}}    ; // 紫色彩条是有红蓝两种颜色混合而成
                    green <=   {GREEN_WIDTH{1'b0}}    ; // 所以紫色彩条要把红蓝两个分量给1
                    blue  <=  ~ {BLUE_WIDTH{1'b0}}    ; // 绿色分量给0
                end
            else                                                                  // 青色彩条
                begin
                    red   <=     {RED_WIDTH{1'b0}}    ; // 青色彩条是由蓝绿两种颜色混合而成
                    green <=  ~{GREEN_WIDTH{1'b0}}    ; // 所以青色彩条要把蓝绿两个分量给1
                    blue  <=  ~ {BLUE_WIDTH{1'b0}}    ; // 红色分量给0
                end
        end
    else
        begin
            red   <=    {RED_WIDTH{1'b0}}   ;
            green <=  {GREEN_WIDTH{1'b0}}   ;
            blue  <=   {BLUE_WIDTH{1'b0}}   ;
        end
end

endmodule

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值