FPGA—VGA 显示器驱动设计与验证(附代码)

目录

1.学理论

2.实操

2.1 顶层设计

2.1.1 模块框图

2.1.2 代码编写 

2.1.3 仿真验证

2.2 时钟生成模块

2.3 VGA时序控制模块

2.3.1 模块框图

2.3.2 波形图绘制

2.3.3 代码编写

2.3.4 仿真验证

2.4 图像数据生成模块

2.4.1 模块框图

2.4.2 波形图绘制

2.4.3 代码编写

3.总结

1.学理论

VGA简介

       图像显示设备在日常生活中随处可见,例如家庭电视机、计算机显示屏幕等,这些设备能够显示数据图像信息,归功于视频传输接口。常见的视频传输接口有三种: VGA 接口、 DVI 接口和 HDMI 接口

       VGA,英文全称“Video Graphics Array” ,译为视频图形阵列,是一种使用模拟信号进行视频传输的标准协议。由于 VGA 接口体积较大,在笔记本电脑领域, VGA 接口已被逐渐淘汰,但台式机并没有.

VGA接口及引脚定义

     在最初的应用中, VGA 接口常用于计算机与 VGA 显示器之间的图像传输,在台式计算机、旧式笔记本电脑和 VGA 显示器上一般会有标准的 VGA 接口。VGA 接口和VGA 连接线见下图。

       由下图可知VGA 接口共有 15 个引脚,分为 3 排,每排各 5 个, 按照自上而下、从左向右的顺序排列。 其中第一排的引脚 1、 2、 3 和第三排的引脚 13、 14 最为重要。

 下表为各引脚的说明。

      VGA 使用工业界通用的 RGB 色彩模式作为色彩显示标准,这种色彩显示标准是根据三原色中红色、绿色、蓝色所占比例多少及三原色之间的相互叠加得到各式各样的颜色。
      引脚 1 红基色(RED)、引脚 2 绿基色(GREEN)、引脚 3 蓝基色(BLUE)就是 VGA 接口中负责传输三原色的传输通道。要注意的是,这 3 个引脚传输的是模拟信号。

      引脚 13 行同步信号(HSYNC)、引脚 14 场同步信号(VSYNC),这两个信号,是在 VGA显示图像时,负责同步图像色彩信息的同步信号。

      其他引脚,不重要不介绍。

VGA显示原理

      VGA 显示器显示图像,而是采用扫描的方式,将构成图像的像素点,在行同步信号和场同步信号的同步下,按照从上到下、 由左到右的顺序扫描到显示屏上。 VGA 显示器扫描方式,具体见图 下图。

       结合 VGA 显示器扫描方式示意图,简要说明一下其扫描规律:

      (1) 在行、场同步信号的同步作用下,扫描坐标定位到左上角第一个像素点坐标;
      (2) 自左上角(第一行)第一个像素点坐标,逐个像素点向右扫描(图中第一个水平方向箭头);
      (3) 扫描到第一行最后一个数据,一行图像扫描完成,进行图像消隐,扫描坐标自第一行行尾转移到第二行行首(图中第一条虚线);
      (4) 重复若干次扫描至最后一行行尾,一帧图像扫描完成,进行图像消隐,扫描坐标跳转回到左上角第一行行首(图中对角线箭头),开始下一帧图像的扫描。

VGA时序标准

 VESA VGA 时序标准图

       由 VESA VGA 时序标准图可知, VGA 时序由行同步时序与场同步时序两部分构成,为了方便理解,将其分开讲解。

a. 行同步时序

      上图中 Video 代表传输的图像信息, HSync 表示行同步信号。 HSync 自上升沿起到下一个上升沿止为一个完整周期,称为行扫描周期
       一个完整的行扫描周期,包含 6 部分: Sync(同步) 、 Back Porch(后沿) 、 Left Border(左边框) 、 “Addressable” Video(有效图像)、 Right Border(右边框)、 Front Porch(前沿) ,这 6 部分的基本单位是 pixel(像素),即一个像素时钟周期。
       在一个完整的行扫描周期中,Video 图像信息在 HSync 行同步信号的同步下完成一行图像的扫描显示, Video 图像信息只有在“Addressable” Video(有效图像) 阶段,图像信息有效,其他阶段图像信息无效。
        HSync 行同步信号在 Sync(同步) 阶段,维持高电平,其他阶段均保持低电平,在下一个行扫描周期的 Sync(同步) 阶段,HSync 行扫描信号会再次拉高,其他阶段拉低,周而复始。
