FPGA快速入门-VGA显示
设计背景:
VGA (Video Graphics Array) 即视频图形阵列,是IBM于1987年随PS/2机(PersonalSystem 2)一起推出的使用模拟信号的一种视频传输标准。这个标准对于现今的个人电脑市场已经十分过时。但在当时具有分辨率高、显示速率快、颜色丰富等优点,在彩色显示器领域取得了广泛的应用,是众多制造商所共同支持的一个低标准。
设计原理:
VGA的实体图与接口示意图,如下图所示,它有15个针孔:
在开发板(ZX_1)中,VGA的电路原理图如下图所示:
通过原理图,我们不难发现,VGA需要我们控制的接口只有5个:
信号定义 信号描述
Hsyns(行同步) 扫描一行
Vsync(场同步) 扫描一场
Red 红色
Green 绿色
Blue 蓝色
显示器的扫描规律是什么?本设计采用逐行扫描,逐行扫描是扫描从屏幕左上角一点开始,从左向右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,CRT对电子束进行消隐,每行结束时,用行同步信号进行行同步;当扫描完所有的行,形成一帧,用场同步信号进行场同步,并使扫描回到屏幕左上方,同时进行场消隐,开始下一帧。通过这种扫描规律,很容易看出,在设计两个有效范围计数器时,场同步信号计数器是以行同步信号计数器为周期的。
VGA的显示标准如下表所示:
对于普通的VGA显示器都要严格遵循“VGA工业标准”,否则可能会损害VGA显示器,因此我们在设计时VGA控制器时,都需要参考显示器的显示标准,下面是VGA的行扫描时序与场扫描时序:
行扫描时序:
场扫描时序:
根据上述显示器的扫描参数以及扫描时序,例如800*600@60的显示模式,60指得是显示器图像的刷新频率,时钟40MHz指得是一个像素输出的频率。800*600为VGA的分辨率,指有效显示区域为时序中的c段只有800*600,也就是行计数在[216,1016],列计数在[27,627],在这个范围内,给RGB色值才会有效。
在VGA 工业标准显示模式要求:行同步、场同步都为负极性,即同步脉冲要求是负脉冲。行同步信号上电拉高,在行同步计数为0时拉低a个时钟周期,即128,之后拉高,在行同步计数到1055时,行同步计数器清零,场同步计数器加1。在行扫描时序中,扫描计数时,周期就是一个像素点的时间。
场同步信号上电拉高,在场同步计数为0时拉低场同步a个时钟周期,即4,之后拉高,在场同步计数到627时,场同步计数器清零。
在VGA控制器中,还需要控制三个接口,即三种基色(R、G、B),它们共专用8位,分别是Red为3位,Green为3位,Blue为3位,所以可以显示256种颜色,RGB数据的格式如下表所示:
设计架构图:
本设计选择的VGA显示标准为800*600@60,实现点亮整个屏幕,并显示为全红。通过分析设计的功能,可以得到如下的顶层架构:
vga_pll模块是为了满足分辨率800*600@60的时钟为40MHz,而ZX_1开发板的系统时钟为50MHz,通过锁相环,将50MHz转化为40MHz。vga_control模块是为了设定行场同步信号,并标定出有效显示区域,并输出控制颜色的po_rgb信号。为了便于移植,根据800*600@60分辨率下的参数,对其进行参数化定义。
设计代码:
VGA控制器代码:
0 0 module vga_control (pi_clk, pi_rst_n, po_hs, po_vs, po_rgb);
1
2 input pi_clk, pi_rst_n; //系统时钟复位
3 output reg po_vs; //VGA场同步信号
4 output reg po_hs; //VGA行同步信号
5 output [7:0] po_rgb; //VGA场红绿蓝三基色
6
7 //----------------VGA时序-----------------------------------
8 // 显示模式 时钟
9 // 800*600@60 40MHz
10 //行/场 同步(a) 消隐后沿(b) 有效显示(c) 消隐前沿(d) 扫描时间(e)
11 //hs 128 88 800 40 1056
12 //vs 4 23 600 1 628
13
14 // 行(Horizontal)扫描 Parameter (像素)
15 parameter H_A = 128;
16 parameter H_B = 80;
17 parameter H_C = 800;
18 parameter H_D = 40;
19 parameter H_E = 1056;
20
21
22 // 场(Vertical)扫描 Parameter (行数)
23 parameter V_A = 4;
24 parameter V_B = 23;
25 parameter V_C = 600;
26 parameter V_D = 1;
27 parameter V_E = 628;
28
29 //行扫描计数器,
30 reg [10:0] hcnt;
31
32 always @ (posedge pi_clk or negedge pi_rst_n)
33 begin
34 if (!pi_rst_n)
35 hcnt <= 11'd0;
36 else
37 begin
38 if (hcnt == (H_E - 1'b1)) //扫描完一行像素
39 hcnt <= 11'd0;
40 else
41 hcnt <= hcnt + 1'b1;
42 end
43 end
44
45 //场扫描计数器
46 reg [10:0] vcnt;
47
48 always @ (posedge pi_clk or negedge pi_rst_n)
49 begin
50 if (!pi_rst_n)
51 vcnt <= 11'd0;
52 else if (vcnt == (V_E - 1'b1))
53 vcnt <= 11'd0;
54 else if (hcnt == (H_E - 1'b1))
55 vcnt <= vcnt + 1;
56 end
57
58 //行同步输出
59 always @ (posedge pi_clk or negedge pi_rst_n)
60 begin
61 if (!pi_rst_n)
62 po_hs <= 1'b1;
63 else if (hcnt < H_A)
64 po_hs <= 1'b0;
65 else
66 po_hs <= 1'b1;
67 end
68
69 //assign po_hs = (hcnt <= H_A - 1'b1) ? 1'b0 : 1'b1;
70
71 //场同步输出
72 always @ (posedge pi_clk or negedge pi_rst_n)
73 begin
74 if (!pi_rst_n)
75 po_vs <= 1'b1;
76 else if (vcnt < V_A)
77 po_vs <= 1'b0;
78 else
79 po_vs <= 1'b1;
80 end
81
82 //assign po_vs = (vcnt <= V_A - 1'b1) ? 1'b0 : 1'b1;
83
84 wire rgb_en;
85
86 assign rgb_en = (hcnt >= H_A + H_B && hcnt < H_A + H_B + H_C) &&
87 (vcnt >= V_A + V_B && vcnt < V_A + V_B+ V_C) ? 1'b1 : 1'b0;
88
89 assign po_rgb = rgb_en ? 8'b111_000_00 : 8'b0000_0000;
90
91 endmodule
顶层文件如下所示:
0 module vga_display_pure (pi_clk, pi_rst_n, po_hs, po_vs, po_rgb);
1
2 input pi_clk, pi_rst_n; //系统时钟复位
3 output po_vs; //VGA场同步信号
4 output po_hs; //VGA行同步信号
5 output [7:0] po_rgb; //VGA场红绿蓝三基色
6
7 //----------------VGA时序-----------------------------------
8 // 显示模式 时钟
9 // 800*600@60 40MHz
10 //行/场 同步(a) 消隐后沿(b) 有效显示(c) 消隐前沿(d) 扫描时间(e)
11 //hs 128 88 800 40 1056
12 //vs 4 23 600 1 628
13
14 wire vga_clk;
15
16 vga_pll vga_pll_dut(
17 .areset(~pi_rst_n),
18 .inclk0(pi_clk),
19 .c0(vga_clk)
20 );
21
22 vga_control vga_control_dut(
23 .pi_clk(vga_clk),
24 .pi_rst_n(pi_rst_n),
25 .po_hs(po_hs),
26 .po_vs(po_vs),
27 .po_rgb(po_rgb)
28 );
29
30 endmodule
通过编译后生成的RTL视图如下:
为了验证本设计的逻辑正确性,我们先对其进行了仿真,在仿真时,为了减少仿真的时间,先将行、场扫描的对应参数,进行了缩放,这样不仅节约了仿真时间,同时由于扫描数据量变少,更加便于分析观察。其仿真代码所示;
0 `timescale 1ns/1ps //仿真时间精度时间单位
1
2 module vga_display_pure_tb;
3
4 reg pi_clk, pi_rst_n; //系统时钟复位
5 wire po_vs; //VGA场同步信号
6 wire po_hs; //VGA行同步信号
7 wire [7:0] po_rgb; //VGA场红绿蓝三基色
8
9 //初始化数据,并附相应初值
10 initial begin
11 pi_clk = 0;
12 pi_rst_n = 0;
13 #200.1 pi_rst_n = 1;
14
15 end
16
17 vga_display_pure vga_display_pure_inst (
18 .pi_clk(pi_clk),
19 .pi_rst_n(pi_rst_n),
20 .po_hs(po_hs),
21 .po_vs(po_vs),
22 .po_rgb(po_rgb)
23 );
24
25 always #10 pi_clk = ~pi_clk; //50MHz时钟描述
26
27 endmodule
|