草庐IT

VGA、TFT显示模块——verilog实现

FPGA与ZYNQ的学习笔记 2023-03-28 原文

这次设计一个VGA、TFT显示模块,其特点如下:

  • 行同步信号、场同步信号、数据有效信号的延迟数可调。(应用时方便与存储模块数据对齐)
  • 分辨率可以通过调整参数来改变。
  • 数据格式为RGR565,可简单修改位宽来修改成其他数据格式。
    TFT的接口时序和VGA的时序相似,但是TFT接口比VGA多了数据有效信号和背光信号。
    所以该模块可以用在TFT上,也可以用在VGA显示上。

VGA时序

RGB 接口的 TFT 屏扫描方式和 VGA (Video Graphics Array)标准类似,使用行列扫描的方式。在介绍 TFT 屏扫描原理之前,先来介绍下 VGA 显示器的扫描原理。
在 VGA 标准兴起的时候,常见的彩色显示器一般由 CRT(阴极射线管)构成,色彩是由 RGB 三基色组成。显示是用逐行扫描的方式解决。阴极射线枪发出的电子束打在涂有荧光粉的荧光屏上,产生 RGB 三基色,合成一个彩色像素,扫描从屏幕的左上方开始,从左到右,从上到下进行扫描,每扫完一行,电子束都回到屏幕的下一行左边的起始位置。

在这期间,CRT 对电子束进行消隐。每行结束时,用行同步信号进行行同步;扫描完所有行,用场同步信号进行场同步,并使扫描回到屏幕的左上方。同时进行场消隐,预备下一场的扫描。
随着显示技术的发展,出现了液晶显示器,液晶显示器的成像原理与 CRT 不同,液晶显示器是通过对液晶像素点单元施加电压与否,来实现液晶单元的透明程度,并添加三色滤光片、分别使 R、G、B 这 3 中光线透过滤光片,最后通过 3 个像素点合成一个彩色像素点,从而实现彩色显示。由于液晶技术晚于 CRT 显示技术诞生,为了能够兼容传统的显示接口,液晶显示器通过内部电路实现了对 VGA 接口的完全兼容。因此,在使用显示器时,只要该显示器带有标准的 VGA 接口,就不用去关注其成像原理,直接用标准的 VGA 时序即可驱动。
RGB 接口的 TFT 屏,扫描方式与 VGA 完全一致。不同之处只是在于,VGA 显示器是接收模拟信号,而 TFT 屏则省略了这一过程,直接接受数字信号。例如,在驱动 VGA 时首先产生对应像素的颜色数字信号编码,再使用数模转换电路(例如 ADV7123)将数字转换为模拟信号,然后通过 VGA 线缆将模拟信号传输到 VGA 显示器上进行显示。而 TFT 屏则直接省略了数模转换这一过程,直接将接收到的数字信号进行显示。因此在控制器设计端,并没有任何区别,因此本章学习结束可以只适当修改时序参数便可驱动学习套件中的 VGA 接口。
如图所示为该屏接口的行扫描时序图。

以 5 寸 TFT 屏(分辨率 800*480)为例,对行扫描时序要求如下(单位:输出一个像素的时间间隔,即像素时钟)。

如图所示为屏幕场扫描的时序图。

5 寸 TFT 屏中对场扫描时序要求如下(单位:输出一行 Line 的时间间隔)。

整个 TFT 屏的图像扫描示意图如下。

(以上介绍摘抄自小梅哥的教材文档,侵权删)

总之就是在行和场同步信号都是可视区域的时间时输出图像数据即可。

