-

所有工程文件均已开源,获取地址:zikwq/FPGA_prj: FPGA_prj (github.com)

VGA介绍

1. 背景

VGA(视频图形阵列),是一种电脑显示标准,是IBM在1987年提出的一个使用类比讯号的电脑显示标准。个人电脑在加载自己独特的驱动程序之前,都必须支持VGA的标准,绝大多数显卡都带有这种接口。

说到VGA接口,相信很多朋友都不会陌生,因为这种接口是电脑显示器上最主要的接口,从块头巨大的CRT显示器时代开始,VGA接口就被使用,并且一直沿用至今,另外VGA接口还被称为D-Sub接口。

VGA支持在640x480的较高分辨率下同时显示16种色彩或256种灰度。同时在320x240分辨率下可以同时显示256种颜色。

VGA接口共有15针,分成3排,每排5针。传输红绿蓝模拟信号以及同步信号(场同步和行同步)。使用VGA连接设备,线缆长度最好不要超过10米。

2. 特征

1、是模拟信号的传输,随着显示器的分辨率越高画面就会越模糊。一般模拟信号在超过1280×1024分辨率以上的情况下就会出现明显的误差。

2、因为是模拟信息,信号极其容易受干扰。

3、不能传达音频。

4、一般不加放大器和转换器在30米左右,加放大器和放大转换器可以到150米。

3. 原理图

我在进行VGA显示器驱动时,因为我使用的FPGA开发板是至芯z1开发板,而我看到视频教程是在哔哩哔哩中野火FPGA的教程。当时我在跟着视频做完程序后,在配置引脚的时候发现我要传输出去的信号,野火定义了16位宽,而我的开发板原理图只留出来8个引脚,也就是我传输出的数据必须是8位宽的数据才行。

后来我找了一些资料才发现,野火教程里面使用的是RGB565协议,而我的原理图是RGB332协议,所以需要修改对应的输出数据的位宽以及重新使用8位宽定义输出不同的颜色。其实也很简单,只不过想在这里记录一下。因为当天晚上我回到宿舍还硬想了一晚上为啥不一样嘞。今天回到实验室我重新了原理图才明白哈哈

可以对比看出来,RGB332和RGB565的一些不一样的地方,但是在这两个原理图中,都是使用权电阻网络来驱动VGA的,还有一种方法就是使用AD模数转换芯片来输出模拟信号驱动VGA,常见的AD芯片如:AD7123

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

4. 时序图

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

VGA时序标准,主要注意的点就是需要一个行同步信号和场同步信号,并且在同步信号开始之后并不直接是有效图像信号,在一个完整的扫描周期中,分为同步、后沿、左边框、有效信号、右边框、前沿。只有在有效信号的部分才是有效信号(说了跟没说一样)

行扫描周期的单位是像素!

场扫描周期的单位是行!

程序设计

1. 系统框图

一个项目的设计,我们首先需要设计好整个系统的框图,将功能分成模块,然后自下而上一步一步分析。完成一个模块后,进行仿真验证

在这个系统框图里面,我们使用PLL锁相环IP核分出来一个25MHZ的时钟信号作为VGA的时钟,并且输出一个锁定信号locked,将锁定信号和复位信号使用与门连接,其输出作为其他模块的复位信号。

在vga_pic模块中,主要是产生8位宽的图像数据,是根据输入的(x,y)坐标进行确定的。因为我们要在一个显示屏幕上显示10种不同的颜色嘛,

vga_ctrl模块,顾名思义,就是一个控制模块,用来输出行场同步信号和图像数据信号

2. 程序设计

vga_pic文件

module vga_pic(
    input           wire        vga_clk ,
    input           wire        rst_n,
    input           wire  [9:0] pix_x,
    input           wire  [9:0] pix_y,

    output          reg  [7:0]pix_data
);

parameter       H_VALID =   10'd640,
                V_VALID =   10'd480;

