H2—VGA接口彩条显示测试-2022-11-10

1.场景

FPGA驱动VGA接口显示1024*768@60Hz彩条图像到带VGA接口的显示屏。

2.步骤与结构

H2—VGA接口彩条显示测试-2022-11-10_第1张图片

 2.1接口部分

VGA的全称是Video Graphics Array,即视频图形阵列,是一个使用模拟信号进行视频传输的标准。早期的CRT显示器由于设计制造上的原因,只能接收模拟信号输入,因此计算机内部的显卡负责进行数模转换,而VGA接口就是显卡上输出模拟信号的接口。如今液晶显示器虽然可以直接接收数字信号,但是为了兼容显卡上的VGA接口,也大都支持VGA标准。(在液晶屏内部又做了一次AD转换)。如图所示,图像数据是模拟信号,而行场同步信号可以直接连接到VGA接口上。

早期的VGA特指分辨率为640X480的显示模式,后来根据分辨率的不同,VGA又分为VGA(640x480)、SVGA(800x600)、XGA(1024x768)、SXGA(1280x1024)等。不同分辨率的VGA显示时序是类似的,仅存在参数上的差异

2.2ADV芯片

该芯片主要将数字信号转换成模拟信号连接VGA接口。其内部有三路10bitAD转换通道,刚好分别连接R、G、B三路信号,具体FPGA程序如何连接管脚,应该看具体的硬件设计。例如,本次例程使用一个VGA转接板连接,设计很奇葩,AD的低两位直接接地,原本可以支持RGB888,但是强行通过转接板删减成RGB565,R[4:2],G[3:2],B[4:2]直接接到了一起,于是只能逻辑上将这个地方接0,即代码中的vga_dummy信号。原理图如下所示。

H2—VGA接口彩条显示测试-2022-11-10_第2张图片

 

2.3FPGA逻辑

      驱动VGA接口的逻辑,其实是驱动ADV芯片的逻辑,这与RGB接口是一致的,只有一点不同是RGB接口时序参数是由制作LCD的厂家定义的,而VGA接口的时序参数是由制定VGA接口标准的协会制定的,是通用的。不同分辨率和刷新率能决定不同的显示参数和像素时钟。例程中,采用的是1024*768@60Hz,像素时钟为时钟为65MHz,常见的VGA接口的参数如下,其中a(o)对应同步脉冲,b(p)对应后沿脉冲,c(q)为有效数据,d(r)为前沿脉冲,e(s)为总长度。

H2—VGA接口彩条显示测试-2022-11-10_第3张图片

对于VGA的驱动编写很简单,用一句话概括就是按照VGA显示该分辨率帧频图像时要求的准确时刻置位行场同步信号,并送出模拟图像有效信号到VGA管脚。如下图所示只需要修改相应的时序参数就可以了。关于RGB接口的时序可以参考F2—TFT显示彩条测试

H2—VGA接口彩条显示测试-2022-11-10_第4张图片

 3.源码及效果

对应工程源码

效果如下:

H2—VGA接口彩条显示测试-2022-11-10_第5张图片

 代码如下:

Top模块,例化驱动模块和逻辑控制模块

module Vga_top(
    input sys_clk_i,//Y18
    input key,//S4 B21
    output [15:0]vga_data,  //VGA数据 RGB565信号
    output vga_hs,   //VGA行同步信
    output vga_vs,   //VGA场同步信
    output vga_clk,  //VGA像素时钟
    output vga_blank_n,   //VGA数据使能
    output vga_sync_n,    //JIE 0
    output [2:0]vga_dummy  //VGA背光控制
    );