一、VGA显示模块

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: GDUT
// Engineer: Lclone
// 
// Create Date: 2023/02/07 13:58:13
// Design Name: VGA_Display
// Module Name: VGA_Display
// Project Name: VGA_Display
// Target Devices: 
// Tool Versions: 
// Description: 
//               行同步信号、场同步信号、数据有效信号的延迟数可调。(应用时方便与存储模块数据对齐)
//               分辨率可以通过调整参数来改变。 
//               数据格式为RGR565,可简单修改位宽来修改成其他数据格式。
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module VGA_Display
#   (   parameter HS_DELAY = 0,           //信号延迟参数
        parameter VS_DELAY = 0,          
        parameter DE_DELAY = 0          )
    (
        //-----------内部接口------------
        input               Clk,           //时钟信号
        input               Rst_n,         //复位信号
        output              Data_Req,      //数据请求信号
        input       [15:0]  RGB_Data,      //图像数据输入
        output reg  [11:0]  H_addr,       //数据行地址
        output reg  [11:0]  V_addr,       //数据列地址
        //-----------外部接口------------
        output              Disp_Pclk,     //像素时钟
        output      [ 4:0]  Disp_Red,      //数据红色
        output      [ 5:0]  Disp_Green,    //数据绿色
        output      [ 4:0]  Disp_Blue,     //数据蓝色
        output              HS,            //行同步信号
        output              VS,            //场同步信号
        output              DE,            //数据有效信号
        output              BL             //背光信号(可以通过PWM来调节亮度,这里直接输出1)
    );
    
    localparam H_PULSE_BEGIN = 0;
    localparam H_PULSE_END   = 128;
    localparam H_DATA_BEGIN  = 128 + 88 + 0;
    localparam H_DATA_END    = 128 + 88 + 0 + 800;
    localparam H_END         = 128 + 88 + 0 + 800 + 40 + 0;
    
    localparam V_PULSE_BEGIN = 0;
    localparam V_PULSE_END   = 2;
    localparam V_DATA_BEGIN  = 2 + 25 + 8;
    localparam V_DATA_END    = 2 + 25 + 8 + 480;
    localparam V_END         = 2 + 25 + 8 + 480 + 8 + 2;
    
    reg [11:0] H_cnt; //行计数
    reg [11:0] V_cnt; //场计数
    
    reg HS_act; //行 有效信号
    reg VS_act; //场 有效信号
    reg DE_act; //数据有效 有效信号
    
    reg [3:0] HS_reg; //行同步信号延迟寄存器
    reg [3:0] VS_reg; //场同步信号延迟寄存器
    reg [3:0] DE_reg; //数据有效信号延迟寄存器
    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            H_cnt <= 0;
        else if(H_cnt == H_END - 1)
            H_cnt <= 0;
        else
            H_cnt <= H_cnt + 1'b1;
    end
    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            V_cnt <= 0;
        else if(V_cnt == V_END - 1 & H_cnt == H_END - 1)
            V_cnt <= 0;
        else if(H_cnt == H_END - 1)
            V_cnt <= V_cnt + 1'b1;
        else
            V_cnt <= V_cnt;
    end
    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            H_addr <= 0;
        else if(H_cnt == H_DATA_END - 1)
            H_addr <= 0;
        else if(H_cnt >= H_DATA_BEGIN & DE_act == 1)
            H_addr <= H_addr + 1'b1;
        else
            H_addr <= 0;
    end
    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            V_addr <= 0;
        else if(V_cnt >= V_DATA_END - 1  & H_cnt == H_DATA_END - 1)
            V_addr <= 0;
        else if(V_cnt >= V_DATA_BEGIN & H_cnt == H_DATA_END - 1)
            V_addr <= V_addr + 1'b1;
        else
            V_addr <= V_addr;
    end
    

    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            HS_act <= 1;
        else if(H_cnt == H_PULSE_BEGIN )
            HS_act <= 0;
        else if(H_cnt == H_PULSE_END - 1)
            HS_act <= 1;
        else if(H_cnt == H_END - 1)
            HS_act <= 0;
        else
            HS_act <= HS_act;
    end
    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            VS_act <= 1;
        else if(V_cnt == V_PULSE_BEGIN)
            VS_act <= 0;
        else if(V_cnt == V_PULSE_END)
            VS_act <= 1;
        else if(V_cnt == V_END)
            VS_act <= 0;
        else
            VS_act <= VS_act;
    end
    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            DE_act <= 0;
        else if(H_cnt >= H_DATA_BEGIN - 1 & H_cnt <= H_DATA_END - 1 - 1 & 
                    V_cnt >= V_DATA_BEGIN & V_cnt <= V_DATA_END - 1) // 这里H_DATA_END减两个1是通过仿真得出的结果。
            DE_act <= 1;
        else
            DE_act <= 0;
    end
    
    always @(posedge Clk) begin
        HS_reg[0] <=  HS_act;
        HS_reg[3:1] <= HS_reg[2:0];
        
        VS_reg[0] <=  VS_act;
        VS_reg[3:1] <= VS_reg[2:0];
        
        DE_reg[0] <= DE_act;
        DE_reg[3:1] <= DE_reg[2:0];
    end
    
    assign HS = (HS_DELAY == 0)? HS_act : HS_reg[HS_DELAY - 1];
    assign VS = (VS_DELAY == 0)? VS_act : VS_reg[VS_DELAY - 1];
    assign DE = (DE_DELAY == 0)? DE_act : DE_reg[DE_DELAY - 1];
    
    assign Disp_Pclk  = Clk;
    assign Disp_Red   = (DE == 1)? RGB_Data[15:11] : 0 ;
    assign Disp_Green = (DE == 1)? RGB_Data[10:5]  : 0 ;
    assign Disp_Blue  = (DE == 1)? RGB_Data[4:0]   : 0 ;
    
    assign Data_Req =  DE_act;
        
    assign BL = 1'b1;
       
