原创 FPGA学习笔记5——VGA驱动

2015-11-7 23:51 2500 12 17 分类: FPGA/CPLD

VGA驱动

    VGA接口是一个常用到的接口,今天笔者带大家来探讨下怎么样编写VGA驱动。从数据手册上我们可以看到VGA接口一共有15个引脚,但其实真正需要我们去操作的只有5个引脚,他们分别是行同步信号,场同步信号,以及RGB三基色这三个信号。我们需要通过FPGA去模拟行同步信号以及场同步信号的时序,并且控制RGB的信号,来输出图像到显示屏上。
    一个需要提醒的点就是,我们使用FPGA输出的是数字信号,而驱动显示屏的RGB信号必须是模拟信号,所以在这里,我们需要一个DA转换芯片,或者使用电阻网络来完成DA转换,把数字信号转化为模拟信号,才可以获得彩色的信号。
    好了,我们以显示一张800*600*60Hz的图像为例,首先我们来看一下VGA的时序是怎么样的
    20151107152620941.jpg
     从上图可以看到,对于行同步信号,分为同步脉冲a,显示后沿b,显示时序段c和显示前沿d。一共有a,b,c和d四个段,其中a段是需要把电平拉低,而b,c,d段则需要把电平拉高,而且我们看到只有在c段,视频帧才是高电平的,证明在这时,RGB信号才有效。
     对于场同步信号,分析也与行同步信号类似。
    2.jpg
    从这里我们可以看到,对于一张800*600*60Hz的图像来说,其一行的像素点应该为1056个,有效的像素段为c段的800个,而一列的像素点为628个,其有效的像素段为c段的600个。通过计算 1056*628*60=39790080=39.79008M,我们可以得知,一秒需要刷新接近40M个像素点,所以我们需要一个40MHz的时钟信号。对于行同步信号来说,他的一个时序周期为1056个时钟脉宽长度,而对于场同步信号来说,他的一个时序周期则为1056*628=663168个时钟脉宽长度。
   下面笔者以一个屏幕显示蓝绿红彩条的图像为例,这个让笔者想起了翡翠台的台标。
   我们首先以Top-down的设计方法,设计出整个工程的模块框图
   20151107170348553.jpeg
     整个设计分为3个模块,第一个模块为PLL模块,把50MHz的时钟4倍频为200MHz,再5分频得到40MHz的时钟,输入到VGA控制模块以及输出模块。
     第2个模块为VGA控制模块,主要是模拟行同步信号HS和场同步信号VS的时序,并告诉VGA输出模块,什么时候该输出RGB信号。
     第3个模块则是VGA输出模块,在VGA控制模块的操控下,有序输出RGB信号。

     对于PLL模块,我们使用IP核得到1个40MHz的时钟,这是非常简单的。
     对于VGA控制模块,代码具体如下:
module vga_ctrl(clk,rst_n,hs,vs,valid,x,y);
input clk;
input rst_n;
output hs;//输出行同步信号
output vs;//输出场同步信号
output valid;//行同步信号和场同步信号在都在c段时拉高
output [10:0] x,y;//行坐标x为1-800,列坐标为y为1-600

reg [10:0]count_h;//行同步信号计时器
reg [10:0]count_v;//场同步信号计时器
reg valid;

