今天给大侠带来基于FPGA的VGA/LCD显示控制器设计,由于篇幅较长,分三篇。今天带来第三篇,下篇,程序的仿真与测试以及总结,话不多说,上货。
前两篇和之前推送过关于VGA显示相关的文章,这里给个超链接,给各位大侠作个参考。
基于FPGA的VGA/LCD显示控制器设计(上)
基于FPGA的VGA/LCD显示控制器设计(中)
源码系列:基于FPGA的VGA驱动设计(附源工程)
导读
VGA (Video Graphics Array) 即视频图形阵列,是IBM于1987年随PS/2机(PersonalSystem 2)一起推出的使用模拟信号的一种视频传输标准。这个标准对于现今的个人电脑市场已经十分过时。但在当时具有分辨率高、显示速率快、颜色丰富等优点,在彩色显示器领域取得了广泛的应用,是众多制造商所共同支持的一个低标准。
LCD ( Liquid Crystal Display 的简称)液晶显示器。LCD 的构造是在两片平行的玻璃基板当中放置液晶盒,下基板玻璃上设置TFT(薄膜晶体管),上基板玻璃上设置彩色滤光片,通过TFT上的信号与电压改变来控制液晶分子的转动方向,从而达到控制每个像素点偏振光出射与否而达到显示目的。按照背光源的不同,LCD可以分为CCFL显示器和LED显示器两种。LCD已经替代CRT成为主流,价格也已经下降了很多,并已充分普及。
在之前的文章中介绍了如何获取、处理摄像头提供的视频信号,在实际应用中还需要将经过处理的信号显示在显示器上。这个过程与信号处理中的过程上是相反的,将数字信号按照电视信号的制式组成合乎时序、格式要求的信号,并加入用于控制的各种同步信号。本篇将通过 FPGA实现一个 VGA/LCD 显示控制器的实例,并详细介绍实现过程。
第三篇内容摘要:本篇会介绍程序的仿真与测试以及总结等相关内容。
四、程序的仿真与测试
为了检验程序是否实现预先设定的功能,需要编写仿真程序。仿真程序的主要代码如下:
module test;
//寄存器
reg clk;
reg rst;
//参数
parameter LINE_FIFO_AWIDTH = 7;
申明
wire int;
wire [31:0] wb_addr_o;
wire [31:0] wb_data_i;
wire [31:0] wb_data_o;
wire [3:0] wb_sel_o;
wire wb_we_o;
wire wb_stb_o;
wire wb_cyc_o;
wire [2:0] wb_cti_o;
wire [1:0] wb_bte_o;
wire wb_ack_i;
wire wb_err_i;
wire [31:0] wb_addr_i;
wire [31:0] wbm_data_i;
wire [3:0] wb_sel_i;
wire wb_we_i;
wire wb_stb_i;
wire wb_cyc_i;
wire wb_ack_o;
wire wb_rty_o;
wire wb_err_o;
reg pclk_i;
wire pclk;
wire hsync;
wire vsync;
wire csync;
wire blanc;
wire [7:0] red;
wire [7:0] green;
wire [7:0] blue;
wire dvi_pclk_p_o;
wire dvi_pclk_m_o;
wire dvi_hsync_o;
wire dvi_vsync_o;
wire dvi_de_o;
wire [11:0] dvi_d_o;
wire vga_stb_i;
wire clut_stb_i;
reg scen;
测试程序变量
integer wd_cnt;
integer error_cnt;
reg [31:0] data;
reg [31:0] pattern;
reg int_warn;
integer n;
integer mode;
reg [7:0] thsync, thgdel;
reg [15:0] thgate, thlen;
reg [7:0] tvsync, tvgdel;
reg [15:0] tvgate, tvlen;
reg hpol;
reg vpol;
reg cpol;
reg bpol;
integer p, l;
reg [31:0] pn;
reg [31:0] pra, paa, tmp;
reg [23:0] pd;
reg [1:0] cd;
reg pc;
reg [31:0] vbase;
reg [31:0] cbase;
reg [31:0] vbara;
reg [31:0] vbarb;
reg [7:0] bank;
常量定义
CTRL 32'h0000_0000
STAT 32'h0000_0004
HTIM 32'h0000_0008
VTIM 32'h0000_000c
HVLEN 32'h0000_0010
VBARA 32'h0000_0014
VBARB 32'h0000_0018
USE_VC 1
parameter PCLK_C = 20;
//测试内容
initial
begin
(-9, 1, " ns", 12);
$display("\n\n");
$display("******************************************************");
Controller Simulation started ... *");
$display("******************************************************");
$display("\n");
WAVES
$shm_open("waves");
$shm_probe("AS",test,"AS");
Signal dump enabled ...\n\n"); :
`endif
scen = 0;
error_cnt = 0;
clk = 0;
pclk_i = 0;
rst = 0;
int_warn=1;
@(posedge clk);
rst = 1;
@(posedge clk);
if(0)
begin
end
else
if(1)
begin
VGA_12BIT_DVI
dvi_pd_test;
`endif
end
else
begin
测试区域
$display("\n\n");
$display("*****************************************************");
XXX Test ***");
$display("*****************************************************\n");
s0.fill_mem(1);
@(posedge clk);
//参数设置
vbara = 32'h0000_0000;
vbarb = 32'h0001_0000;
`VBARA, 4'hf, vbara );
`VBARB, 4'hf, vbarb );
thsync = 0;
thgdel = 0;
thgate = 340;
thlen = 345;
tvsync = 0;
tvgdel = 0;
tvgate = 240;
tvlen = 245;
/*
thsync = 0;
thgdel = 0;
thgate = 63;
thlen = 70;
tvsync = 0;
tvgdel = 0;
tvgate = 32;
tvlen = 36;
*/
hpol = 0;
vpol = 0;
cpol = 0;
bpol = 0;
`HTIM, 4'hf, {thsync, thgdel, thgate} );
`VTIM, 4'hf, {tvsync, tvgdel, tvgate} );
`HVLEN, 4'hf, {thlen, tvlen} );
mode = 2;
0;bank<3;bank=bank + 1) =
begin
case(mode)
0:
begin
cd = 2'h2;
pc = 1'b0;
end
1:
begin
cd = 2'h0;
pc = 1'b0;
end
2:
begin
cd = 2'h0;
pc = 1'b1;
end
3:
begin
cd = 2'h1;
pc = 1'b0;
end
endcase
`CTRL, 4'hf, {
// Reserved
cpol,
hpol,
// 1'b0, // PC
// 2'h2, // CD
// VBL
// Reserved
// CBSWE
// VBSWE
// BSIE
// HIE
// VIE
// Video Enable
});
%0d Screen: %0d", mode, bank); :
@(posedge vsync);
vsync);
每一行数据
0;l
= For each Pixel
0;p
= begin
@(posedge pclk);
vbase = vbarb[31:2];
else vbase = vbara[31:2];
cbase = 32'h0000_0c00;
else cbase = 32'h0000_0800;
各种显示模式
行数* (thgate + 1) + p =
pn = l * (thgate + 1) + p;
case(mode)
0: // 24 位模式
begin
pra = pn[31:2] * 3;
paa = pra + vbase; // 像素决定地址
像素数据
0]) :
0:
begin
tmp = s0.mem[paa];
pd = tmp[31:8];
end
1:
begin
tmp = s0.mem[paa];
16] = tmp[7:0]; :
tmp = s0.mem[paa+1];
0] = tmp[31:16]; :
end
2:
begin
tmp = s0.mem[paa+1];
8] = tmp[15:0]; :
tmp = s0.mem[paa+2];
0] = tmp[31:24]; :
end
3:
begin
tmp = s0.mem[paa+2];
pd = tmp[23:0];
end
endcase
end
1: // 8 位灰度模式
begin
pra = pn[31:2]; // 像素相对地址
paa = pra + vbase; // 像素绝对地址
0]) :
0:
begin
tmp = s0.mem[paa];
pd = { tmp[31:24], tmp[31:24], tmp[31:24] };
end
1:
begin
tmp = s0.mem[paa];
pd = { tmp[23:16], tmp[23:16], tmp[23:16] };
end
2:
begin
tmp = s0.mem[paa];
pd = { tmp[15:8], tmp[15:8], tmp[15:8] };
end
3:
begin
tmp = s0.mem[paa];
pd = { tmp[7:0], tmp[7:0], tmp[7:0] };
end
endcase
end
2: // 8 位伪彩色模式
begin
pra = pn[31:2]; //像素相对地址
paa = pra + vbase; //像素绝对地址
0]) :
0:
begin
tmp = s0.mem[paa];
tmp = s0.mem[cbase[31:2] + tmp[31:24]];
pd = tmp[23:0];
end
1:
begin
tmp = s0.mem[paa];
tmp = s0.mem[cbase[31:2] + tmp[23:16]];
pd = tmp[23:0];
end
2:
begin
tmp = s0.mem[paa];
tmp = s0.mem[cbase[31:2] + tmp[15:8]];
pd = tmp[23:0];
end
3:
begin
tmp = s0.mem[paa];
tmp = s0.mem[cbase[31:2] + tmp[7:0]];
pd = tmp[23:0];
end
endcase
end
3: // 16 位模式
begin
pra = pn[31:1]; //像素相对地址
paa = pra + vbase; //像素绝对地址
case(pn[0])
0:
begin
tmp = s0.mem[paa];
0] = tmp[31:16]; :
pd = {tmp[15:11], 3'h0, tmp[10:5], 2'h0, tmp[4:0], 3'h0};
end
1:
begin
tmp = s0.mem[paa];
pd = {tmp[15:11], 3'h0, tmp[10:5], 2'h0, tmp[4:0], 3'h0};
end
endcase
end
endcase
!== {red, green, blue} )
begin
Pixel Data Mismatch: Expected: %h, Got: %h %h %h", :
red, green, blue);
pixel=%0d, line=%0d, (%0t)",p,l,$time);
error_cnt = error_cnt + 1;
end
pclk);
end
end
show_errors;
$display("*****************************************************");
Test DONE ... ***");
end
@(posedge clk);
$finish;
end
//同步监视
VGA_12BIT_DVI
sync_check #(PCLK_C*2) ucheck(
`else
sync_check #(PCLK_C) ucheck(
`endif
pclk ),
rst ),
scen ),
hsync ),
vsync ),
csync ),
blanc ),
hpol ),
vpol ),
cpol ),
bpol ),
thsync ),
thgdel ),
thgate ),
thlen ),
tvsync ),
tvgdel ),
tvgate ),
tvlen ) );
视频数据监视
wb_b3_check u_wb_check (
( clk ),
( wb_cyc_o ),
( wb_stb_o ),
( wb_cti_o ),
( wb_bte_o ),
( wb_we_o ),
( wb_ack_i ),
( wb_err_i ),
( 1'b0 ) );
//看门狗计数器
always @(posedge clk)
| wb_cyc_o | wb_ack_i | wb_ack_o | hsync)
wd_cnt <= #1 0;
else
wd_cnt <= #1 wd_cnt + 1;
always @(wd_cnt)
if(wd_cnt>9000)
begin
$display("\n\n*************************************\n");
Watch Dog Counter Expired\n"); :
$display("*************************************\n\n\n");
$finish;
end
always @(posedge int)
if(int_warn)
begin
$display("\n\n*************************************\n");
Recieved Interrupt (%0t)", $time); :
$display("*************************************\n\n\n");
end
always #2.5 clk = ~clk;
always #(PCLK_C/2) pclk_i = ~pclk_i;
//模块原型
vga_enh_top #(1'b0, LINE_FIFO_AWIDTH) u0 (
( clk ),
( 1'b0 ),
( rst ),
( int ),
//从信号
( wb_addr_i[11:0] ),
( wb_data_i ),
( wb_data_o ),
( wb_sel_i ),
( wb_we_i ),
( wb_stb_i ),
( wb_cyc_i ),
( wb_ack_o ),
( wb_rty_o ),
( wb_err_o ),
//主信号
( wb_addr_o[31:0] ),
( wbm_data_i ),
( wb_sel_o ),
( wb_we_o ),
( wb_stb_o ),
( wb_cyc_o ),
( wb_cti_o ),
( wb_bte_o ),
( wb_ack_i ),
( wb_err_i ),
信号
( pclk_i ),
VGA_12BIT_DVI
( dvi_pclk_p_o ),
( dvi_pclk_m_o ),
( dvi_hsync_o ),
( dvi_vsync_o ),
( dvi_de_o ),
( dvi_d_o ),
`endif
( pclk ),
( hsync ),
( vsync ),
( csync ),
( blanc ),
( red ),
( green ),
( blue )
);
wb_mast m0( .clk( clk ),
rst ),
wb_addr_i ),
wb_data_o ),
wb_data_i ),
wb_cyc_i ),
wb_stb_i ),
wb_sel_i ),
wb_we_i ),
wb_ack_o ),
wb_err_o ),
1'b0 )
);
wb_slv #(24) s0(.clk( clk ),
rst ),
{1'b0, wb_addr_o[30:0]} ),
32'h0 ),
wbm_data_i ),
wb_cyc_o ),
wb_stb_o ),
wb_sel_o ),
wb_we_o ),
wb_ack_i ),
wb_err_i ),
)
);
"tests.v"
endmodule
五、总结
本篇介绍了一个 VGA/LCD 显示控制器的实例。首先介绍了 VGA/LCD 显示的相关知识,然后介绍了程序的主要结构和主要功能模块的实现过程。最后用一个测试程序验证程序的功能是否满足要求。本章为各位大侠设计自己的 VGA/LCD 显示控制器提供了一个可以使用的方案。
本篇到此结束,各位大侠,有缘再见!
- -THE END- -
往期精选
FPGA技术江湖广发江湖帖
无广告纯净模式,给技术交流一片净土,从初学小白到行业精英业界大佬等,从军工领域到民用企业等,从通信、图像处理到人工智能等各个方向应有尽有,QQ微信双选,FPGA技术江湖打造最纯净最专业的技术交流学习平台。
FPGA技术江湖微信交流群
加群主微信,备注职业+方向+名字进群
FPGA技术江湖QQ交流群
备注地区+职业+方向+名字进群