(七)Zedboard上面实现PL-VGA实验

时间:2024-03-31 21:45:36

注:由于没有系统的学过FPGA和Verilog语言,都是用到了才查的,所以对照参考的实验有很多删减的地方。参考链接见文末。

1.本实验是用Zedboard实现VGA接口的使用,通过PL端的资源,根据对应引脚连接信号线,完成VGA的使用。
实验分为几个小节,首先介绍VGA的引脚,然后了解VGA的时序,再根据硬件电路图,完成Verilog程序的设计,接着烧写bit流到开发板,测试。
2.介绍
参考:米联------- ZYNQ VGA 硬件驱动设计
VGA 是 Video Graphics Array 的简称, 也叫 D-Sub 接口。 目前显示器都配备 VGA接口, 有的显示器还配备 DVI(Digital Visual Interface) 和 HDMI(High Definition
Multimedia Interface) 接口。 VGA 是 IBM 在 1987 年随 PS/2 机一起推出的一种视频传输标准, 具有分辨率高、 显示速率快、 颜色丰富等优点, 在彩色显示器领域得到了广
泛的应用。 根据不同分辨率, VGA 分为 VGA(640x480) 、 SVGA(800x600) 、 XGA(1024x768) 、SXGA(1280x1024) 等。 目前 VGA 已经成为一种标准, 广泛应用于显示器、 TFT 液晶屏中。
VGA 接口分为公头和母头, 一般显示器都自带一根公头线, MIS603 开发平台上是母头, VGA 接口一共 15 根线, 分为 3 排, 标号如图所示:
(七)Zedboard上面实现PL-VGA实验
引脚定义
(七)Zedboard上面实现PL-VGA实验

在这里强调一下, VGA 接口的 RED、 GREEN 和 BLUE 传输的是模拟信号, 峰峰值为 0V~0.714V, 0V 代表无色, 0.714V 代表满色; HSYNC 和 VSYNC 传输的是数字信号, 为 TTL 电平。 VGA 模拟信号传输如图 6.1-2 所示, 源端和终端匹配电阻均为 75欧姆。
(七)Zedboard上面实现PL-VGA实验

  1. 时序
    参考:https://www.cnblogs.com/spartan/archive/2011/08/16/2140546.html
    显示器扫描方式分为逐行扫描和隔行扫描:逐行扫描是扫描从屏幕左上角一点开始,从左像右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,CRT对电子束进行消隐,每行结束时,用行同步信号进行同步;当扫描完所有的行,形成一帧,用场同步信号进行场同步,并使扫描回到屏幕左上方,同时进行场消隐,开始下一帧。隔行扫描是指电子束扫描时每隔一行扫一线,完成一屏后在返回来扫描剩下的线,隔行扫描的显示器闪烁的厉害,会让使用者的眼睛疲劳。

完成一行扫描的时间称为水平扫描时间,其倒数称为行频率;完成一帧(整屏)扫描的时间称为垂直扫描时间,其倒数称为场频率,即刷新一屏的频率,常见的有60Hz,75Hz等等。标准的VGA显示的场频60Hz,行频31.5KHz。

行场消隐信号:是针对老式显像管的成像扫描电路而言的。电子枪所发出的电子束从屏幕的左上角开始向右扫描,一行扫完需将电子束从右边移回到左边以便扫描第二行。在移动期间就必须有一个信号加到电路上,使得电子束不能发出。不然这个回扫线会破坏屏幕图像的。这个阻止回扫线产生的信号就叫作消隐信号,场信号的消隐也是一个道理。
(七)Zedboard上面实现PL-VGA实验
注:这个图很重要,决定了编程的结构,一定要理解到,没看懂时序的要去看上面的参考链接,那里写的更详细。
4. VGA硬件图
(七)Zedboard上面实现PL-VGA实验
由图可知,颜色信号共12个bit可以显示4096个色号。
VGA 接口信号和Zedboard引脚连接如下表所示:
(七)Zedboard上面实现PL-VGA实验

在VIVADO里面编写好了Verilog程序后,要编写约束文件,就要把对应的信号线与引脚连接起来。
5. 接着就是在VIVADO 里面开始创建工程,不会的去翻以前的文章,里面有讲。然后添加三个文件,一个产生VG信号的.v文件,一个顶层封装文件,一个就是约束文件。由于VGA信号是800×[email protected],我们还需要一个像素时钟40MHZ。zedboard的PL部分默认的时钟是100MHZ,所以我们还需要一个时钟单元来产生40MHZ的特定时钟。分频的方法不稳定,不推荐。
(七)Zedboard上面实现PL-VGA实验

