VGA(Video Graphics Array)是IBM在1987年随PS/2机一起推出的一种视频传输标准,具有分辨率高、显示速率快、颜色丰富等优点,在彩色显示器领域得到了广泛的应用。不支持热插拔,不支持音频传输。
VGA接口
行同步时序
场同步时序
分辨率
硬件简介
VGA接口信号HS(1bit),VS(1 bit), R(8bit), G(8bit), B(1bit)。VGA显示器常用的分辨率为640x480,即操作的时候FPGA想办法向VGA显示器输出640行480列个点位数据(共307200个数据)。那具体是如何操作的呢?
基本操作流程:VS叫场扫描信号或者帧扫描信号,首先VS发一个负脉冲,告诉显示器我要开始发送一副图片了,你准备接收。然后,HS行扫描信号,发一个负脉冲,告诉显示器我要开始发送第一行数据了,准备接受。跟在HS负脉冲信号后,接着就每个一个时钟周期发送一组R,G,B点位的数据,一直发送640组。接下来HS再发一个负脉冲,告诉显示器我要开始发送第二行数据了,准备接收,然后发送第2行的640组R,G,B数据,以此类推,第3行,第4行。一直发送到第480行结束后,所有307200个数据就发送完全了。然后VS信号再发一个负脉冲,发第二幅图片的信息。以此类推。
具体时序数据:下图就对应了具体行扫描和场扫描阶段每个操作具体所需要的周期数。一目了然。注意HS,VS负脉冲后要等待几个周期才正式发送数据这是为了满足VGA的器件要求而定的。下图给了周期数,然后计算VGA的时钟频率。一般VGA显示器的刷新频率为60帧/s,每一秒显示60幅图片,即一帧数据需要1/60s发送。由下图可知,完整发送一帧数据(一副图片)需要800*525=420000个周期。则VGA时钟频率为420000/(1/60)=25.2MHZ。近似等于25MHZ。开发板上的主频时钟为50MHZ,所以只要二分频就OK啦。
代码如下:
module vga( output [7:0] VGA_B, output VGA_BLANK_N, output VGA_CLK, output [7:0] VGA_G, output VGA_HS, output [7:0] VGA_R, output VGA_SYNC_N, output VGA_VS, input CLOCK_50, input KEY ); wire[8:0] q; rom2 rom_inst ( .address( address ), .clock( CLOCK_50 ), .rden( ~rdn ), .q ( q ) ); assign VGA_BLANK_N=~rdn; wire[23:0]datain={q[8:6],5'd0,q[5:3],5'd0,q[2:0],5'd0}; wire[18:0]rd_a; wire[18:0]address=(rd_a[18:10]*640)+rd_a[9:0]; wire rdn; wire vga_clk; assign VGA_CLK=vga_clk; vga_c u0( .clk(CLOCK_50),//50MHZ .clrn(KEY), .datain(datain), .h_count(), .v_count(), .vga_clk(vga_clk), .rdn(rdn), //read enable"0"active .rd_a(rd_a),//read_address={row(9),col(10bit)} .hs(VGA_HS), .vs(VGA_VS), .r(VGA_R), .g(VGA_G), .b(VGA_B)); endmodule
`timescale 1ns/1ns module vga_c( input clk, // 系统时钟50MHZ input clrn, //复位信号 input [23:0]datain, //输入R,G,B数据24bit output rdn, //读使能信号,低电平有效。 output [18:0]rd_a, //读地址,read_address={row(9),col(10bit)} output reg[9:0]h_count=10'd0, //列计数器 0-799 output reg[9:0]v_count=10'd0, //行计数器 0-524 output reg vga_clk=1'b0, //vga时钟 25MHZ //VGA接口信号 output hs, //行扫描信号 output vs, //场扫描信号 output [7:0]r,g,b //R,G,B输出信号 ); //refreshrate=25*10^6/((96+48+640+16)*(480+2+33+10))=59.5=60 //1.VGA时钟生成,vga_clk:25MHZ always @( negedge clrn or posedge clk ) vga_clk<=(!clrn)?1'b0: ~vga_clk; //2.计数器v_count(0-524) and h_count(0-799) always @( negedge clrn or posedge vga_clk ) if(!clrn) begin v_count<=10'd0; h_count<=10'd0; end else if(v_count==10'd524) v_count<=10'd0; else begin if(h_count==10'd799) begin h_count<=10'd0; v_count<=v_count+1'b1; end else h_count<=h_count+1'b1; end // 3.锁存输入数据data_in,rdn=0时,外部有存储器有40ns的时间提供datain数据 reg video_out=1'b0; reg [23:0]data_reg=24'd0; always @(negedge clrn or posedge vga_clk) if(!clrn) begin video_out<=1'b0; data_reg<=24'd0; end else begin video_out<=~rdn; data_reg<=datain; end //4.接口信号生成 assign hs=(h_count>=96); //HS波形输出 assign vs=(v_count>=2); //VS波形输出 wire [9:0]rol=v_count-10'd35; //计算行地址 wire [9:0]col =h_count-10'd143; //计算列地址 assign rd_a={rol[8:0],col[9:0]}; //行列地址拼接成一个rd_a assign rdn=~(((h_count>=10'd143)&&(h_count<10'd783))&&((v_count>=10'd35)&&(v_count<10'd515))); //只有在有效的数据位rdn才置0 assign r=(video_out)?data_reg[23:16]:8'd0; assign g=(video_out)?data_reg[15:8]:8'd0; assign b=(video_out)?data_reg[7:0]:8'd0; endmodule
rom ip设置
图片数据mif文件生成:可以使用工具或MATLAB