VGA、RGBTFT、HDMI 显示,VGA是最基础的
在基于FPGA的数字系统设计中,VGA显示实验是一个大家绕不开的话题,毕竟使用FPGA就能驱动平常只有电脑才能驱动的大型显示器,相较于普通的MCU一般只能点亮普通的低分辨率小尺寸LCD液晶屏,驱动VGA显示器显得更加令人感兴趣。
然而事实上,使用FPGA驱动VGA显示器在指定位置显示某一指定颜色并不难,一个具有基本的FPGA逻辑设计能力的人在了解了VGA驱动时序后,大概半个小时就能写出一个易用的VGA控制器,相较于使用小尺寸MCU接口的LCD液晶屏需要查阅一大堆的寄存器来配置其工作模式。显然VGA控制器的开发难度要小的很多。
其次从功能应用来说,使用FPGA驱动VGA显示器显示复杂多变的图案就显得异常的麻烦了。不仅开发周期长,而且灵活性很低。不适合用来显示人机交互类的图案(所谓人机交互类图案就是指各种多变的文字信息,以及按钮信息等)。
使用FPGA驱动VGA虽然不适合用来显示复杂多变的图像内容,但是却常用于显示实时图像内容,例如显示图像传感器实时采集到的图像。此种方式数据流由图像传感器实时提供,FPGA只需要控制好图像数据流的存储和传输即可,无需主动生成需要绘制的图像数据,所以实现相对简单。
鉴于当前基于FPGA的图像处理是一个非常热门的应用方向,而在图像处理中,通过显示器实时查看显示内容属于必备功能,因此本节将详细介绍VGA时序及在FPGA中实现VGA控制器的方法。
行消隐
行同步
场同步
场消隐


了解了整个VGA扫描时序的详细参数之后
只需要输出 HS、VS、BLK、DATA (RGB三种颜色的分量)。
需要找到各个信号或者数据的时间节点。
行同步脉冲开始位置,HS_Begin = 0 (640*480 分辨率)
行同步脉冲的结束位置 HS_End = 96 (pclk)
行数据开始输出的位置 Hdata_begin = 96 + 40 + 8
行数据停止输出的位置 Hdata_end = 96 + 40 + 8 + 640
行同步信号的结束位置 Hsyns_End = 96 + 40 + 8 + 640 + 8 + 8
场同步脉冲的开始位置 VS_Begin = 0
场同步脉冲的结束位置 VS_End = 2(line )
场数据开始输出的位置 Vdata_begin = 2 + 25 + 8
场数据停止输出的位置 Vdata_end = 2 + 25 + 8 + 480
场同步信号的结束位置 Vsyns_End = 2 + 25 + 8 + 480 + 2 + 8
VGA_CTRL.v
module VGA_CTRL(
Clk,
Reset,
VGA_HS,
VGA_VS,
VGA_BLK,
VGA_RGB,
Data
);
input Clk;
input Reset;
output reg VGA_HS;
output reg VGA_VS;
output VGA_BLK;
output [23:0]VGA_RGB; // R[7:0] G[7:0] B[7:0]
localparam Hsync_End = 800;
localparam HS_End = 96;
localparam Vsync_End = 525;
localparam VS_End = 2;
localparam Hdat_Begin = 144;
localparam Hdat_End = 784;
localparam Vdat_Begin = 35;
localparam Vdat_End = 515;
reg [9:0]hcnt;
always@(posedge Clk or negedge Reset)
if(!Reset)
hcnt <= 0;
else if(hcnt == Hsync_End - 1)
hcnt <= 0;
else
hcnt <= hcnt + 1'b1;
// 对于我们的HS信号
// assign VGA_HS = (hcnt < HS_End - 1'd1)?0:1;
// 可以改成时序逻辑,更合理 VGA_VS 同理
always@(posedge Clk)
VGA_HS <= (hcnt < ES_End - 1'd1)?0:1;
reg [9:0]vcnt;
always@(posedge Clk or negedge Reset)
if(!Reset)
vcnt <= 0;
else if(hcnt == Hsync_End - 1) begin
if(vcnt >= Vsyns_End - 1)
vcnt <= 0;
else
vcnt <= vcnt + 1'd1;
end
assign VGA_VS = (vcnt < VS_End - 1'd1) ?0:1;
// Blk 信号,表示数据输出的时间段,也可以改成时序逻辑
assign VGA_BLK = (hcnt >= Hdat_begin - 1) && (hcnt < Hdat_End - 1) && (vcnt >= Vdat_Begin - 1 && (vcnt < Vdat_End))?1:0;
// 可以
assign VGA_RGB = VGA_BLK?Data:0;
endmodule
测试 testbench
module VGA_CRTL_tb;
reg Clk;
reg Reset;
reg [23:0] Data;
wire VGA_HS;
wire_VGA_VS;
wire [23:0]VGA_RGB;
VGA_CTRL VGA_CTRL(
Clk,
Reset,
Data,
VGA_HS,
VGA_VS,
VGA_BLK,
VGA_RGB
);
initial Clk = 1;
always #20 Clk = ~Clk;
// HS 的变化位置
// VS 的变化位置
// 待显示数据和HS、VS的位置关系
initial begin
Reset = 0;
// Data = 0;
#201;
Reset = 1;
#20000000;
$stop
end
always@(posedge Clk or negedge Reset)
if(!Reset)
Data <= 0;
else if(!VGA_BLK)
Data <= Data;
else
Data <= Data + 1'd1;
endmodule
然后调试:需要不断的找bug,修改源代码

改一下得到一个新的文件,学习条件语法来进行编译
条件编译和条件控制语句有本质的区别,条件编译:根据不同的条件来选择对应的HDL文件进行编译以得到对应的逻辑电路。
if a
…
cntmax = 256
elseif b
…
cntmax = 618
else
…
endif
修改如下:
决定分辨率的是这几个参数:

vga_parameter.v


然后控制器的内容要进行改写:
·include “vga_parameter.v”

位宽最好都设计成12位的
通过条件编译来适配,多分辨率编译器