FPGA学习笔记——VGA

正文

参考《Verilog HDL那些事儿建模篇》而作。
VGA分为VGA硬件接口和VGA协议。VGA硬件接口没什么。下面先介绍VGA协议。
VGA 协议主要由 5 个输入信号组成:HSYNC Signal, VSYNC Signal, RGB Signal。 说简单一点,HSYNC Signal 是“列同步信号”, VSYNC Signal 是“行同步信号”, RGB Signal 是“红色、绿色、蓝色、颜色信号”。

FPGA学习笔记——VGA_第1张图片
VGA扫描一帧屏幕是由“m行扫描”和“n列填充”组成。以800 x 600 x 60HZ(一下涉及到的所有与分辨率有关的都以他为例)为例:
扫描次序如下:
扫描第 0 行 - 在第 0 行,列填充 0~799。
扫描第 1 行 - 在第 1 行,列填充 0 ~ 799。
扫描第 2 行 - 在第 2 行,列填充 0 ~ 799。
扫描第 m 行 - 在第 m 行,列填充 0 ~ 799。
扫描第 598 行 - 在第 598 行,列填充 0 ~ 799。
直到描第 599 行- 在第 599 行,列填充 0 ~ 799

宏观上,一帧屏幕的显示是由 600 行从上至下扫描,800 列从左至右填充,然而微观上,一行的行 扫描是由超过 800 个列填充完成。

FPGA学习笔记——VGA_第2张图片

FPGA学习笔记——VGA_第3张图片

HSYNC Signal用来控制“列填充”。
a(同步段):拉低128个列像素(这里的像素和图像的像素不太一样,下面会介绍)。
b(后廊段):拉高88个列像素。
c(激活段):拉高800个列像素。
d(前廊段):拉高40个列像素。
所以一列总共有1056个列像素。

VSYNC Signal 是用来控制“行扫描”。
o(同步段):拉低4个行像素。
p(后廊段):拉高23个行像素。
q(激活段):拉高600个行像素。
r(前廊段):拉高一个行像素。
所以一行总共有628个行像素。

一个行像素是以列像素为单位:一个行像素 = 1056个列像素。
一个列像素以时间为单位:一个列像素 = 25ns;一个行像素 = 1056 x 25ns = 2.64us。
FPGA学习笔记——VGA_第4张图片
上图表示 HSYNC Signal 只有在的 C 段 (红色部分)和 VSYNC Signal 的 q 段 (黄色部分)的激活段,数据的输入才有效。
交叉部分的表达式可以描述为:列像素 > 216 && 列像素 < 1017 && 行像素 > 27 && 行像素 < 627。
先实现VGA驱动模块:
FPGA学习笔记——VGA_第5张图片
PLL模块用于输出40MHZ的时钟频率,同步模块对 HSYNC Signal 和 VSYNC Signal 进行控制,用于控制VGA的时序。VGA控制模块用于图像的显示控制。
下面先实现同步模块:

module sync
(
 CLK,
 RSTn,
 HSYNC_Sig,
 VSYNC_Sig,
 Ready_Sig,
 Column_Addr_Sig,
 Row_Addr_Sig
);
 input CLK;
 input RSTn;
 output HSYNC_Sig;    //HSYNC信号线,控制“列填充”
 output VSYNC_Sig;    //VSYNC信号线,控制“行扫描”
 output Ready_Sig;    //数据输入的有效时间标志位,1表示可以输入数据
 output [10:0] Column_Addr_Sig; //列像素地址信号
 output [10:0] Row_Addr_Sig;  //行像素地址信号
 
 //列像素计数器
 reg [10:0] count_H;
 always @ (posedge CLK , negedge RSTn)
 begin
  if(!RSTn)
   count_H <= 11'd0;
  else if(count_H == 11'd1056)
   count_H <= 11'd0;
  else 
   count_H <= count_H + 1'b1;
 end
 
 //行像素计数器
 reg [10:0] count_V;
 always @ (posedge CLK , negedge RSTn)
 begin
  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;
 end 
 
 
 reg isReady;
 always @ (posedge CLK , negedge RSTn)
 begin
  if(!RSTn) 
   isReady <= 1'b0;
  else if((count_H > 11'd216 && count_H < 11'd1017) &&
    (count_V > 11'd27 && count_V < 11'd627))  //数据输入有效区域
   isReady <= 1'b1;        //表示可以输入数据了
  else
   isReady <= 1'b0;
 end
 
 assign VSYNC_Sig = (count_V <= 11'd4) ? 1'b0 : 1'b1;  //表示出时序图的时序,o,p,q,r,段
 assign HSYNC_Sig = (count_H <= 11'd128) ? 1'b0 : 1'b1; //表示出时序图的时序,a,b,c,d,段
 assign Ready_Sig = isReady;        
 
 assign Column_Addr_Sig = isReady ? (count_H - 11'd217) : 11'd0;//可以输入数据时顺便输出一下现在的列像素
 assign Row_Addr_Sig = isReady ? (count_V - 11'd28) : 11'd0; //可以输入数据时顺便输出一下现在的行像素
 
endmodule

然后是VGA控制模块:

module vga_control
(
 CLK,
 RSTn,
 Ready_Sig,
 Column_Addr_Sig,
 Row_Addr_Sig,
 Red_Sig,
 Green_Sig,
 Blue_Sig
);
 input CLK;
 input RSTn;
 input Ready_Sig;
 input [10:0] Column_Addr_Sig;
 input [10:0] Row_Addr_Sig;
 output Red_Sig;
 output Green_Sig;
 output Blue_Sig;
 
 reg isRectangl;
 always @ ( posedge CLK or negedge RSTn ) 
 begin
  if(!RSTn)
   isRectangl <= 1'b0;
  else if( Column_Addr_Sig > 11'd0 && Row_Addr_Sig < 11'd100)
   isRectangl <= 1'b1;
  else
   isRectangl <= 1'b0;
 end
 
 //所有颜色信号被赋予同样的表达式,这表示了在 799 x 100 
 //(显示部分的像素是800 x 600,然后isRectangl是在Row_Addr_Sig < 11'd100时置一)
 //的区域内显示白色的矩形。
 assign Red_Sig = (Ready_Sig && isRectangl) ? 1'b1 : 1'b0;
 assign Green_Sig = (Ready_Sig && isRectangl) ? 1'b1 : 1'b0;
 assign Blue_Sig = (Ready_Sig && isRectangl) ? 1'b1 : 1'b0;
 
endmodule

然后是PLL模块,我直接复制的例程暂时还不懂原理:
这编辑器我是服了,粘贴太多直接卡死,保存都不行,我贴个链接想用的去下载吧,也可以去网上找找fpga的pll模块的使用方法。
pll_module的代码
最后是顶层模块,把这些功能都连接起来:

module vga
(
 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_module U1
 (
  .inclk0(CLK),
  .c0(CLK_40MHZ)
 );
 
 wire [10:0]Column_Addr_Sig;
 wire [10:0]Row_Addr_Sig;
 wire Ready_Sig;
 sync U2 
 (
  .CLK(CLK_40MHZ),
  .RSTn(RSTn),
  .VSYNC_Sig(VSYNC_Sig),
  .HSYNC_Sig(HSYNC_Sig),
  .Column_Addr_Sig(Column_Addr_Sig),
  .Row_Addr_Sig(Row_Addr_Sig),
  .Ready_Sig(Ready_Sig)
 );
 vga_control U3
 (
  .CLK(CLK_40MHZ),
  .RSTn(RSTn),
  .Ready_Sig(Ready_Sig),
  .Column_Addr_Sig(Column_Addr_Sig),
  .Row_Addr_Sig(Row_Addr_Sig),
  .Red_Sig(Red_Sig),
  .Green_Sig(Green_Sig),
  .Blue_Sig(Blue_Sig)
 );
 
endmodule

代码就这些,他的RTL图如下图:
FPGA学习笔记——VGA_第6张图片

然后配置引脚并下载到开发板中(不知道怎么配置和下载的可以去看看我的quartus2的使用教程),效果如图:
FPGA学习笔记——VGA_第7张图片
还是挺有成就感的,接下来让屏幕显示图像。

你可能感兴趣的:(fpga)