b.  场同步时序

       理解了行同步时序,两者相类似。如上图示,图中Video 代表传输的图像信息, VSync 表示场同步信号, VSync 自上升沿起到下一个上升沿止为一个完整周期,称为场扫描周期。

       周期组成相同,与行同步信号不同的是,这 6 部分的基本单位是 line(行),即一个完整的行扫描周期。

      综上所述,将行同步时序图与场同步时序图结合起来就构成了 VGA 时序图,具体见下图 。

       图中的红色区域表示在一个完整的行扫描周期中,Video 图像信息只在此区域有效,黄色区域表示在一个完整的场扫描周期中,Video 图像信息只在此区域有效,两者相交的橙色区域, 就是 VGA 图像的最终显示区域。

VGA显示模式及相关参数

       行同步时序可分为 6 个阶段,对于这 6 个阶段的参数是有严格定义的,参数配置不正确VGA 不能正常显示。 VGA 显示器可支持多种分辨率,不同分辨率对应个阶段的参数是不同的,常用 VGA 分辨率时序参数,见下图。

      以经典 VGA 显示模式 640x480@60 为例,讲解一下 VGA 显示的相关参数。

      (1) 显示模式: 640x480@60

      640x480 是指 VGA 的分辨率, 640 是指有效显示图像每一行有 640 个像素点, 480 是指每一帧图像有 480 行, 640 * 480 = 307200 ≈ 300000,每一帧图片包含约 30 万个像素点,之前某品牌手机广告上所说的 30 万像素指的就是这个; @60 是指 VGA 显示图像的刷新频率, 60 就是指 VGA 显示器每秒刷新图像 60 次,即每秒钟需要显示 60 帧图像。
      (2) 时钟(MHz): 25.175MHz

      VGA 显示的工作时钟,像素点扫描频率。
      (3) 行同步信号时序(像素)、 场同步信号时序(行数)

      行同步信号时序分为 6 段, Sync(同步) 、 Back Porch(后沿) 、 Left Border(左边框) 、 “Addressable” Video(有效图像)、 Right Border(右边框)、 Front Porch(前沿) ,这 6 段构成一个行扫描周期,单位为像素时钟周期。
      同步阶段,参数为 96,指在行时序的同步阶段,行同步信号需要保持 96 个像素时钟周期的高电平, 其他几个阶段与此相似。场同步信号时序与其类似,只是单位不再是像素时钟周期,不介绍。

时钟频率的计算方法:

显示模式 640x480@60

行扫描周期 = 800(像素)场扫描周期 X 525(行扫描周期) X 刷新频率

    800 * 525 * 60 = 25,200,000 ≈ 25.175MHz (误差忽略不计)

   注意图像无效阶段的扫描也花费了扫描时间要使用行扫描周期和场扫描周期的参数进行计
算,不能使用有效图像的参数进行计算。

2.实操

      编写 VGA 驱动, 使用 FPGA 开发板驱动 VGA 显示器显示十色等宽彩条(没有显示屏使用仿真波形验证),VGA 显示模式为 640x480@60。

VGA 彩条实验效果图

硬件资源:

       VGA 只能识别模拟信号,而 FPGA 输出的图像信息为数字信号,故需要数模转换。常见有以下两种方法,一,使用专业的转换芯片,如常用的转换芯片 AD7123,这种方式更为稳定,但成本稍高;二,使用权电阻网络实现数模转换,优点有效降低成本,该板就是使用的第二种方法。 

  VGA 部分原理图

      由图可知,踏浪 Pro 使用的 RGB565 图像模式,位宽为 16bit,高 5 位表示红色,低 5位表示蓝色,中间 6 位表示绿色。根据位宽不同, RGB 图形格式还包括 RGB232、RGB888 等,数据位宽越大,表示颜色种类越多,显示图像越细腻。

      VGA_D[15:0]表示 FPGA 传入权电阻网络的数字图像信号,经过权电阻网络的数模转换,生成能够被 VGA 识别的模拟图像信号 VGA_R、 VGA_G、 VGA_B。
       这三路模拟信号的电压范围为 0V ~ 0.714V, 0V 代表无色, 0.714V 代表满色,电压高低由输入的数字信号决定。输入的 R、 G、 B 数字信号不同,输出的三原色红、绿、蓝电压不同,颜色深浅不同,三原色相结合可以产生多种颜色。