endmodule

二、仿真

1、仿真激励

`timescale 1ns / 1ps

module VGA_Display_tb();
reg clk_50m;
initial clk_50m <= 0;
always #10 clk_50m <= ~clk_50m;

reg rst_n;
initial begin
    rst_n <= 0;
    #200
    rst_n <= 1;
end

wire        Data_Req;
wire [11:0] H_addr;
wire [11:0] V_addr;
reg  [15:0] RGB_Data;
wire [15:0] output_data;
wire        Disp_Pclk;

wire HS;
wire VS;
wire DE;
wire BL;

always @(posedge clk_50m or negedge rst_n) begin
    if(rst_n == 0)
        RGB_Data <= 0;
    else if(Data_Req)
        RGB_Data <= RGB_Data + 1'b1;
    else
        RGB_Data <= RGB_Data;
end

VGA_Display
#   (   .HS_DELAY           (1),
        .VS_DELAY           (1),
        .DE_DELAY           (1))
VGA_Display_inst
    (
        //-----------内部接口------------
        .Clk                (clk_50m),
        .Rst_n              (rst_n),
        .Data_Req           (Data_Req),
        .RGB_Data           (RGB_Data),
        .H_addr             (H_addr),
        .V_addr             (V_addr),
        //-----------外部接口------------
        .Disp_Pclk          (Disp_Pclk),
        .Disp_Red           (output_data[4:0]),
        .Disp_Green         (output_data[10:5]),
        .Disp_Blue          (output_data[15:11]),
        .HS                 (HS),
        .VS                 (VS),
        .DE                 (DE),
        .BL                 (BL)
    );
endmodule

2、仿真分析


可见行时序符合参数设计。


可见场时序也符合参数设计。

数据行地址从0计到799共800次

数据列地址从0记到479共480次
结论:仿真验证初步通过

三、上板验证

1、上板验证代码

此段代码中例化了一个时钟管理单元,输入为50M的时钟,输出为33M的时钟。
本模块设计预期为显示8个色块。

module VGA_TFT(
    //内部信号
    input               CLK,
    input               Rst_n,
    //外部信号
    output              Disp_HS,
    output              Disp_VS,
    output      [4:0]   Disp_Red,
    output      [5:0]   Disp_Green,
    output      [4:0]   Disp_Blue,
    output              Disp_DE,
    output              Disp_PCLK,
    output              Disp_BL
    );
    
    reg [15:0]  RGB_Data_in;
    wire        DataReq;
    wire[11:0]  H_Addr;
    wire[11:0]  V_Addr;
    
    parameter
        BLACK   = 16'h0000, //黑色
        BLUE    = 16'h001F, //蓝色
        RED     = 16'hF800, //红色
        PURPPLE	= 16'hF81F, //紫色
        GREEN   = 16'h07E0, //绿色
        CYAN    = 16'h07FF, //青色
        YELLOW	= 16'hFFE0, //黄色
        WHITE   = 16'hFFFF; //白色
    
    parameter 
        C0_R0 = BLACK,	 //第0行0列像素块
        C1_R0 = BLUE,	 //第0行1列像素块
        C0_R1 = RED,	 //第1行0列像素块
        C1_R1 = PURPPLE, //第1行1列像素块
        C0_R2 = GREEN,	 //第2行0列像素块
        C1_R2 = CYAN,	 //第2行1列像素块
        C0_R3 = YELLOW,	 //第3行0列像素块
        C1_R3 = WHITE;	 //第3行1列像素块
    
    wire    C0_ACT = H_Addr >= 0 && H_Addr < 401;
    wire    C1_ACT = H_Addr >= 401 && H_Addr <= 800;
    
    wire    R0_ACT = V_Addr >= 0   && V_Addr < 119;
    wire    R1_ACT = V_Addr >= 120 && V_Addr < 239;
    wire    R2_ACT = V_Addr >= 240 && V_Addr < 359;
    wire    R3_ACT = V_Addr >= 360 && V_Addr < 479;
    
    wire    C0_R0_ACT = C0_ACT & R0_ACT & DataReq;
    wire    C0_R1_ACT = C0_ACT & R1_ACT & DataReq;
    wire    C0_R2_ACT = C0_ACT & R2_ACT & DataReq;
    wire    C0_R3_ACT = C0_ACT & R3_ACT & DataReq;
    wire    C1_R0_ACT = C1_ACT & R0_ACT & DataReq;
    wire    C1_R1_ACT = C1_ACT & R1_ACT & DataReq;
    wire    C1_R2_ACT = C1_ACT & R2_ACT & DataReq;
    wire    C1_R3_ACT = C1_ACT & R3_ACT & DataReq;
    
    always @(*)
        case({C0_R0_ACT,C0_R1_ACT,C0_R2_ACT,C0_R3_ACT,
                C1_R0_ACT,C1_R1_ACT,C1_R2_ACT,C1_R3_ACT})
            8'b0000_0001:RGB_Data_in = C1_R3;
            8'b0000_0010:RGB_Data_in = C1_R2;
            8'b0000_0100:RGB_Data_in = C1_R1;
            8'b0000_1000:RGB_Data_in = C1_R0;
            8'b0001_0000:RGB_Data_in = C0_R3;
            8'b0010_0000:RGB_Data_in = C0_R2;
            8'b0100_0000:RGB_Data_in = C0_R1;
            8'b1000_0000:RGB_Data_in = C0_R0;
            default: RGB_Data_in <= 0;
        endcase
    
    assign  Disp_BL = 1'b1;
    
    wire    CLK_33M;
    
    clk_wiz_0 clk_wiz_0_inst
   (
    // Clock out ports
    .clk_out1(CLK_33M),     // output clk_out1
    // Status and control signals
    .reset(~Rst_n), // input reset
    .locked(),       // output locked
   // Clock in ports
    .clk_in1(CLK));      // input clk_in1
        

VGA_Display
#   (   .HS_DELAY           (1),
        .VS_DELAY           (1),
        .DE_DELAY           (1))
VGA_Display_inst
    (
        //-----------内部接口------------
        .Clk                (CLK_33M),
        .Rst_n              (Rst_n),
        .Data_Req           (DataReq),
        .RGB_Data           (RGB_Data_in),
        .H_addr             (H_Addr),
        .V_addr             (V_Addr),
        //-----------外部接口------------
        .Disp_Pclk          (Disp_PCLK),
        .Disp_Red           (Disp_Red),
        .Disp_Green         (Disp_Green),
        .Disp_Blue          (Disp_Blue),
        .HS                 (Disp_HS),
        .VS                 (Disp_VS),
        .DE                 (Disp_DE),
        .BL                 ()
    );
endmodule

2、上板实验现象


可见TFT屏能够正常显示8个色块,实验成功,验证模块可用。

有关VGA、TFT显示模块——verilog实现的更多相关文章

  1. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  2. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  3. ruby-on-rails - Rails 编辑表单不显示嵌套项 - 2

    我得到了一个包含嵌套链接的表单。编辑时链接字段为空的问题。这是我的表格:Editingkategori{:action=>'update',:id=>@konkurrancer.id})do|f|%>'Trackingurl',:style=>'width:500;'%>'Editkonkurrence'%>|我的konkurrencer模型:has_one:link我的链接模型:classLink我的konkurrancer编辑操作:defedit@konkurrancer=Konkurrancer.find(params[:id])@konkurrancer.link_attrib

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  6. ruby-on-rails - 使用 Sublime Text 3 突出显示 HTML 背景语法中的 ERB? - 2

    所以我在关注Railscast,我注意到在html.erb文件中,ruby代码有一个微弱的背景高亮效果,以区别于其他代码HTML文档。我知道Ryan使用TextMate。我正在使用SublimeText3。我怎样才能达到同样的效果?谢谢! 最佳答案 为SublimeText安装ERB包。假设您安装了SublimeText包管理器*,只需点击cmd+shift+P即可获得命令菜单,然后键入installpackage并选择PackageControl:InstallPackage获取包管理器菜单。在该菜单中,键入ERB并在看到包时选择

  7. ruby-on-rails - link_to 不显示任何 rails - 2

    我试图在索引页中创建一个超链接,但它没有显示,也没有给出任何错误。这是我的index.html.erb代码。ListingarticlesTitleTextssss我检查了我的路线,我认为它们也没有问题。PrefixVerbURIPatternController#Actionwelcome_indexGET/welcome/index(.:format)welcome#indexarticlesGET/articles(.:format)articles#indexPOST/articles(.:format)articles#createnew_articleGET/article

  8. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  9. ruby-on-rails - 如何在 Rails View 上显示错误消息? - 2

    我是rails的新手,想在form字段上应用验证。myviewsnew.html.erb.....模拟.rbclassSimulation{:in=>1..25,:message=>'Therowmustbebetween1and25'}end模拟Controller.rbclassSimulationsController我想检查模型类中row字段的整数范围,如果不在范围内则返回错误信息。我可以检查上面代码的范围,但无法返回错误消息提前致谢 最佳答案 关键是您使用的是模型表单,一种显示ActiveRecord模型实例属性的表单。c

  10. ruby - 当使用::指定模块时,为什么 Ruby 不在更高范围内查找类? - 2

    我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or

随机推荐