FPGA学习日记(三)FPGA的VGA显示实验

  • Post author:
  • Post category:其他


最近会复习之前的知识,用正点原子的开拓者开发板做一些以前实验的复现,目的是为了

锻练在只提供目的和条件的情况下,独立设计编写模块的能力,及找出复现过程中出现的问题,最后做一个备忘日记的作用。

本次实验目的:VGA接口在显示器上显示彩条,要求分辨率为640*480, 刷新速率为60hz。

实验介绍:

VGA的全称是Video Graphics Array, 即视频图形阵列, 是一个使用模拟信号进行视频传输的标准。

所用到的引脚:



VGA时序:

不同分辨率的VGA时序参数 :

实验设计:由一个pll模块,VGA驱动模块,VGA显示模块,及顶层模块一共是四个模块。

pll:提供25MHZ时钟;

vga_dri:通过计数器得出有效数据及行场同步信号的时钟周期,输出从显示模块传来的数据中的有效数据,并输出行场同步信号;

vga_disp:按像素地址输出设置好的RGB图像数据.

顶层模块:

//VGA彩条显示 
//端口定义  
module vga_colorbar(
       sys_clk,
       sys_rst,
       
       vga_hs,
       vga_vs,
       vga_rgb
);
//I/O说明
input              sys_clk;
input              sys_rst;

output             vga_hs;
output             vga_vs;
output [15:0]      vga_rgb;

//内部信号定义
wire clk_w;
wire locked_w;

wire [15:0] pixel_data_w;
wire [9:0]  pixel_hpos_w;
wire [9:0]  pixel_vpos_w;

wire sys_rst_n;


assign sys_rst_n = sys_rst && locked_w;

//例化pll    
vga_pll u_vga_pll(
    .areset (~sys_rst),//对复位信号取反
	.inclk0 (sys_clk),
    
	.c0     (clk_w),
	.locked (locked_w)
 );   
        
//vga驱动模块
vga_driver u_vga_driver(
 //pll与系统时钟复位输入信号
    .clk_dri      (clk_w),
    .rst_dri      (sys_rst_n),

 //驱动模块输出信号   
    .vga_hs       (vga_hs),
    .vga_vs       (vga_vs),
    .vga_rgb      (vga_rgb),
//显示模块输入像素坐标数据信号    
    .pixel_hpos   (pixel_hpos_w),
    .pixel_vpos   (pixel_vpos_w),
    .pixel_data   (pixel_data_w)
 );   
    
 //vga显示模块   
vga_disp u_vga_disp(
    .clk_disp     (clk_w),
    .rst_disp     (sys_rst_n), 
    
    .pixel_hpos   (pixel_hpos_w),
    .pixel_vpos   (pixel_vpos_w),   
    .pixel_data   (pixel_data_w)
);       
endmodule

驱动模块:

//vga驱动模块
module vga_driver(
            clk_dri, 
            rst_dri,
            
            pixel_hpos,
            pixel_vpos,
            pixel_data,
            
            vga_hs,
            vga_vs, 
            vga_rgb
);

//I/O定义
input               clk_dri;
input               rst_dri;
output [9:0]        pixel_hpos;//一行像素点800
output [9:0]        pixel_vpos;//一帧525行
input [15:0]        pixel_data;//RGB565

output              vga_hs;
output 	            vga_vs;
output [15:0]	    vga_rgb;
       

//内部参量定义,vga时序参数
parameter           H_SYNC  = 10'd96;
parameter           H_BACK  = 10'd48;
parameter           H_DISP  = 10'd640;            
parameter           H_FRONT = 10'd16;
parameter           H_PRIOD = 10'd800;

parameter           V_SYNC  = 10'd2;
parameter           V_BACK  = 10'd33;
parameter           V_DISP  = 10'd480;            
parameter           V_FRONT = 10'd10;
parameter           V_PRIOD = 10'd525;

//使能信号(用于区分有效数据),请求信号(用于定位像素点坐标)
wire vga_en;
wire data_req;


//行场同步信号计数器
reg [9:0] cnt_h;
reg [9:0] cnt_v;

