• Verilog编写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  、 48640   、16     、  800
    //     vs  :2   、 33480   、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;
    	 else
    	     h_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 ;
        else
    	     v_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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    tb测试:

    `timescale 1ns/1ns
    `define clk_period 20
    
    module 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;
    end
    
    endmodule 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    波形分析

    如下是行场同步以及数据有效显示信号:
    可以看到第一行有效数据是从第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 ;
    
    • 1

    下图:

    assign  hsync = (cnt_h  <=  H_SYNC - 1'd1) ? 1'b1 : 1'b0  ;
    
    • 1

    虽然正负极性不同,但最终引脚配置的时候一样

  • 相关阅读:
    HTML躬行记(2)——WebRTC基础实践
    uni-app点击复制指定内容(点击复制)
    认识 Cookie 和 Session
    网络爬虫——urllib(2)
    文件复制到u盘后文件夹是空的,怎么恢复?
    VScode为什么选择了Electron,而不是QT?
    跨网段主机搭建k8s集群注意事项
    小程序 navigateBack 携带参数返回的三种方式(详细)
    “Java基础全方位解析,从入门到精通“
    企业级邮件系统架构
  • 原文地址:https://blog.csdn.net/H19981118/article/details/126449556