2.1 顶层设计

2.1.1 模块框图

       由实验目标可以确认我们需要完成按照vga显示时序,传输显示数据(内容),故将模块划分为以下三个部分。

 VGA 彩条显示实验整体框图

      本实验工程包括 4 个模块,各模块功能如下:

       为了实现模块的的复用性(可以兼容显示其他图案),没有将vga_pic划到vga_ctrl模块中。

2.1.2 代码编写 

`timescale  1ns/1ns


module  vga_colorbar
(
    input   wire            sys_clk     ,   
    input   wire            sys_rst_n   ,   

    output  wire            hsync       ,   //行同步信号
    output  wire            vsync       ,   //场同步信号
    output  wire    [15:0]  rgb             //像素信息
);

//wire define
wire            vga_clk ;   
wire            locked  ;   
wire            rst_n   ;   
wire    [9:0]   pix_x   ;   //有效显示区域X轴坐标
wire    [9:0]   pix_y   ;   //有效显示区域Y轴坐标
wire    [15:0]  pix_data;   //像素点色彩信息
assign  rst_n = (sys_rst_n & locked);

clk_gen clk_gen_inst
(
    .RESET      (~sys_rst_n ), 
    .CLK_IN1    (sys_clk    ), 

    .CLK_OUT1   (vga_clk    ), 
    .LOCKED     (locked     )  
);
vga_ctrl  vga_ctrl_inst
(
    .vga_clk    (vga_clk    ), 
    .sys_rst_n  (rst_n      ), 
    .pix_data   (pix_data   ), 

    .pix_x      (pix_x      ), 
    .pix_y      (pix_y      ), 
    .hsync      (hsync      ), 
    .vsync      (vsync      ), 
    .rgb        (rgb        )  
);
vga_pic vga_pic_inst
(
    .vga_clk    (vga_clk    ),  
    .sys_rst_n  (rst_n      ),  
    .pix_x      (pix_x      ),  //像素点X轴坐标,10bit
    .pix_y      (pix_y      ),  //像素点Y轴坐标

    .pix_data   (pix_data   )   //输出像素点色彩信息,16bit

);

endmodule

2.1.3 仿真验证

`timescale  1ns/1ns

module  tb_vga_colorbar();

wire            hsync       ;
wire    [15:0]  rgb         ;
wire            vsync       ;

//reg   define
reg             sys_clk     ;
reg             sys_rst_n   ;

initial
    begin
        sys_clk     =   1'b1;
        sys_rst_n   <=  1'b0;
        #200
        sys_rst_n   <=  1'b1;
    end

//sys_clk:产生时钟
always  #10 sys_clk = ~sys_clk  ;


vga_colorbar    vga_colorbar_inst
(
    .sys_clk    (sys_clk    ),  
    .sys_rst_n  (sys_rst_n  ),  

    .hsync      (hsync      ),  
    .vsync      (vsync      ),  
    .rgb        (rgb        )   
);

endmodule

       上板验证则VGA 显示器显示出十色彩条。

2.2 时钟生成模块

        时钟生成模块将 50MHz 晶振时钟分频为25MHz(理论是25.175Mhz,方便时钟生成) 的 VGA 工作时钟。

 实现时钟分频有两种方法

         一是使用 IP 核,可通过配置相关参数分频或倍频产生多种频率的时钟信号;

          二是编写逻辑代码实现时钟分频(思路:在系统时钟上升沿到来时,cga_clk取反即可)。

         为了练习调用ip核,本模块采用第一种方法。时钟生成模块框图见下图。

       要注意两点,该 复位信号RESET 为高电平有效,故将系统复位信号 sys_rst_n 取反输入。

        另外,时钟锁定信号LOCKED,作为输出稳定时钟信号的指示信号,因为 IP 核分频产生的分频时钟在信号初始位置会出现震荡(无效信号),在时钟震荡过程中, LOCKED 保持低电平;震荡结束,分频时钟稳定后, LOCKED 信号赋值高电平, 表示输出时钟稳定且可被其他模块使用。故为了屏蔽前期的震荡,使用一个与门连接。具体连接方式见整体框图。在震荡期间,此时的 LOCKED 信号为低电平,与门输出也为低电平,复位信号处于低电平使能,使得vga_pic与vga_ctrl 模块不工作,从而屏蔽了前期的震荡。

       具体如何生成pll ip核之前文章有讲,这里不再说明。