wire   clk_65m; 
wire   clk_rst;
wire   vga_de;
assign vga_blank_n=vga_de;
assign vga_sync_n = 1'b0;
assign vga_dummy = 3'b0;
clk_wiz_0 clk_tree(.clk_out1(clk_65m),.reset(1'b0),.locked(clk_rst),.clk_in1(sys_clk_i));
wire [15:0]user_data;
wire data_req;
wire [11:0]x_addr;
wire [11:0]y_addr;
vga_driver my_driver(
    .clk_65m   (clk_65m),
    .rst_n      (clk_rst),
    .start_show (key),
    .user_data  (user_data),
    .data_req   (data_req),
    .addr_x     (x_addr),
    .addr_y     (y_addr),

    .vga_data   (vga_data),
    .vga_hs     (vga_hs),
    .vga_vs     (vga_vs),
    .vga_clk    (vga_clk),    
    .vga_de     (vga_de)
);
vga_ctrl my_ctrl(
    .x_addr(x_addr),
    .y_addr(y_addr),
    .data_req(data_req),
    .pix_data(user_data)
);
endmodule

Driver模块,接收数据源并且实现VGA接口驱动

module vga_driver(   
    input    clk_65m,
    input    rst_n,

    input    start_show,
    input [15:0]user_data,
    output   data_req,
    output   [11:0]addr_x,
    output   [11:0]addr_y,

    output[15:0]vga_data,
    output   vga_hs,
    output   vga_vs,
    output   vga_clk,
    output   vga_de//背光使能信号 
    );
//VGA时序参数 Timing 1024*768 & 65MHz & 60Hz
parameter H_Total_Time   = 12'd1344;
parameter H_Right_Border = 12'd0;
parameter H_Front_Porch  = 12'd24;
parameter H_Sync_Time    = 12'd136;
parameter H_Back_Porch   = 12'd160;
parameter H_Left_Border  = 12'd0;

parameter V_Total_Time   = 12'd806;
parameter V_Bottom_Border= 12'd0;//border信号是人为定义的上下边框
parameter V_Front_Porch  = 12'd3;
parameter V_Sync_Time    = 12'd6;
parameter V_Back_Porch   = 12'd29;
parameter V_Top_Border   = 12'd0;

reg [11:0] h_cnt;//行计数
reg [11:0] v_cnt;//场计数
wire h_pulse;

/***********RGB 接口时序输出****************/
assign vga_hs = (h_cnt >= H_Sync_Time);//行场同步在数据无效的时候也可能有效
assign vga_vs = (v_cnt >= V_Sync_Time);
assign vga_data = vga_de ? user_data : 16'd0; 
//数据有效区域,使能背光并且使能计数器
assign vga_de = (h_cnt >= H_Sync_Time + H_Back_Porch + H_Left_Border -1)&&
                (h_cnt <  H_Total_Time- H_Right_Border - H_Front_Porch-1)&&
                (v_cnt >= V_Sync_Time + V_Back_Porch + V_Top_Border -1)&&
                (v_cnt <  V_Total_Time - V_Bottom_Border - V_Front_Porch-1);
assign data_req = vga_de;                 
assign vga_clk = clk_65m;//这是刷新率和屏幕参数决定的

//行同步计数
always@(posedge clk_65m or negedge rst_n)
  if(!rst_n)
    h_cnt <= 0;
  else if(start_show)begin
    if(h_pulse)
        h_cnt <= 0;
    else
        h_cnt <= h_cnt+1;
  end else
    h_cnt <= 0;
assign h_pulse = (h_cnt >= H_Total_Time-1);//计一行产生一个脉冲 用于刷新行计数器和场计数器   
//场同步计数
always@(posedge clk_65m or negedge rst_n)
  if(!rst_n)
    v_cnt <= 0;
  else if(start_show)begin
    if(h_pulse)begin
        if(v_cnt == V_Total_Time - 1)
            v_cnt <= 0;
        else
            v_cnt <= v_cnt+1;
    end else
        v_cnt <= v_cnt;
  end else
    v_cnt <= 0;
//x坐标返回
assign addr_x = vga_de?(h_cnt - H_Sync_Time - H_Back_Porch - H_Left_Border +1):12'd0;;
//y坐标返回
assign addr_y = vga_de?(v_cnt - V_Sync_Time - V_Back_Porch - V_Top_Border +1):12'b0;
endmodule

ctrl模块,提供数据源,整合可将数据源替换即可

module vga_ctrl(
    input [11:0]x_addr,
    input [11:0]y_addr,
    input data_req,
    output reg [15:0]pix_data
    );
//提供显示数据
wire s_1;//指示四块区域
wire s_2;
wire s_3;
wire s_4;
wire [3:0]s_list;
localparam 
    BLACK =     16'h0000, //黑色
    BLUE  =     16'h001F, //蓝色
    RED   =     16'hF800, //红色
    PURPPLE	=   16'hF81F; //紫色
//将1920个像素点分成四个部分,
assign s_1 = data_req && x_addr >= 0   && x_addr < 256; //正在扫描第1列条纹;
assign s_2 = data_req && x_addr >= 256 && x_addr < 512;//正在扫描第2列条纹;
assign s_3 = data_req && x_addr >= 512 && x_addr < 768;//正在扫描第3列条纹;
assign s_4 = data_req && x_addr >= 768&& x_addr < 1024;//正在扫描第4列条纹;
assign s_list = {s_1,s_2,s_3,s_4};
always @(*) begin
    case (s_list)
        4'b0001:  pix_data = BLACK;
        4'b0010:  pix_data = BLUE;
        4'b0100:  pix_data = RED;
        4'b1000:  pix_data = PURPPLE;
        default:  pix_data = BLACK;
    endcase
end
endmodule

你可能感兴趣的:(FPGA积累——基础篇,fpga开发,VGA,xilinx)