通过本实验了解VGA接口的工作原理,熟悉VGA接口的编程方法。
本实验要求将保存在FPGA内部存储器中的静态图像通过VGA接口显示在显示器上。
在介绍VGA接口的工作原理之前,先介绍一下本实验的VGA接口电路图,如图18-1所示。其中U18为VGA控制芯片ADV7123,而J21为VGA接口。实验中通过FPGA控制U18来实现图像的输出。
图18-1 实验原理图
VGA接口是计算机领域常用的一种常用的显示接口,在计算机中一般都由VGA显示卡来实现VGA接口。VGA显示卡系统一般由控制电路、显示缓存区和视频BIOS程序三个部分组成。控制电路如图18 2所示。控制电路主要完成时序发生、显示缓冲区数据操作、主时钟选择和D/A转换等功能;显示缓冲区提供显示数据缓存空间;视频BIOS作为控制程序固化在显示卡的ROM中。
图18-2 通用VGA显示卡控制电路原理
本实验需要在显示器上显示一个640*480分辨率的静态图像,同时根据VGA接口需要产生相应的场行消影信号,具体信号比例如图18-3所示。
图18-3 有效信号和场行消影信号所占的比例
控制VGA接口要解决数据来源、数据存储、时序实现等问题,其中关键还是如何实现VGA时序。 VGA的行时序如图 18-4所示,其中包括同步脉冲(Sync a)、显示后沿(Back porch b)、显示时序段(Display interval c)和显示前沿(Front porch d)四个部分。
图18-4 VGA行时序参考图
行时序中各个部分持续时钟周期分别为:同步脉冲96像素点周期,显示后沿48像素点周期,显示时序段640像素点周期,显示前沿16像素点周期,一共800个像素点周期。在程序中对应的参数为:
parameter H_SYNC_CYC = 96; parameter H_SYNC_BACK = 48; parameter H_SYNC_ACT = 640; parameter H_SYNC_FRONT= 16; parameter H_SYNC_TOTAL= 800;
图18-5 VGA场时序参考图
场时序和行时序不同的是,行时序以像素点周期为单位,而场时序则以行周期为单位。场时序也分为四个部分组成,如图 18 5所示,各个部分持续行周期数分别为:帧同步脉冲2个行周期,帧显示后沿32个行周期,帧显示时序段个480个行周期,帧显示前沿11个行周期,一共525个行周期。在程序中对应的参数为:
parameter V_SYNC_CYC = 2; parameter V_SYNC_BACK = 32; parameter V_SYNC_ACT = 480; // 484 parameter V_SYNC_FRONT= 11; parameter V_SYNC_TOTAL= 525;
根据帧刷新频率以及每一帧的行数和每一行的像素个数来确定主时钟频率,确定主时钟之后,在FPGA中利用计数器,以计算出的各时序段时钟周期数为基准,产生不同宽度和周期的脉冲信号,再利用它们的逻辑组合构成图 18 4和图 18 5中的a、b、c、d各时序段以及D/A转换器的空白信号BLANK和同步信号SYNC。在行同步期间和场同步期间空白信号BLANK有效,而同步信号SYNC一直拉低。
在本实验中设计刷新率为60Hz/s,每帧包含525行,而每行包含800个像素点,在VGA主时钟频率为:60X525X800=25200000Hz,即25.2MHz。
整个程序由5个模块组成,如图18-6所示:
1. TE3Default是顶层模块,实例化各子模块并连接各子模块。
2. ResetDelay使系统延时20’hFFFFF个时钟周期(50MHz)后才开始工作,延时主是为了防止系统上电初期的不稳定性。
3. VGAController模块产生图 18 4和图 18 5所示的接口时序,同时控制VGAOSDRAM模块,读取VGAOSDRAM模块中ROM内保存的图像数据值,并将该图像通过VGA接口输出。
4. VGAOSDRAM模块存储待显示的图像。
5. VGAPLL模块产生频率为25.2MHz的VGA接口所需的主时钟。
图18-6 程序总体架构
TE3Default是顶层模块,实例化各模块。TE3Default输入输出接口如下:
input CLOCK_27 //27 MHz时钟。 input CLOCK_50 //50 MHz时钟。 output VGA_CLK //VGA时钟,25.2MHz。 output VGA_HS //VGA行同步信号线H_SYNC output VGA_VS; //VGA场同步信号线V_SYNC output VGA_BLANK; //VGA空白信号指示信号BLANK output VGA_SYNC; //VGA SYNC output [9:0] VGA_R; //VGA Red[9:0] output [9:0] VGA_G; //VGA Green[9:0] output [9:0] VGA_B; //VGA Blue[9:0]
系统延时20’hFFFFF个系统周期后才开始工作。具体程序如下所示:
module Reset_Delay(iCLK,oRESET); input iCLK; output reg oRESET; reg [19:0] Cont; always@(posedge iCLK) begin if(Cont!=20'hFFFFF) begin Cont <= Cont+1; oRESET <= 1'b0; end else oRESET <= 1'b1; end endmodule
VGA_Controller模块主完成两部分工作:产生图 18 4和图 18 5所示的时序;产生像素值存储的地址信息。其接口为如下所示:
// Host Side output reg [19:0] oAddress;//输出像素值存储的地址信号 output reg [9:0] oCoord_X; //行内像素序号 output reg [9:0] oCoord_Y; //场内行周期序号 input [9:0] iRed;//红色信号 input [9:0] iGreen; input [9:0] iBlue; // VGA Side output [9:0] oVGA_R; output [9:0] oVGA_G; output [9:0] oVGA_B; output reg oVGA_H_SYNC; // VGA H_SYNC:行同步信号线 output reg oVGA_V_SYNC; //VGA V_SYNC:帧同步信号线 output oVGA_SYNC; //VGA同步信号 output oVGA_BLANK; //VGA BLANK: 空白信号指示信号 output oVGA_CLOCK; // VGA时钟:25.2MHz // Control Signal input iCLK; // 输入时钟:25.2MHz input iRST_N;//全局同步复位 VGA_Controller模块产生图 18 4和图 18 5所示时序的程序如下所示: assign oVGA_BLANK = oVGA_H_SYNC & oVGA_V_SYNC; assign oVGA_SYNC = 1'b0; assign oVGA_CLOCK = iCLK; //根据25.2Mhz的时钟生成行同步信号 always@(posedge iCLK or negedge iRST_N) begin if(!iRST_N) begin H_Cont <= 0; //像素点计数器 oVGA_H_SYNC <= 0; //行同步信号 end else begin if( H_Cont < H_SYNC_TOTAL )// H_Cont小于行周期800,一直增加。 H_Cont <= H_Cont+1; else H_Cont <= 0; if( H_Cont < H_SYNC_CYC ) // 生成行同步信号 oVGA_H_SYNC <= 0; else oVGA_H_SYNC <= 1; end end // 根据行同步信号产生帧同步信号 always@(posedge iCLK or negedge iRST_N) begin if(!iRST_N) begin V_Cont <= 0;//行计数器 oVGA_V_SYNC <= 0; //帧同步信号 end else begin // When H_Sync Re-start if(H_Cont==0) begin if( V_Cont < V_SYNC_TOTAL )// V_Cont小于行周期525,一直增加。 V_Cont <= V_Cont+1; else V_Cont <= 0; if( V_Cont < V_SYNC_CYC ) // 生成场同步信号 oVGA_V_SYNC <= 0; else oVGA_V_SYNC <= 1; end end end VGA_Controller模块产生像素值存储地址的程序如下所示: // 生成地址信息 always@(posedge iCLK or negedge iRST_N) begin if(!iRST_N) begin oCoord_X <= 0;//当前像素点在当前行内的序号。 oCoord_Y <= 0;//当前像素所在行在当前帧内的序号。 oAddress <= 0; //产生的地址信号。 end else begin if( H_Cont>=X_START && H_Cont<X_START+H_SYNC_ACT && V_Cont>=Y_START && V_Cont<Y_START+V_SYNC_ACT ) begin oCoord_X <= H_Cont-X_START; oCoord_Y <= V_Cont-Y_START; //根据所处的行数和所处的行中的像素数生成当前像素存储的地址。 oAddress <= oCoord_Y*H_SYNC_ACT+oCoord_X-3; end end end
这里需要说明的是因为FPGA内部的RAM大小有限,无法存储10位三基色的图像数据,所以这里只存储了黑白2值图像。VGAOSDRAM模块根据读取出来的2值图像的值,也即是根据读取出来的是0还是1而给出相应的10位的三基色。其接口如下所示:
output reg [9:0] oRed;//输出像素色彩信号 output reg [9:0] oGreen; //输出像素色彩信号 output reg [9:0] oBlue; //输出像素色彩信号 input [18:0] iVGA_ADDR; //输出像素值存储的地址信号 input iVGA_CLK; // VGA时钟:25.2MHz input [18:0] iWR_ADDR; //输入写地址信号 input iWR_DATA;//输入待写入数据 input iWR_EN;//输入写使能 input iWR_CLK;//输入写时钟 input [9:0] iON_R;//当存储的像素点数据位1’b1时,对应的RGB三色值 input [9:0] iON_G; input [9:0] iON_B; input [9:0] iOFF_R; //当存储的像素点数据位1’b0时,对应的RGB三色值 input [9:0] iOFF_G; input [9:0] iOFF_B; input iRST_N; 具体程序如下所示: reg [2:0] ADDR_d;//存储输入地址信号的低3位 reg [2:0] ADDR_dd;//为时序需要将ADDR_d延时一个时钟周期 always@(posedge iVGA_CLK or negedge iRST_N) begin if(!iRST_N) begin oRed <= 0; oGreen <= 0; oBlue <= 0; ADDR_d <= 0; ADDR_dd<= 0; end else begin ADDR_d <= iVGA_ADDR[2:0]; ADDR_dd <= ~ADDR_d; oRed <= ROM_DATA[ADDR_dd]? iON_R : iOFF_R; //根据该像素对应 //在存储器的值为0还是为1,输出项对应的10为RGB三色信号 oGreen <= ROM_DATA[ADDR_dd]? iON_G : iOFF_G; oBlue <= ROM_DATA[ADDR_dd]? iON_B : iOFF_B; end end //存储了图像的RAM。 Img_RAM u0 ( //写入接口,目前没有使用 .data(iWR_DATA), .wren(iWR_EN), .wraddress({iWR_ADDR[18:3],~iWR_ADDR[2:0]}), .wrclock(iWR_CLK), // Read Out Side .rdaddress(iVGA_ADDR[18:3]), .rdclock(iVGA_CLK), .q(ROM_DATA));
读取出来的数据为8位宽,而8位宽的数据中的每一位对应一个像素点,同时8位位宽数据的低位对应的像素应该先显示在屏幕上,截取输入地址信号iVGA_ADDR的低3位作为MUX,输出相应的像素信息。
文件名 | 功能 |
TE3Default.v|顶层模块。| |ResetDelay.v | 延时模块。 |
VGAController.v|VGA控制逻辑模块| |VGAOSDRAM.v|存储显示数据模块。| |VGAPLL.v | 产生频率为25.2MHz的VGA时序所需的主时钟。 |
演示设备:核心板、扩展板、带VGA接口的显示器。
演示方法:将显示器连接到开发板上面的VGA接口上,然后把程序下载到开发板上既可以看到显示器上显示的图像。