2.3 VGA时序控制模块

2.3.1 模块框图

       VGA 时序控制模块,作用是驱动 VGA 显示器,将输入模块的彩条图形像素点信息,按照 VGA 时序扫描显示到 VGA 显示器上。模块框图见下图。  

VGA 时序控制模块输入输出信号信号功能描述:                   

2.3.2 波形图绘制

1. 行同步信号(hsync)、场同步信号(vsync)的波形绘制思路

       由时序图可知,行同步信号(hsync)、场同步信号(vsync)是由时序而来是周期信号,生成此信号可以利用计数器以像素时钟周期进行计数,每一个像素时钟周期自加1,计数范围为 0-799,共计数 800 次,与完整行扫描周期数相吻合。场同步信号与此类似只是计数周期单位为一个行扫描周期。

2. 图像显示有效信号(rgb_valid)波形绘制思路
       VGA 只有在有效的显示区域内送入图像数据,图像才会被正确显示,所以这里声明一个有效信号rgb_valid。前面提到的cnt_h、 cnt_v 两个计数器,以其为约束条件,当两个计数器计数到图像有效显示区域时, rgb_valid 赋值高电平,否则赋值低电平。

 3. 图像信息请求信号 (pix_data_req)、 VGA 有效显示区域像素点坐标 (pix_x,pix_y) 波形绘制思路

       针对不同坐标点对 pix_data 进行赋值,声明 VGA 有效显示区域像素点坐标(pix_x,pix_y)。由于图像数据生成模块 vga_pic 需要以坐标(pix_x,pix_y)为约束条件对 pix_data 信号进行赋值,只能使用时序逻辑的赋值方式,那么pix_data 的赋值时刻会滞后条件满足时刻一个时钟周期,所以这里我们声明新的图像数据请求信号 pix_data_req,不在使用图像显示有效信号(rgb_valid)。具体波形如下图。

 4. RGB 色彩信息(rgb)波形绘制思路

       生成信号 rgb 在 rgb_valid 高电平时写入正确图像数据。

2.3.3 代码编写