always @(posedge clk or negedge rst_n)
if (!rst_n)
count_h<=11'd0;
else if (count_h==11'd1055)
count_h<=11'd0;
else 
count_h<=count_h+11'd1;
always @(posedge clk or negedge rst_n)
if (!rst_n)
count_v<=11'd0;
else if (count_v==11'd627)
count_v<=11'd0;
else if(count_h==11'd1055)
count_v<=count_v+11'd1;
always @(posedge clk or negedge rst_n)
if (!rst_n)
valid<=1'b0;
else if(count_h>=11'd215&&count_h<11'd1015&&count_v>=11'd3&&count_v<11'd626)
valid<=1'b1;
else 
valid<=1'b0;
assign hs=(count_h<=11'd127)? 1'b0:1'b1;//a段拉低,b,c,d拉高
assign vs=(count_v<=11'd3)? 1'b0:1'b1;//a段拉低,b,c,d拉高
assign x=valid?count_h-11'd215:11'd0;//x坐标,仅c段有效
assign y=valid?count_v-11'd2:11'd0;//y坐标,仅c段有效

endmodule 


对于 always @(posedge clk or negedge rst_n)
if (!rst_n)
count_h<=11'd0;
else if (count_h==11'd1055)
count_h<=11'd0;
else 
count_h<=count_h+11'd1;
always @(posedge clk or negedge rst_n)
if (!rst_n)
count_v<=11'd0;
else if (count_v==11'd627)
count_v<=11'd0;
else if(count_h==11'd1055)
count_v<=count_v+11'd1;
这一段代码,count_h计数器在时钟每来一次便自加1,周期为前面所说的1056个时钟脉冲宽度,count_v,也一样,不过是在count_h完成一次周期时才自加1,所以他的周期为1056*628=663168个时钟脉冲宽度。

always @(posedge clk or negedge rst_n)
if (!rst_n)
valid<=1'b0;
else if(count_h>=11'd215&&count_h<11'd1015&&count_v>=11'd3&&count_v<11'd626)
valid<=1'b1;
else 
valid<=1'b0;
这一段代码valid仅在行同步信号和场同步信号均在c段时才为高电平,否则为低电平。




VGA的控制模块分析就到此结束,下面看一下VGA输出模块的代码实现
module vga_output(clk,rst_n,x,y,valid,rs,gs,bs);

input clk,rst_n;
input [10:0]x,y;
input valid;
output [4:0]rs;//RPB565信号,R信号为5bit
output [5:0]gs;//RPB565信号,G信号为6bit
output [4:0]bs;//RPB565信号,B信号为5bit


reg [4:0]rs;
reg [5:0]gs;
reg [4:0]bs;
always @(posedge clk or negedge rst_n)
 if (!rst_n)
begin 
rs<=5'd0;
gs<=5'd0;
bs<=5'd0;
end
 else if (valid&&x>=11'd0&&x<=11'd800)
begin 
if (y>=11'd0&&y<=11'd200)
begin 
rs<=5'd0;
gs<=6'd0;
bs<=5'b11111;
end
else if (y>11'd200&&y<=11'd400)
begin
rs<=5'd0;
gs<=6'b111111;
bs<=5'd0;
end
else if (y>11'd400&&y<=11'd600)
begin 
rs<=5'b11111;
gs<=6'd0;
bs<=5'd0;
end
end
endmodule

因为我们实现的是蓝绿红彩条,最上面的矩形为蓝色,矩形范围为y从0到200,蓝基色5个位全部都取1,而其他两个基色则为0。中间的矩形为绿色,矩形范围为y从200到400,绿基色6个位全部取1,而其他两个基色则为0。最下面的矩形则为蓝色,矩形范围为y从400到600,红基色5个位全部取1,则可以完成彩条的逻辑了。

最后把模块例化
module vga(clk,rst_n,vs,hs,rs,gs,bs);
input clk;
input rst_n;
output vs,hs;

wire clk_40MHz;
wire [10:0]x,y;
wire valid;

output [4:0]rs;
output [5:0]gs;
output [4:0]bs;

pll_ctrl pll_ctrl_inst (
.inclk0 ( clk ),
.c0 ( clk_40MHz )
);

vga_ctrl i1(
.clk( clk_40MHz ),
.rst_n( rst_n ),
.hs( hs ),
.vs( vs ),
.valid( valid),
.x( x ),
.y( y )
);

vga_output   i2(
.clk( clk_40MHz ),
.rst_n( rst_n ),
.x( x ),
.y( y ),
.valid( valid ),
.rs( rs ),
.gs( gs ),
.bs( bs )
);


endmodule

20151107235006325.jpg


实验的效果如图所示
 

文章评论5条评论)

登录后参与讨论

广州老伯 2015-11-30 23:04

这个图网上找的,的确错了

用户1836944 2015-11-30 10:30

博主麻烦问下,为什么在行时序里面行后沿反而在行前沿的前面。

用户377235 2015-11-12 19:07

可以的话,做一个显示图像的更好

用户1573777 2015-11-11 17:19

count_v计数错误,场信号不对。

用户1136505 2015-11-11 10:21

VGA 的行跟場 圖貼錯了, valid 也不大對 . 感謝分享
相关推荐阅读
广州老伯 2015-11-18 21:40
FPGA学习笔记6——VGA显示图片
VGA显示图片 在上一个笔记熟悉了VGA的时序后,显示了一个翡翠台彩条的图片,采用的思想是在vga_output模块上,根据x,y坐标的不同,把图片分成了三个800*200的区域,分别在三个区域上显示...
广州老伯 2015-11-05 21:24
FPGA学习笔记4——PLL的IP核使用
PLL的IP核使用    PLL锁相环的IP核应该是在FPGA开发中用得最多的一个模块了,下面介绍如何使用PLL的IP核。首先,我们新建一个工程,新建一个verilog文件,保存。    然后选择to...
广州老伯 2015-11-05 13:18
FPGA学习笔记3——booth乘法器设计
Booth乘法器设计booth乘法器是一种可以计算带符号数的乘法器,他的实现思维较为简单,具体的重要逻辑是设乘数分别为a[3:0]和b[3:0],积为y[8:1],在这里y其实是一个9位的寄存器,只不...
广州老伯 2015-11-03 17:24
FPGA学习笔记2——传统乘法器设计
传统乘法器设计传统乘法器我们以传统的乘法竖式计算为例说明一下。以10*9为例,即二进制1010*1001有  1 0 1 0                   X    1 0 0 1     ...
广州老伯 2015-10-30 23:29
FPGA学习笔记1——分频电路设计
    分频就是用一个时钟信号通过一定的电路结构变成不同频率的时钟信号,这里介绍一下整数分频电路的设计方法。整数分频电路有偶数分频和奇数分频两种,我们以实现占空比为50%的分频电路为例子来解释一下...
我要评论
5
12
关闭 站长推荐上一条 /2 下一条