FPGA: VGA显示

一、VGA通信协议

VGA(Video Graphics Array)是IBM在1987年随PS/2机一起推出的一种视频传输标准,具有分辨率高、显示速率快、颜色丰富等优点,在彩色显示器领域得到了广泛的应用。不支持热插拔,不支持音频传输。对于一些嵌入式VGA显示系统,可以在不使用VGA显示卡和计算机的情况下,实现VGA图像的显示和控制。VGA显示器具有成本低、结构简单、应用灵活的优点。对于一名FPGA工程师,尤其是视频图像的方向的学习者,VGA协议是必须要掌握的。

1、外部接口

在这里插入图片描述
在这里插入图片描述

由电路图可以看到,VGA并没有特殊的外部芯片,我们需要关注的其实只有5个信号:HS行同步信号,VS场同步信号,R红基色,G绿基色,B蓝基色。

2、色彩原理

三基色是指通过其他颜色的混合无法得到的“基本色”由于人的肉眼有感知红、绿、蓝三种不同颜色的锥体细胞,因此色彩空间通常可以由三种基本色来表达。这是色度学的最基本原理,即三基色原理。三种基色是相互独立的,任何一种基色都不能有其它两种颜色合成。红绿蓝是三基色,这三种颜色合成的颜色范围最为广泛。我们的RGB信号真是三基色的运用,对这三个信号赋予不同的数值,混合起来便是不同的色彩。
在这里插入图片描述

设计RGB信号时,既可以R信号、G信号和B信号独立的赋值,最后连到端口上,也可以直接用RGB当做一个整体信号,RGB信号在使用时的位宽有三种常见格式,以你的VGA解码芯片的配置有关。

1. RGB_8,R:G:B = 3:3:2,即RGB332

2. RGB_16,R:G:B = 5:6:5,即RGB565

3. RGB_24,R:G:B = 8:8:8,即RGB888

3、扫描方式

VGA显示器扫描方式分为逐行扫描和隔行扫描:逐行扫描是扫描从屏幕左上角一点开始,从左像右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,CRT对电子束进行消隐,每行结束时,用行同步信号进行同步;当扫描完所有的行,形成一帧,用场同步信号进行场同步,并使扫描回到屏幕左上方,同时进行场消隐,开始下一帧。隔行扫描是指电子束扫描时每隔一行扫一线,完成一屏后在返回来扫描剩下的线,隔行扫描的显示器闪烁的厉害,会让使用者的眼睛疲劳。因此我们一般都采用逐行扫描的方式。

扫描原理如下所示:
在这里插入图片描述

4、行场信号

行场信号共有 4 种模式,即 hsync 和 vsync 的高低状态不同,如下所示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一开始看这些时序图可能看不懂,它是把行场信号绘制在同一张图里,说明行场信号的控制是相似的,只是时间参数不一样而已。如果展开的话,其实时序是这样的:
在这里插入图片描述
  从VGA的时序图中, 可以看出,帧时序和行时序都产生了四部分,帧时序的四个部分分别是:同步脉冲(o),显示后沿(p),显示时序段(q)和显示前沿(r)。其中同步脉冲(o),显示后沿(p)和显示前沿(r)是消隐区,RGB信号无效,屏幕不显示数据。显示数据段(q)是有效数据区。行时序的四个部分是:同步脉冲(a),显示后沿(b),显示时序段(c)和显示前沿(d)。其中同步脉冲(a),显示后沿(b),显示前沿(d)是消隐区,RGB信号无效,屏幕不显示数据。显示时序段(c)是有效数据区。
在这里插入图片描述
从表中我们可以知道,不同的分辨率,它的时序是不一样的。以800600@60HZ分辨率为例。HSYNC是用来控制“行时序”,HSYNC的a是拉低128个列像素,b是拉高88个列像素,c是拉高800个列像素,而d是拉高40个列像素,e总共有1056个列像素。VSYNC用来控制“帧时序”,VSYNC的o是拉低4个行像素,p是拉高23个行像素,q是拉高600个行像素,而r是拉高1个行像素,s总共有628个行像素。每帧对应628个行周期(628=4+23+600+1),每显示行包括1056点时钟,可知,时钟频率:6281056*60约40MHZ。
如果在一副图片中,那正确的时序表示方式应该如下图这样。
在这里插入图片描述