// RGB565协议生成颜色
// parameter       RED     =   16'hF800    ,
//                 ORANGE  =   16'hFC00    ,    
//                 YELLOW  =   16'hFFE0    ,    
//                 GREEN   =   16'h07E0    ,    
//                 CYAN    =   16'h07EF    ,    
//                 BLUE    =   16'h001F    ,    
//                 PURPPLE =   16'hF81F    ,    
//                 BLACK   =   16'h0000    ,    
//                 WHITE   =   16'hFFFF    ,    
//                 GRAY    =   16'hD69A    ;   
// RGB332协议生成颜色 
parameter RED     =   8'b11100000; // 16'hF800 -> 8'b11100000
parameter ORANGE  =   8'b11111000; // 16'hFC00 -> 8'b11111000
parameter YELLOW  =   8'b11111010; // 16'hFFE0 -> 8'b11111010
parameter GREEN   =   8'b00100000; // 16'h07E0 -> 8'b00100000
parameter CYAN    =   8'b00100011; // 16'h07EF -> 8'b00100011
parameter BLUE    =   8'b00011000; // 16'h001F -> 8'b00011000
parameter PURPPLE =   8'b11100001; // 16'hF81F -> 8'b11100001
parameter BLACK   =   8'b00000000; // 16'h0000 -> 8'b00000000
parameter WHITE   =   8'b11111111; // 16'hFFFF -> 8'b11111111
parameter GRAY    =   8'b11010110; // 16'hD69A -> 8'b11010110

always @(posedge vga_clk or negedge rst_n) begin
    if(!rst_n)
        pix_data <= 8'd0000;
    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;

end

endmodule

vga_ctrl文件