接下来是全部代码。
vga_data_gen.v(添加注释版)

module vga_data_gen(
    input pixel_clk,        //像素时钟
    input rst,              //复位按键,高电平复位

    output [11:0] vga_r,     //vga信号红色视频信号
    output [11:0] vga_g,     //vga信号绿色视频信号
    output [11:0] vga_b,     //vga信号蓝色视频信号
    output vga_hs,          //行同步信号
    output vga_vs,          //场同步信号
    output vga_de,          //vga信号有效,如当前时刻传输数据有效,即为1;否则为0;

    input turn_mode,        //按键。按下为高电平,切换vga显示模式
    output [3:0] mode       //当前显示模式,用4位编码,也就是LED
    );

//---------------------------------//
//使用的是800*600的分辨率
// 水平扫描参数的设定 800*600 60HZ
//行同步信号的参数
//--------------------------------//
parameter H_Total = 1056 - 1;   //一行分为四个段a,b,c,d总共1056个像素
parameter H_Sync = 128 - 1;		//a同步段-拉低的128个像素
parameter H_Back = 88 - 1;		//b后廊段--拉高的88个像素
parameter H_Active = 800 - 1;	//c**段--拉高的800个像素****
parameter H_Front = 40 - 1;		//d前廊段--拉高的40个像素	
parameter H_Start = 216 - 1;	//从第三段开始
parameter H_End = 1016 - 1;		//到第三段结尾,才是是要上机显示使用的像素点

//-------------------------------//
// 垂直扫描参数的设定 
//场同步信号的参数--(同上)
//-------------------------------//
parameter V_Total = 628 - 1;
parameter V_Sync = 4 - 1;
parameter V_Back = 23 - 1;
parameter V_Active = 600 - 1;
parameter V_Front = 1 - 1;
parameter V_Start = 27 - 1;
parameter V_End = 627 - 1;

//行信号计数器
//计数每一行的信号,计到最大值H_Total则清零
reg [11:0] x_cnt;
always @(posedge pixel_clk or posedge rst)
begin
    if(rst)
        x_cnt <= 12'd0;
    else if(x_cnt == H_Total)
        x_cnt <= 12'd0;
    else
        x_cnt <= x_cnt + 1'b1;
end

//根据时序图参数,产生行同步信号
//如果复位或者则行同步信号赋高电平(低电平有效,每行结束时,用行同步信号进行同步)
reg hsync_r;
always @(posedge pixel_clk or posedge rst)
begin
    if(rst)
        hsync_r <= 1'b1;
    else if(x_cnt>=0 && x_cnt < H_Sync)
        hsync_r <= 1'b0; //有效
    else
        hsync_r <= 1'b1;
end

//行信号有效,即当前数据有效(高电平有效)
reg hs_de;
always @(posedge pixel_clk or posedge rst)
begin
    if(rst)
        hs_de <= 1'b0;
    else if(x_cnt>=H_Start && x_cnt<H_End)
        hs_de <= 1'b1;
    else
        hs_de <= 1'b0;
end

//场信号计数器
//计数每一列的信号(即有多少列),计到最大值V_Total则清零
reg [11:0] y_cnt;
always @(posedge pixel_clk or posedge rst)
begin
    if(rst)
        y_cnt <= 12'd0;
    else if(y_cnt == V_Total)
        y_cnt <= 12'd0;
    else if(x_cnt == H_Total)
        y_cnt <= y_cnt + 1'b1;
end

//根据时序图参数,产生场同步信号
//如果复位或者则场同步信号赋高电平(低电平有效,每帧结束时,用场同步信号进行同步)
reg vsync_r;
always @(posedge pixel_clk or posedge rst)
begin
    if(rst)
        vsync_r <= 1'b1;
    else if(y_cnt>=0 && y_cnt<V_Sync)
        vsync_r <= 1'b0;
    else
        vsync_r <= 1'b1;
end

//列信号有效,即当前数据有效
reg vs_de;
always @(posedge pixel_clk or posedge rst)
begin
    if(rst)
        vs_de <= 1'b0;
    else if(y_cnt>=V_Start && y_cnt<V_End)
        vs_de <= 1'b1;
    else
        vs_de <= 1'b0;