现在稍稍解释一下这些参数。SYNC是“信号同步”,Back proch和Left border常常加在一起称为“显示后沿”,Addressable video为“显示区域”,Right porder和Front porch常常加在一起称为“显示前沿”,一个时序其实就是先拉高一段较短的“信号同步”时间,然后拉低一段很长的时间,这就是一个回合。同时需要注意,其实也可以完全相反。即先拉低一段时间“信号同步”时间,然后拉高一段很长的时间。

二、实验任务

任务要求:
通过Verilog编程,在640*480@60Hz显示模式,分别实现以下VGA显示,并对照VGA协议信号做时序分析:
1)屏幕上显示彩色条纹;
2)显示自定义的汉字字符(姓名-学号);
3)输出一幅彩色图像。

1、显示彩色条纹

使用开发板:EP4CE115F29C7
将多种显示模式对应帧时序和行时序进行定义,保存在一个单独的文件中。
参数定义文件:vga_para.v


`ifdef vga_640_480
    `define H_Right_Border 8
    `define H_Front_Porch  8
    `define H_Sync_Time    96
    `define H_Back_Porch   40
    `define H_Left_Border  8
    `define H_Data_Time    640
    `define H_Total_Time   800

    `define V_Bottom_Border 8 
    `define V_Front_Porch   2 
    `define V_Sync_Time     2 
    `define V_Back_Porch    25
    `define V_Top_Border    8 
    `define V_Data_Time     480 
    `define V_Total_Time    525

`elsif vga_1920_1080 
    `define H_Right_Border 0
    `define H_Front_Porch  88
    `define H_Sync_Time    44
    `define H_Back_Porch   148
    `define H_Left_Border  0
    `define H_Data_Time    1920
    `define H_Total_Time   2200

    `define V_Bottom_Border 0 
    `define V_Front_Porch   4 
    `define V_Sync_Time     5 
    `define V_Back_Porch    36
    `define V_Top_Border    0 
    `define V_Data_Time     1080 
    `define V_Total_Time    1125

`elsif vga_1024_768 
    `define H_Right_Border 0
    `define H_Front_Porch  24
    `define H_Sync_Time    136
    `define H_Back_Porch   160
    `define H_Left_Border  0
    `define H_Data_Time    1024
    `define H_Total_Time   1344

    `define V_Bottom_Border 0 
    `define V_Front_Porch   3 
    `define V_Sync_Time     6 
    `define V_Back_Porch    29
    `define V_Top_Border    0 
    `define V_Data_Time     768 
    `define V_Total_Time    806
`else 


`endif

顶层文件:

module vga_top(
    input                     clk       ,
    input                     rst_n     ,

    output                    hsync     ,
    output                    vsync     ,
    
    output        [7:0]       vga_r     ,//red
    output        [7:0]       vga_g     ,//green
    output        [7:0]       vga_b     ,//blue   
    output                    vga_clk   ,
    output                    sync      ,
    output                    vga_blk                     
);

wire  [23:0]   data_dis;

wire  [10:0]    h_addr  ;
wire  [10:0]    v_addr  ;

 data_gen    u_data_gen(
    .clk            (clk   	   ),//时钟信号
    .rst_n          (rst_n     ),//复位信号
    .h_addr         (h_addr    ),//数据有效显示区域行地址
    .v_addr         (v_addr    ),//数据有效显示区域场地址
    .data_dis       (data_dis  ) 
                        
);


vga_ctrl u_vga_ctrl(
    .clk                (clk            ),//vga时钟信号 640*480 25.2MHZ
    .rst_n              (rst_n          ),//复位信号
    .data_dis           (data_dis       ),//
    . h_addr            ( h_addr        ),//数据有效显示区域行地址
    . v_addr            ( v_addr        ),//数据有效显示区域场地址
    . hsync             ( hsync         ),
    . vsync             ( vsync         ),
    . vga_r             ( vga_r         ),//red
    . vga_g             ( vga_g         ),//green
    . vga_b             ( vga_b         ),//blue
    . vga_blk           ( vga_blk       ),   
    .sync               (sync           ), 
    . vga_clk           ( vga_clk       )
);
endmodule

vga驱动设计:
(这里的25MHz是在50M时钟的上升沿取值进行分频的,还可以使用IP核PLL进行分频,会在后面的图片显示用IP核方法)
vga_crtl.v:

`define vga_640_480
//`define vga_1920_1080
//`define vga_1024_768

