基于VerilogHDL的VGA驱动设计
VGA简介
VGA接口共有15针,分成3排,每排5个孔,显卡上应用最为广泛的接口类型,绝大多数显卡都带有此种接口。它传输红、绿、蓝模拟信号以及同步信号(水平和垂直信号)。基于FPGA设计来驱动VGA也可以达到我们平时所看到的显示结果。以Quartus II软件作为开发环境,Verilog HDL硬件描述语言描述VGA驱动各功能模块,不仅可以很方便的调整内部逻辑电路,优化性能,还可以显示不同的效果。 FPGA的工作速度比较快、处理像素点的速度也比较快,所以具有较好的性能和稳定性。
设计流程
我大概的设计思路如下:
我们设计一个东西时,思路很重要。当我们要通过驱动VGA来显示时,那么我们就先要选择显示的内容,即选择图片并确定控制这张图片的大小。为什么要控制大小呢?我们后面接着说。之后我们要让这个图片以什么模式显示,则我们就要选择显示模式。然后我们要按照所选的显示模式,设置该模式所需要的工作时钟,若学习板上的时钟不足以支持工作,那么我们就要使用的锁相环定制出所需要的时钟频率。当我们获取足够的时钟频率后,我们就可以真正的进行设计VGA的驱动了。当我们选择好显示模式时,那意味着我们的同步时序模块就可以定下来了。我们要根据所选模式的时序图来设计同步时序模块。一些要点我们会在后面详细说明。当我们选择的图片直接给我们的开发板,它是读不懂的。所以我们要把先把图片转换成开发板能读懂的文件,然后我们在把文件存到我们的开发板里面去,这时候我们就要考虑,我们开发板最多可以存多少东西呢?所以我们的显示的图片的大小会受到限制,不像电脑主机有非常之多的内存。当我们完成把图片转换成开发板能读得懂文件后,我们就可以设计输出模块了。我们的输出模块是在同步时序模块的基础是建立的,我们输出模块是根据图片的大小来设计的。完成我能的所有的准备模块之后,我们需要一个总的模块将全部模块连接在一起,然后我们就可以进行测试了。这就是我设计的大概过程。
流程图
显示模式表
VGA的协议
**VGA 协议主要由 5 个输入信号组成,亦是 HSYNC Signal,VSYNC Signal, R,G,B Signal。说简单一点, HSYNC Signal 是 “水平同步信号”(horizontal Synchronize) , VSYNC Signal 是“垂直同步信号”( vertical Synchronize) , R, G ,B Signal 是“ 红色-绿色-蓝色 颜色信号” 。这一点我们可以通过VGA接口的引脚图就可以知道。
以800X600@60为例
其中VSYNC Signal为行像素,HSYNC Signal为列像素。1 个行像素 = 1056 个列像素。水平同步信号和垂直同步信号同样分为四段同步段(a和o)、 后廊段(b和p)、激活段(c和q)、前廊段(d和r)。由时序图我们可以知道同步段电平是拉低的,后廊段将电平拉高、激活段保持高电平、前廊段将电平拉低。而我们要显示的图形是在激活段中显示的,列像素 > 216 && 列像素 < 1017 && 行像素 >27&& 行像素 <627。时序读懂,其他都好做。
105662860=39790080
至少需要一个40MHz晶振
根据时钟需求,定制时钟频率,再根据时序图搭建同步模块,再根据图片大小建立输出模块,最后用一个总层来把关联端口关联起来。
对输出信息处理,生成mif文件再把文件存进FPGA的ROM(即生成对应的ROM)。
如对输出信息有分辨率要求,提高RGB的位,将RGB位提到8位,则2^8=256,可高度还原输出,不然可能产生失真。代码如下
module sync_module//同步模块
(
CLK, RSTn,
VSYNC_Sig, HSYNC_Sig, Ready_Sig,
Column_Addr_Sig, Row_Addr_Sig
);
input CLK;
input RSTn;
output VSYNC_Sig;//行同步信号
output HSYNC_Sig;//列同步信号
output Ready_Sig;//有效区域信号
output [10:0]Column_Addr_Sig;//有效区域像素点列坐标
output [10:0]Row_Addr_Sig;
//有效区域像素点行坐标
/********************************/
reg [10:0]Count_H;//水平方向计数变量
always @ ( posedge CLK or negedge RSTn )
if( !RSTn )
Count_H <= 11'd0;
else if( Count_H == 11'd1056 )//列方向共 1056
Count_H <= 11'd0;
else
Count_H <= Count_H + 1'b1;
/********************************/
reg [10:0]Count_V;//垂直方向计数变量
always @ ( posedge CLK or negedge RSTn )
if( !RSTn )
Count_V <= 11'd0;
else if( Count_V == 11'd628 )
Count_V <= 11'd0;
else if( Count_H == 11'd1056 )//一行结束
Count_V <= Count_V + 1'b1;
/********************************/
reg isReady;
always @ ( posedge CLK or negedge RSTn )
if( !RSTn )
isReady <= 1'b0;
//else if( ( Count_H > 11'd216 && Count_H < 11'd1017 ) &&( Count_V > 11'd27 && Count_V < 11'd627 ) )//C 段 Q 段
else if( ( Count_H > 11'd572 && Count_H < 11'd636) &&( Count_V > 11'd313 && Count_V < 11'd377 ) )//C 段 Q 段控制显示范围
isReady <= 1'b1;
else
isReady <= 1'b0;
/*********************************/
//垂直、水平同步信号产生;有效区域信号驱动输出
assign VSYNC_Sig = ( Count_V <= 11'd4 ) ? 1'b0 : 1'b1;
assign HSYNC_Sig = ( Count_H <= 11'd128 ) ? 1'b0 : 1'b1;
assign Ready_Sig = isReady;
/********************************/
assign Column_Addr_Sig = isReady ? Count_H - 11'd572 : 11'd0;// 确保列地址从0开始
assign Row_Addr_Sig = isReady ? Count_V - 11'd313 : 11'd0; // 确保行地址从0开始
endmodule
//产生有效区域像素点的坐标
//输出模块
module vga_control_module(
CLK, RSTn,
Ready_Sig, Column_Addr_Sig, Row_Addr_Sig,
Red_Rom_Data, Green_Rom_Data, Blue_Rom_Data,
Rom_Addr,
Red_Sig, Green_Sig, Blue_Sig
);
input CLK;
input RSTn;
input Ready_Sig;
input [63:0]Red_Rom_Data;
input [63:0]Green_Rom_Data;
input [63:0]Blue_Rom_Data;
output [5:0]Rom_Addr;
input [10:0]Column_Addr_Sig;
input [10:0]Row_Addr_Sig;
output Red_Sig;
output Green_Sig;
output Blue_Sig;
//reg isRectangle;//矩形标志寄存器
//读取行地址低6位
reg [5:0]m;
always @ ( posedge CLK or negedge RSTn )
if( !RSTn )
m <= 6'd0;
else if( Ready_Sig && Row_Addr_Sig < 64 )
m <= Row_Addr_Sig[5:0];
else
m <= 6'd0;
//读取列地址低6位
reg [5:0]n;
always @ ( posedge CLK or negedge RSTn )
if( !RSTn )
n <= 6'd0;
else if( Ready_Sig && Column_Addr_Sig < 64 )
n <= Column_Addr_Sig[5:0];
else
n <= 6'd0;
assign Rom_Addr = m; //输出存储器地址
/************************************/
assign Red_Sig = Ready_Sig ? Red_Rom_Data[ 6'd63 - n ] : 1'b0;//读取存储器内容“逐行扫描”,"高位在前"
assign Green_Sig = Ready_Sig ? Green_Rom_Data[ 6'd63 - n ] : 1'b0;
assign Blue_Sig = Ready_Sig ? Blue_Rom_Data[ 6'd63 - n ] : 1'b0;
endmodule
/***********************************/
///
//顶层
module vga_module
(
CLK, RSTn,
VSYNC_Sig, HSYNC_Sig,
Red_Sig, Green_Sig, Blue_Sig);
input CLK;
input RSTn;
output VSYNC_Sig;
output HSYNC_Sig;
output Red_Sig;
output Green_Sig;
output Blue_Sig;
/*************************************/
wire CLK_40Mhz;
PLL_40MHz U1
(
.inclk0( CLK ),// input - from top
.c0( CLK_40Mhz )// output - inter global
);
/**************************************/
wire [10:0]Column_Addr_Sig;
wire [10:0]Row_Addr_Sig;
wire Ready_Sig;
sync_module U2
(
.CLK( CLK_40Mhz ),
.RSTn( RSTn ),
.VSYNC_Sig( VSYNC_Sig ),// output - to U3
.HSYNC_Sig( HSYNC_Sig ),// output - to U3
.Column_Addr_Sig( Column_Addr_Sig ), // output - to U3
.Row_Addr_Sig( Row_Addr_Sig ),// output - to U3
.Ready_Sig( Ready_Sig )// output - to U3
);
/******************************************/
wire[63:0]Red_Rom_Data;
Red_rom U3
(
.clock(CLK_40Mhz),
.address(Rom_Addr),
.q(Red_Rom_Data)
);
//
wire[63:0]Green_Rom_Data;
Green_rom U4
(
.clock(CLK_40Mhz),
.address(Rom_Addr),
.q(Green_Rom_Data)
);
//
wire[63:0]Blue_Rom_Data;
Blue_rom U5
(
.clock(CLK_40Mhz),
.address(Rom_Addr),
.q(Blue_Rom_Data)
);
//
wire[5:0]Rom_Addr;
vga_control_module U6
(
.CLK( CLK_40Mhz ),
.RSTn( RSTn ),
.Ready_Sig( Ready_Sig ),// input - from U2
.Column_Addr_Sig( Column_Addr_Sig ), // input - from U2
.Row_Addr_Sig( Row_Addr_Sig ),// input - from U2
.Red_Rom_Data(Red_Rom_Data),
.Green_Rom_Data(Green_Rom_Data),
.Blue_Rom_Data(Blue_Rom_Data),
.Rom_Addr(Rom_Addr),
.Red_Sig( Red_Sig ),// output - to top
.Green_Sig( Green_Sig ),// output - to top
.Blue_Sig( Blue_Sig )// output - to top
);
endmodule
/*******************************************/
对应时钟和ROM文件用Quartus II生成即可。