`timescale  1ns/1ns

module  vga_ctrl
(
    input   wire            vga_clk     ,   
    input   wire            sys_rst_n   ,   
    input   wire    [15:0]  pix_data    ,   //输入像素点色彩信息

    output  wire    [9:0]   pix_x       ,   //输出VGA有效显示区域像素点X轴坐标
    output  wire    [9:0]   pix_y       ,   //输出VGA有效显示区域像素点Y轴坐标
    output  wire            hsync       ,   //输出行同步信号
    output  wire            vsync       ,   //输出场同步信号
    output  wire    [15:0]  rgb             //输出像素点色彩信息
);

parameter H_SYNC    =   10'd96  ,   //行同步
          H_BACK    =   10'd40  ,   //行时序后沿
          H_LEFT    =   10'd8   ,   //行时序左边框
          H_VALID   =   10'd640 ,   //行有效数据
          H_RIGHT   =   10'd8   ,   //行时序右边框
          H_FRONT   =   10'd8   ,   //行时序前沿
          H_TOTAL   =   10'd800 ;   //行扫描周期
parameter V_SYNC    =   10'd2   ,   //场同步
          V_BACK    =   10'd25  ,   
          V_TOP     =   10'd8   ,   
          V_VALID   =   10'd480 ,   
          V_BOTTOM  =   10'd8   ,   
          V_FRONT   =   10'd2   ,   
          V_TOTAL   =   10'd525 ;   

wire            rgb_valid       ;   //VGA有效显示区域
wire            pix_data_req    ;   //像素点色彩信息请求信号

reg     [9:0]   cnt_h           ;   //行同步信号计数器
reg     [9:0]   cnt_v           ;   //场同步信号计数器

//cnt_h:行同步信号计数器
always@(posedge vga_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_h   <=  10'd0   ;
    else    if(cnt_h == H_TOTAL - 1'd1)
        cnt_h   <=  10'd0   ;
    else
        cnt_h   <=  cnt_h + 1'd1   ;

//hsync:行同步信号
assign  hsync = (cnt_h  <=  H_SYNC - 1'd1) ? 1'b1 : 1'b0  ;

//cnt_v:场同步信号计数器
always@(posedge vga_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_v   <=  10'd0 ;
    else    if((cnt_v == V_TOTAL - 1'd1) &&  (cnt_h == H_TOTAL-1'd1))
        cnt_v   <=  10'd0 ;
    else    if(cnt_h == H_TOTAL - 1'd1)
        cnt_v   <=  cnt_v + 1'd1 ;
    else
        cnt_v   <=  cnt_v ;

//vsync:场同步信号
assign  vsync = (cnt_v  <=  V_SYNC - 1'd1) ? 1'b1 : 1'b0  ;

//rgb_valid:VGA有效显示区域
assign  rgb_valid = (((cnt_h >= H_SYNC + H_BACK + H_LEFT)
                    && (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VALID))
                    &&((cnt_v >= V_SYNC + V_BACK + V_TOP)
                    && (cnt_v < V_SYNC + V_BACK + V_TOP + V_VALID)))
                    ? 1'b1 : 1'b0;

//pix_data_req:像素点色彩信息请求信号,超前rgb_valid信号一个时钟周期
assign  pix_data_req = (((cnt_h >= H_SYNC + H_BACK + H_LEFT - 1'b1)
                    && (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VALID - 1'b1))
                    &&((cnt_v >= V_SYNC + V_BACK + V_TOP)
                    && (cnt_v < V_SYNC + V_BACK + V_TOP + V_VALID)))
                    ? 1'b1 : 1'b0;

//pix_x,pix_y:VGA有效显示区域像素点坐标
assign  pix_x = (pix_data_req == 1'b1)
                ? (cnt_h - (H_SYNC + H_BACK + H_LEFT - 1'b1)) : 10'h3ff;
assign  pix_y = (pix_data_req == 1'b1)
                ? (cnt_v - (V_SYNC + V_BACK + V_TOP)) : 10'h3ff;

//rgb:输出像素点色彩信息
assign  rgb = (rgb_valid == 1'b1) ? pix_data : 16'b0 ;

endmodule

2.3.4 仿真验证

`timescale  1ns/1ns

module  tb_vga_ctrl();

wire            locked      ;
wire            rst_n       ;
wire            vga_clk     ;

reg             sys_clk     ;
reg             sys_rst_n   ;
reg     [15:0]  pix_data    ;

//sys_clk,sys_rst_n初始赋值
initial
    begin
        sys_clk     =   1'b1;
        sys_rst_n   <=  1'b0;
        #200
        sys_rst_n   <=  1'b1;
    end

//sys_clk:产生时钟
always  #10 sys_clk = ~sys_clk;

//rst_n:VGA模块复位信号
assign  rst_n = (sys_rst_n & locked);