end

//按键消抖,按下后延时一段时间,再检测是否按下
reg [16:0] key_counter;
always @(posedge pixel_clk)
begin
    if(turn_mode)	//接到按键,按下为高电平
        key_counter <= 17'd0;  //按下后初始化为0
    else if((turn_mode == 1'b0) && (key_counter <= 17'h11704))  
        key_counter <= key_counter + 1'b1; //如果按键未抖动,并且key_counter的值小于17'h11704,则key_counter寄存器的值加一,
											//每一个时钟上升沿加一次,直到key_counter等于17'h11704(估计是按照时钟频率来算的)
end

//显示模式技术,每次按下按键,模式改变,最多有12种显示模式
//并且将每一种模式的输出到LED 对应的值上面,调用4个LED可以显示16种模式
reg [3:0] dis_mode;
assign mode = dis_mode;
always @(posedge pixel_clk)
begin
    if(key_counter == 17'h11704)	//相当于按键按下
    begin
        if(dis_mode == 4'd6)		//判断模式是否为6,如果是的,归零
            dis_mode <= 4'd0;
        else 
            dis_mode <= dis_mode +1'b1; 	//否则,按一下,模式加一
    end
end

//产生小格子,黑白棋盘
reg [11:0] grid_data_1;
always @(posedge pixel_clk)
begin
    if((x_cnt[4] == 1'b0) ^ (y_cnt[4] == 1'b0)) //两个寄存器第五位的值异或为1则为黑色,否则为白
        grid_data_1 <= 12'h000;
    else
        grid_data_1 <= 12'hfff;
end

//产生大格子,黑白棋盘
reg [11:0] grid_data_2;
always @(posedge pixel_clk)
begin
    if((x_cnt[6] == 1'b0) ^ (y_cnt[6] == 1'b0)) //两个寄存器第七位的值异或为1则为黑色,否则为白
        grid_data_2 <= 12'h000;
    else
        grid_data_2 <= 12'hfff;
end

//根据显示模式传输rgb信号到缓存器中
reg [11:0] vga_r_reg;
reg [11:0] vga_g_reg;
reg [11:0] vga_b_reg;
always @(posedge pixel_clk or posedge rst)
begin
    if(rst)
    begin
        vga_r_reg <= 12'd0;
        vga_g_reg <= 12'd0;
        vga_b_reg <= 12'd0;
    end
    else
    begin
        case(dis_mode)
            4'd0:                   //全黑
            begin
                vga_r_reg <= 12'd0;
                vga_g_reg <= 12'd0;
                vga_b_reg <= 12'd0;
            end

            4'd1:                   //全白
            begin
                vga_r_reg <= 12'b1111_1111_1111;
                vga_g_reg <= 12'b1111_1111_1111;
                vga_b_reg <= 12'b1111_1111_1111;
            end

            4'd2:                   //全红
            begin
                vga_r_reg <= 12'b1111_1111_1111;
                vga_g_reg <= 12'd0;
                vga_b_reg <= 12'd0;
            end

            4'd3:                   //全绿
            begin
                vga_r_reg <= 12'd0;
                vga_g_reg <= 12'b1111_1111_1111;
                vga_b_reg <= 12'd0;
            end

            4'd4:                   //全蓝
            begin
                vga_r_reg <= 12'd0;
                vga_g_reg <= 12'd0;
                vga_b_reg <= 12'b1111_1111_1111;
            end

            4'd5:                   //小格子,黑白棋盘
            begin
                vga_r_reg <= grid_data_1;
                vga_g_reg <= grid_data_1;
                vga_b_reg <= grid_data_1;
            end

            4'd6:                   //大格子,黑白棋盘
            begin
                vga_r_reg <= grid_data_2;
                vga_g_reg <= grid_data_2;
                vga_b_reg <= grid_data_2;
            end
           
        endcase
    end
end

//将这些寄存器中的信号输出
assign vga_hs = hsync_r;
assign vga_vs = vsync_r;
assign vga_de = hs_de & vs_de;  //只有当行信号和场信号同时有效时数据才有效
assign vga_r = (hs_de & vs_de) ? vga_r_reg : 12'h0;
assign vga_g = (hs_de & vs_de) ? vga_g_reg : 12'h0;
assign vga_b = (hs_de & vs_de) ? vga_b_reg : 12'h0;

endmodule



vga_disp.v顶层文件
我的理解是顶层文件的作用是把底层的端口隐射到顶层,实现了一个调用底层文件的作用。

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2018/12/13 15:18:47
// Design Name: 
// Module Name: vga_disp
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module vga_disp(
    input clk,
    input rst,
    input turn_mode,  //KEY
    output [3:0] vga_r,
    output [3:0] vga_g,
    output [3:0] vga_b,
    output vga_hs,
    output vga_vs,
    output [3:0] mode   //LED
    );

    wire pixel_clk;
    wire [7:0] R, G, B;
    wire HS, VS, DE;

    assign vga_r = R[7:4];
    assign vga_g = G[7:4];
    assign vga_b = B[7:4];
    assign vga_hs = HS;
    assign vga_vs = VS;

    vga_data_gen vga_data_gen(
        .pixel_clk(pixel_clk),
        .rst(rst),

        .vga_r(R),
        .vga_g(G),
        .vga_b(B),
        .vga_hs(HS),
        .vga_vs(VS),
        .vga_de(DE),

        .turn_mode(turn_mode),
        .mode(mode)
        );

    clk_wiz_0 clk
    (
      .clk_out1(pixel_clk),
      .reset(1'b0),
      .locked(),
      .clk_in1(clk)
     );

endmodule


约束文件 vga.xdc

# Clk(板子上的GCLK)
# Clk(板子上的GCLK)
set_property PACKAGE_PIN Y9 [get_ports {clk}]
set_property IOSTANDARD LVCMOS33 [get_ports {clk}]

# Rst(板子上的BTNU)
set_property PACKAGE_PIN T18 [get_ports {rst}]
set_property IOSTANDARD LVCMOS33 [get_ports {rst}]

# key 板子上的按键
set_property PACKAGE_PIN P16 [get_ports {turn_mode}]
set_property IOSTANDARD LVCMOS33 [get_ports {turn_mode}]

# Led0
set_property PACKAGE_PIN T22 [get_ports {mode[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {mode[0]}]

# Led1
set_property PACKAGE_PIN T21 [get_ports {mode[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {mode[1]}]

# Led2
set_property PACKAGE_PIN U22 [get_ports {mode[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {mode[2]}]

# Led3
set_property PACKAGE_PIN U21 [get_ports {mode[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {mode[3]}]

set_property PACKAGE_PIN  Y19  [get_ports {vga_vs}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_vs}]

set_property PACKAGE_PIN  AA19  [get_ports {vga_hs}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_hs}]

set_property PACKAGE_PIN  V20   [get_ports {vga_r[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_r[0]}]
set_property PACKAGE_PIN    U20 [get_ports {vga_r[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_r[1]}]
set_property PACKAGE_PIN   V19  [get_ports {vga_r[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_r[2]}]
set_property PACKAGE_PIN   V18  [get_ports {vga_r[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_r[3]}]

set_property PACKAGE_PIN  AB22   [get_ports {vga_g[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_g[0]}]
set_property PACKAGE_PIN   AA22  [get_ports {vga_g[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_g[1]}]
set_property PACKAGE_PIN   AB21  [get_ports {vga_g[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_g[2]}]
set_property PACKAGE_PIN   AA21  [get_ports {vga_g[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_g[3]}]

set_property PACKAGE_PIN   Y21  [get_ports {vga_b[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_b[0]}]
set_property PACKAGE_PIN   Y20  [get_ports {vga_b[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_b[1]}]
set_property PACKAGE_PIN   AB20  [get_ports {vga_b[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_b[2]}]
set_property PACKAGE_PIN   AB19  [get_ports {vga_b[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {vga_b[3]}]

接下来的事就很简单了,生成比特流,下载,然后测试。
测试的时候我是录得视频,并未拍照,下面的图的借的,但是是一致的。
连上vga线,可以看到现象如下:
(七)Zedboard上面实现PL-VGA实验

按动按键,依次看见
(七)Zedboard上面实现PL-VGA实验

(七)Zedboard上面实现PL-VGA实验

(七)Zedboard上面实现PL-VGA实验
(七)Zedboard上面实现PL-VGA实验

(七)Zedboard上面实现PL-VGA实验

测试结束。很感谢参考的文章,学到了就要教人,拿到了就要给人。
参考文章:https://blog.csdn.net/hongbin_xu/article/details/76582103