//VGA行场同步信号
assign vga_hs  = (cnt_h <= H_SYNC - 1'b1) ? 1'b0 : 1'b1;
assign vga_vs  = (cnt_v <= V_SYNC - 1'b1) ? 1'b0 : 1'b1;

//选定有效数据区域
assign vga_en = (((cnt_h >=  H_SYNC + H_BACK ) && (cnt_h < H_SYNC + H_BACK + H_DISP )) &&                     	              
                ((cnt_v >= V_SYNC + V_BACK ) && (cnt_v < V_SYNC + V_BACK + V_DISP))) ? 1'b1 : 1'b0;


assign vga_rgb = vga_en ? pixel_data:16'd0;

//请求信号比行有效数据先来一个周期,确定有效数据即将到来和即将结束
assign data_req = (((cnt_h >= H_SYNC + H_BACK - 1'b1) && (cnt_h <  H_SYNC + H_BACK + H_DISP - 1'b1)) &&    
                  ((cnt_v>= V_SYNC + V_BACK) && (cnt_v <  V_SYNC + V_BACK + V_DISP))) ? 1'b1 : 1'b0; 


//确定像素当前坐标
assign  pixel_hpos = data_req ? (cnt_h -(H_SYNC + H_BACK - 1'b1)) : 10'd0;
assign  pixel_vpos = data_req ? (cnt_h -(V_SYNC + V_BACK - 1'b1)) : 10'd0;


always @(posedge clk_dri or negedge rst_dri)begin
	if(!rst_dri)begin
		cnt_h <= 10'd0;
	end
	else begin 
    if(cnt_h < H_PRIOD -1'b1)begin
		cnt_h <= cnt_h + 1'd1;
        end
	else begin
		cnt_h <= 10'd0;
        end 
    end
end

always @(posedge clk_dri or negedge rst_dri)begin
	if(!rst_dri)begin
		cnt_v <= 10'd0;
	end
	else if(cnt_h == H_PRIOD - 1'b1)begin
			if(cnt_v < V_PRIOD - 1'b1)
                cnt_v <= cnt_v + 1'd1; 
            else 
                cnt_v <= 10'd0;          
    end
end

endmodule

显示模块:

module vga_disp(
        clk_disp,   
        rst_disp, 
        
        pixel_hpos, 
        pixel_vpos,        
        pixel_data
);

input               clk_disp;
input               rst_disp;
input      [9:0]    pixel_hpos;
input      [9:0]    pixel_vpos;

output reg [15:0]   pixel_data;


parameter H_DISP =10'd640;
parameter V_DISP =10'd480;

localparam WHITE = 16'b11111_111111_11111;
localparam BLACK = 16'b00000_000000_00000;
localparam RED   = 16'b11111_000000_00000;
localparam GREEN = 16'b00000_111111_00000;
localparam BLUE  = 16'b00000_000000_11111;


always @(posedge clk_disp or negedge rst_disp)begin
    if(!rst_disp)
        pixel_data <= 16'd0;
    else begin
    if (pixel_hpos >= 0 && pixel_hpos <= (H_DISP / 5) * 1)
            pixel_data <= WHITE;
     else if (pixel_hpos >= (H_DISP / 5) * 1 && pixel_hpos < (H_DISP / 5) * 2)
            pixel_data <= BLACK;
     else if (pixel_hpos >= (H_DISP / 5) * 2 && pixel_hpos < (H_DISP / 5) * 3)
            pixel_data <= RED;
     else if (pixel_hpos >=(H_DISP / 5) * 3 && pixel_hpos < (H_DISP / 5) * 4)
            pixel_data <= GREEN;
     else
            pixel_data <= BLUE;
    end
end
endmodule

时序:

问题总结:

(1)编写的时候pll的默认时钟是100MHZ,我忘记改成50MHZ的外部晶振时钟,排查了好久才想到;

(2)像素坐标到来前要使用数据请求信号来区分有效数据的真正坐标位置,当计数器没在有效信号区域时,像素坐标置零,我一开始没有用数据请求信号,如下:

assign  pixel_hpos =cnt_h -(H_SYNC + H_BACK – 1’b1);

当计数器在没有有效的区域内,像素时钟为不定值,造成混乱;

(3)使用gvim并没有想象中的好用,可能还没用熟练。

VGA显示实验总结:

重点在于驱动模块有效数据区域的选择上,围绕有效数据的位置可以定位像素地址等,若有不同的显示需求,根据不同的分辨率要求更改相应参数即可。



版权声明:本文为weixin_45299680原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。