//pix_data:输入像素点色彩信息
always@(posedge vga_clk or negedge rst_n)
    if(rst_n == 1'b0)
        pix_data    <=  16'h0;
    else
        pix_data    <=  16'hffff;

//------------- clk_gen_inst -------------
clk_gen clk_gen_inst
(
    .RESET      (~sys_rst_n ),  //输入复位信号,高电平有效,1bit
    .CLK_IN1    (sys_clk    ),  //输入50MHz晶振时钟,1bit

    .CLK_OUT1   (vga_clk    ),  //输出VGA工作时钟,频率25Mhz,1bit
    .LOCKED     (locked     )   //输出pll locked信号,1bit
);

//------------- vga_ctrl_inst -------------
vga_ctrl  vga_ctrl_inst
(
    .vga_clk    (vga_clk    ),  
    .sys_rst_n  (rst_n      ),  
    .pix_data   (pix_data   ),  

    .pix_x      (pix_x      ),  
    .pix_y      (pix_y      ),  
    .hsync      (hsync      ),  
    .vsync      (vsync      ),  
    .rgb        (rgb        )   
);

endmodule

      仿真波形过于密集,这里大致展示。(仿真时间大约 17ms   1s有60帧,展示一帧即可)

2.4 图像数据生成模块

2.4.1 模块框图

     模块功能为VGA 时序控制模块传入的图像有效显示区域像素点坐标(pix_x,pix_y)为约束条件, 产生 VGA 彩条图像像素点色彩信息并回传给VGA 时序控制模块。

 图像数据生成模块输入输出端口功能描述:

2.4.2 波形图绘制

    思路比较简单不做分析。 

2.4.3 代码编写

`timescale  1ns/1ns
module  vga_pic
(
    input   wire            vga_clk     ,   
    input   wire            sys_rst_n   ,   
    input   wire    [9:0]   pix_x       ,   //像素点X轴坐标
    input   wire    [9:0]   pix_y       ,   //像素点Y轴坐标

    output  reg     [15:0]  pix_data        //输出色彩信息
);

parameter   H_VALID =   10'd640 ,   //行数据
            V_VALID =   10'd480 ;   //场数据

parameter   RED     =   16'hF800,   //红
            ORANGE  =   16'hFC00,   //橙
            YELLOW  =   16'hFFE0,   //黄
            GREEN   =   16'h07E0,   //绿
            CYAN    =   16'h07FF,   //青
            BLUE    =   16'h001F,   //蓝
            PURPPLE =   16'hF81F,   //紫
            BLACK   =   16'h0000,   //黑
            WHITE   =   16'hFFFF,   //白
            GRAY    =   16'hD69A;   //灰
//输出像素点色彩信息,根据当前像素点坐标指定当前像素点颜色数据
always@(posedge vga_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        pix_data    <= 16'd0;
    else    if((pix_x >= 0) && (pix_x < (H_VALID/10)*1))
        pix_data    <=  RED;
    else    if((pix_x >= (H_VALID/10)*1) && (pix_x < (H_VALID/10)*2))
        pix_data    <=  ORANGE;
    else    if((pix_x >= (H_VALID/10)*2) && (pix_x < (H_VALID/10)*3))
        pix_data    <=  YELLOW;
    else    if((pix_x >= (H_VALID/10)*3) && (pix_x < (H_VALID/10)*4))
        pix_data    <=  GREEN;
    else    if((pix_x >= (H_VALID/10)*4) && (pix_x < (H_VALID/10)*5))
        pix_data    <=  CYAN;
    else    if((pix_x >= (H_VALID/10)*5) && (pix_x < (H_VALID/10)*6))
        pix_data    <=  BLUE;
    else    if((pix_x >= (H_VALID/10)*6) && (pix_x < (H_VALID/10)*7))
        pix_data    <=  PURPPLE;
    else    if((pix_x >= (H_VALID/10)*7) && (pix_x < (H_VALID/10)*8))
        pix_data    <=  BLACK;
    else    if((pix_x >= (H_VALID/10)*8) && (pix_x < (H_VALID/10)*9))
        pix_data    <=  WHITE;
    else    if((pix_x >= (H_VALID/10)*9) && (pix_x < H_VALID))
        pix_data    <=  GRAY;
    else
        pix_data    <=  BLACK;

endmodule

3.总结

1. 难点主要在于VGA 时序控制模块(vga_ctrl)的针对不同坐标点对 pix_data 进行赋值的理解,以及生成图像数据请求信号 pix_data_req 来抵消其他模块产生的延时一拍的误差。

2. 设计思想主要是依据vga时序要求定位显示点发送显示数据。

说明:

本人使用的是野火家Xilinx Spartan6系列开发板及配套教程主要用于自我学习,以上内容如有疑惑或错误欢迎评论区指出,或者移步B站观看野火家视频教程。

开发软件:ise14.7     仿真:modelsim 10.5 

如需上述资料私信或留下邮箱。

猜你喜欢

转载自blog.csdn.net/m0_72885897/article/details/129900357