module vga_ctrl(
    input       wire                vga_clk     ,
    input       wire                rstn        ,
    input       wire     [15:0]     pix_data    , // 显示数据
    
    output      wire     [9:0]      pix_x       , // x轴有效坐标位置
    output      wire     [9:0]      pix_y       , // y轴有效坐标位置
    output      wire                hsync       , // 行同步信号
    output      wire                vsync       , // 场同步信号
    output      wire     [7:0]     vga_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   ;


reg     [9:0]       cnt_h ;
reg     [9:0]       cnt_v ;
wire                 rgb_valid ;
wire                 pix_data_req ;


// cnt_h行计数器,计数范围0-799
always @(posedge vga_clk or negedge rstn) begin
    if(!rstn)
        cnt_h       <=      10'd0;
    else if (cnt_h == H_TOTAL-1'd1)
        cnt_h       <=      10'd0;
    else
        cnt_h       <=      cnt_h + 1'd1;
end

// cnt_v列计数器,计数范围0-524并且行计数器计数到799
always @(posedge vga_clk or negedge rstn) begin
    if(!rstn)
        cnt_v       <=      10'd0;
    else if ((cnt_h == H_TOTAL-1'd1) && (cnt_v == V_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;
end

// 定义有效数据范围,
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'd1 : 1'd0;

assign pix_data_req = ((cnt_h >= H_SYNC + H_BACK + H_LEFT - 1'd1) 
                 &&  (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VALID -1'd1)
                 &&  (cnt_v >= V_SYNC + V_BACK + V_TOP)
                 &&  (cnt_v < V_SYNC + V_BACK + V_TOP + V_VALID)) ? 1'd1 : 1'd0;

assign pix_x = (pix_data_req == 1'd1) ? (cnt_h - (H_SYNC + H_BACK + H_LEFT - 1'd1)) : 10'h3ff;
assign pix_y = (pix_data_req == 1'd1) ? (cnt_v - (V_SYNC + V_BACK + V_TOP)) : 10'h3ff;
assign hsync = (cnt_h <= H_SYNC - 1'd1) ? 1'd1 : 1'd0;
assign vsync = (cnt_v <= V_SYNC - 1'd1) ? 1'd1 : 1'd0;
assign vga_rgb = (rgb_valid == 1'd1) ? pix_data : 8'h0000;


endmodule

tb_vga_ctrl 仿真文件,在仿真的时候,将仿真时间设计成17ms即可

`timescale 1ns/1ps

module tb_vga_ctrl ();
reg         clk ;
reg         rstn    ;
wire  [15:0]  pix_data;

wire        vga_clk;
wire        locked;
wire        rst_n;

wire   [9:0]     pix_x      ;
wire   [9:0]     pix_y      ;
wire             hsync      ;
wire             vsync      ;
wire   [15:0]    vga_rgb    ;

initial begin
    clk  =  1'd1;
    rstn =  1'd0;
    # 20
    rstn  =  1'd1;
end

always #10 clk = ~clk;

// always @(posedge vga_clk or negedge rstn) begin
//     if(!rstn)
//         pix_data    <=  16'h0000;
//     else if((pix_x >=0 && pix_x <= 639)
//         && (pix_y >= 0 && pix_y <= 479))
//         pix_data    <= 16'hffff;
//     else
//         pix_data    <= 16'h0000;
// end

// 实例化PLL
clk_gen    clk_gen_inst 
(
    .areset     ( ~rstn ),
    .inclk0     ( clk ),
    .c0         ( vga_clk ),
    .locked     ( locked )
);

assign rst_n = locked && rstn;
// 实例化控制模块
vga_ctrl vga_ctrl_init(
    .vga_clk   (vga_clk)  ,
    .rstn      (rst_n)  ,
    .pix_data  (pix_data)  , // 显示数据

    .pix_x     (pix_x  )  , // x轴有效坐标位置
    .pix_y     (pix_y  )  , // y轴有效坐标位置
    .hsync     (hsync  )  , // 行同步信号
    .vsync     (vsync  )  , // 场同步信号
    .vga_rgb   (vga_rgb)  
);
vga_pic vga_pic_inst(
    . vga_clk(vga_clk)   ,
    . rst_n  (rst_n)   ,
    . pix_x  (pix_x)   ,
    . pix_y  (pix_y)   ,    

    .pix_data(pix_data)
);

endmodule

顶层文件:vga_colorb.v

module vga_colorb(
    input   wire    clk,
    input   wire    rstn,
    
    output  wire    hsync,
    output  wire    vsync,
    output  wire    [7:0]  vga_rgb
);

wire    vga_clk ;
wire    locked  ;
wire    rst_n   ;
wire [9:0]    pix_x   ;
wire [9:0]    pix_y  ;
wire [7:0]   pix_data;

assign  rst_n = (rstn && locked);
// 实例化PLL
clk_gen    clk_gen_inst 
(
    .areset     ( ~rstn ),
    .inclk0     ( clk ),
    .c0         ( vga_clk ),
    .locked     ( locked )
);
// 实例化控制模块
vga_ctrl vga_ctrl_init(
    .vga_clk   (vga_clk)  ,
    .rstn      (rst_n)  ,
    .pix_data  (pix_data)  , // 显示数据

    .pix_x     (pix_x  )  , // x轴有效坐标位置
    .pix_y     (pix_y  )  , // y轴有效坐标位置
    .hsync     (hsync  )  , // 行同步信号
    .vsync     (vsync  )  , // 场同步信号
    .vga_rgb   (vga_rgb)  
);
vga_pic vga_pic_inst(
    . vga_clk(vga_clk)   ,
    . rst_n  (rst_n)   ,
    . pix_x  (pix_x)   ,
    . pix_y  (pix_y)   ,    

    .pix_data(pix_data)
);

endmodule

参考资料

VGA、DVI、HDMI区别 - 知乎 (zhihu.com)

FPGA基础之VGA(一)满屏红色_vga_hs-CSDN博客

VGA接口_百度百科 (baidu.com)


本站由 John Doe 使用 Stellar 1.28.1 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

本"页面"访问 次 | 👀总访问 次 | 🥷总访客