`include "vga_para.v"

module vga_ctrl(
    input                    clk        ,//vga时钟信号 640*480 25.2MHZ
    input                    rst_n      ,//复位信号
    input         [23:0]     data_dis   ,//

    output  reg   [10:0]      h_addr    ,//数据有效显示区域行地址
    output  reg   [10:0]      v_addr    ,//数据有效显示区域场地址
    
    output  reg               hsync     ,
    output  reg               vsync     ,
    
    output  reg   [7:0]       vga_r     ,//red
    output  reg   [7:0]       vga_g     ,//green
    output  reg   [7:0]       vga_b     ,//blue
    output                    vga_blk   ,//VGA消隐信号
    output                    sync      ,
    output                    vga_clk
);
reg     clk_25;
parameter H_SYNC_STA = 1;
parameter H_SYNC_STO = `H_Sync_Time ;
parameter H_DATA_STA = `H_Left_Border + `H_Back_Porch + `H_Sync_Time ;
parameter H_DATA_STO = `H_Left_Border + `H_Back_Porch + `H_Sync_Time + `H_Data_Time ;

parameter V_SYNC_STA = 1;
parameter V_SYNC_STO = `V_Sync_Time;
parameter V_DATA_STA = `V_Top_Border + `V_Back_Porch + `V_Sync_Time ;
parameter V_DATA_STO = `V_Top_Border + `V_Back_Porch + `V_Sync_Time + `V_Data_Time ;

reg     [11:0]   cnt_h_addr        ;//行地址计数器
wire             add_h_addr        ;
wire             end_h_addr        ;

reg     [11:0]   cnt_v_addr        ;//场地址计数器
wire             add_v_addr        ;
wire             end_v_addr        ;

 assign sync = 1'b0;

 //分频25MHz
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        clk_25 <= 0;
    end
   else begin
       clk_25 <= ~clk_25;
   end
end
 assign vga_clk = clk_25;
//计数器
always@(posedge vga_clk or negedge rst_n)begin
   if(!rst_n)begin
      cnt_h_addr <= 12'd0;
   end
   else if(add_h_addr) begin
      if(end_h_addr)begin
         cnt_h_addr <= 12'd0;
      end
      else begin
         cnt_h_addr <= cnt_h_addr + 12'd1;
      end
   end
   else begin
       cnt_h_addr <= 12'd0;
   end
end

assign add_h_addr = 1'b1 ;//开启条件
assign end_h_addr = add_h_addr && cnt_h_addr >= `H_Total_Time - 1;//结束条件
assign vga_blk = ~((cnt_h_addr<(`H_Front_Porch+`H_Sync_Time+`H_Back_Porch))||(cnt_v_addr<`V_Front_Porch+`V_Sync_Time+`V_Back_Porch));

//计数器
always@(posedge vga_clk or negedge rst_n)begin
   if(!rst_n)begin
      cnt_v_addr <= 12'd0;
   end
   else if(add_v_addr) begin
      if(end_v_addr)begin
         cnt_v_addr <= 12'd0;
      end
      else begin
         cnt_v_addr <= cnt_v_addr + 12'd1;
      end
   end
   else 
  • 1
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值