基于FPGA的VGA时序分析

前言

FPGA主要运用于芯片验证、通信、图像处理。显示VGA接口的显示器是最基本的要求了。

原理

  首先需要了解 :

(1)VGA接口协议VGA端子_维基百科 、VGA视频传输标准_百度

引脚1 RED 红色视频
引脚2 GREEN 绿色视频
引脚3 BLUE 蓝色视频
引脚4 ID2/RES 过去为屏幕ID比特2;自DDC2起保留
引脚5 GND 接地(水平同步)
引脚6 RED_RTN 红色接回
引脚7 GREEN_RTN 绿色接回
引脚8 BLUE_RTN 蓝色接回
引脚9 KEY/PWR 过去为key;现为 +5V DC
引脚10 GND 接地(垂直同步,DDC)
引脚11 ID0/RES 过去为屏幕ID比特0;自E-DDC起保留
引脚12 ID1/SDA 过去为屏幕ID比特1;自DDC2起为I²C数据
引脚13 HSync 水平同步
引脚14 VSync 垂直同步
引脚15 ID3/SCL 过去为屏幕ID比特3;自DDC2起为I²C时钟
图表中详叙了较新的15针VESA DDC2连接头。图标中的针脚编号是显卡常见的母接头;在公接头上的针脚相当于图例的左右镜像

 最主要的几根线:

(2)VGA时序

各种分辨率的显示器时序参数:http://tinyvga.com/vga-timing

a、扫描轨迹

 

扫描从屏幕左上方开始,从左至右,从上到下,进行扫描。

每扫描完一行,电子束(CRT)回到屏幕的左边下一行起始位置,在此期间对电子束进行消隐,每行结束时,用行同步信号进行同步。

扫描完所有行,用场同步信号进行场同步,并使扫描回到屏幕的左上方,同时进行消隐,预备下一行的扫描。

b、行场扫描(工业标准为负,大部分显示器HS、VS可正可负,显示器自动调整,此处为正极性)

c、HS时序深入分析

可见时序的循环,可被划分为a,b,c,d4个时期。这四个时期定义如下:

  A~B:行消隐期 即同步,相当于还原扫描坐标吧

  B~C:行消隐后肩 相当于准备开始扫描吧

  C~D:行显示期 扫描中,数据有效区域

  D~E:行消隐前肩 完成扫描,相当于准备同步

d、VS时序深入分析

可见时序的循环,可被划分为a,b,c,d4个时期。这四个时期定义如下:

  A~B:场消隐期 即同步,相当于还原扫描坐标吧

  B~C:场消隐后肩 相当于准备开始扫描吧

  C~D:场显示期 扫描中,数据有效区域

  D~E:场消隐前肩 完成扫描,相当于准备同步

综上描述,我们只要知道每个时期的时间,便可以表示出VGA的时序。而FPGA的工作是由固定频率的时钟触发的,因此某固定时间可以用n次触发来表示。因此我们很容易就想到了FPGA常用的计数方法:比如说行扫描,我们计数0~H_total-1。用另一个进程将其划分为4个时期,按标注分配。其实这相当于状态机。

 以下是固定分辨率1280*1024 60fps下HS,VS的标准:

有图可得:

各个时期像素数:

// Horizontal Parameter( Pixel )
H_DISP     = 11'd1280,
H_FRONT = 11'd48,
H_SYNC    = 11'd112,
H_BACK    = 11'd248,
H_TOTAL  = 11'd1688,
// Virtical Parameter( Line )
V_DISP     = 11'd1024,
V_FRONT = 11'd1,
V_SYNC    = 11'd3,
V_BACK    = 11'd38,
V_TOTAL  = 11'd1066;

像素时钟频率(即所需的VGA时钟频率)

(H_DISP + H_BACK + H_SYNC + H_FRONT)×(V_DISP + V_BACK + V_SYNC + V_FRONT)× REFRESH_RATE

在此为(1280 + 248 + 112 + 48)×(1024 + 38 + 3 + 1)× 60 = 1688 × 1066 × 60 = 107.964480 MHz (工业标准为 108.000 MHz ,当然都是能正常显示的,推荐用工业标准。)

一帧图像的数据量(以RGB565 格式为例)

H_DISP × V_DISP × (5 + 6 + 5)bit = [H_DISP × V_DISP × (5 + 6 + 5)bit ] ÷ 8 B =  [H_DISP × V_DISP × (5 + 6 + 5)bit ] ÷ 8 ÷ 1024 K

在此为 1280 × 1024 × 16 bit = 20971520 ÷ 8 B = 2621440  ÷ 1024 K = 2560  ÷ 1024 M = 2.5 M

(3)三基色原理

常见的彩色显示器一般都是由CRT (阴极射线管) 构成,每一个像素的色彩由R ( 红, Red) 、G( 绿,Green) 、B(蓝,Blue) 三基色构成。显示时采用的是逐行扫描的方式。由VGA 显示模块产生的水平同步信号和垂直同步信号控制阴极射线管中的电子枪产生电子束,轰击涂有荧光粉的屏幕,产生RGB 三基色,于显示屏上合成一个彩色像素点。

像素是产生各种颜色的基本单元。根据物理学中的混色原理,三色发光的亮度比例适当,可呈现白色。适当的调整发光比例可以出现不同的颜色。

程序设计

 显示模块(顶层):

复制代码

  1 /************************************************************************
  2 *    Author         :    yllinux  博客:http://www.cnblogs.com/yllinux/
  3 *    Module Name    :    vga_color_line.v           
  4 *    Tool versions  :    Quartus II 12.1;Cyclone IV E(EP4CE6F17C8)
  5 *    Create Date    :    2017-7-21
  6 *    Revision       :    v1.0
  7 *    Description    :    使1280*1024@60HZ的VGA显示器显示竖彩条    
  8 ************************************************************************/
  9 
 10 module VGA_color_line (clk, rst_n, hs_vga, vs_vga, r_vga, g_vga, b_vga);
 11     
 12     input clk, rst_n;  //系统时钟和低电平复位
 13     output hs_vga, vs_vga;  //行同步hs_vga,场同步vs_vga
 14     output r_vga, g_vga , b_vga;  //像素三基色输出R、G、B
 15     reg [2:0] rgb_vga;  //相当于输入信号了
 16     wire clk_vga;  //像素时钟 1688 * 1066 * 60 MZ (工业标准 108 MHZ)
 17     
 18 // VGA_1280_1024_60fps_50MHz
 19 // Horizontal Parameter( Pixel )
 20     parameter 
 21         H_DISP  = 11'd1280,
 22         H_FRONT = 11'd48,
 23         H_SYNC  = 11'd112,
 24         H_BACK  = 11'd248,
 25         H_TOTAL = 11'd1688,
 26 // Virtical Parameter( Line )
 27         V_DISP  = 11'd1024,
 28         V_FRONT = 11'd1,
 29         V_SYNC  = 11'd3,
 30         V_BACK  = 11'd38,
 31         V_TOTAL = 11'd1066;
 32             
 33 //调用IP核ALTPLL,像素频率108 MHZ
 34     pll_pixel_clock    pll_pixel_clock_inst (
 35         .inclk0 ( clk ),
 36         .c0 ( clk_vga )   //例化端口必须是网线型
 37         );
 38     
 39 //行同步计数器(信号发生器)
 40     reg [10:0] hcnt;
 41     reg hs_vga;
 42     always @ (posedge clk_vga or negedge rst_n)
 43         begin
 44             if (!rst_n) 
 45                 hcnt <= 0;  //复位后计数清零
 46             else 
 47                 begin
 48                     if (hcnt < H_TOTAL - 1'b1)  //判断扫描完一行,像素区间为[0, H_TOTAL - 1'b1]
 49                         hcnt <= hcnt + 1'b1;
 50                     else
 51                         hcnt <= 0; 
 52                 end
 53         end 
 54 //行同步    
 55     always @ (posedge clk_vga or negedge rst_n)
 56         begin
 57             if (!rst_n)
 58             hs_vga <= 0; //因为复位后计数器置零,而0 ~ (H_DISP - 1)区间为显示区,此处必为 0  
 59             else
 60                 begin
 61                 //像素(H_DISP + H_FRONT - 1, H_DISP + H_FRONT + H_SYNC - 1]区间同步
 62                     if (hcnt >= (H_DISP + H_FRONT - 1'b1) && hcnt < (H_DISP + H_FRONT + H_SYNC - 1'b1))
 63                         hs_vga <= 1;  //在同步区置1,行同步
 64                     else
 65                         hs_vga <= 0;
 66                 end
 67         end 
 68     
 69 //场同步计数器(信号发生器)
 70     reg [10:0] vcnt;
 71     reg vs_vga;
 72     always @ (posedge clk_vga or negedge rst_n)  //异步复位
 73         begin
 74             if (!rst_n)
 75             vcnt <= 0;  //复位后计数清零
 76             else
 77                 begin
 78                     if (hcnt == H_DISP - 1'b1)  //判断显示区扫面完一行,此处也可判断整个一行扫描完
 79                         begin
 80                             if (vcnt < V_TOTAL - 1'b1)  //判断扫面完一场
 81                                 vcnt <= vcnt + 1'b1;
 82                             else
 83                                 vcnt <= 0;
 84                         end
 85                     else
 86                         vcnt <= vcnt;
 87                 end 
 88         end 
 89 //场同步
 90     always @ (posedge clk_vga or negedge rst_n)
 91         begin
 92             if (!rst_n)
 93                 vs_vga <= 0;
 94             else 
 95                 begin
 96                     if (vcnt >= (V_DISP + V_FRONT - 1'b1) && vcnt < (V_DISP + V_FRONT + V_SYNC - 1'b1))
 97                         vs_vga <= 1;
 98                     else
 99                         vs_vga <= 0;
100                 end 
101         end 
102 
103 //在显示期坐标根据显示的扫描而改变,在非显示期,坐标置零
104     wire [10:0] xpos_vga, ypos_vga;
105     assign xpos_vga = (hcnt < H_DISP) ? (hcnt + 1'b1) : 11'd0;  //在显示区横坐标 + 1(即1~H_DISP)
106     assign ypos_vga = (vcnt < V_DISP) ? (vcnt + 1'b1) : 11'd0;  //在显示区竖坐标 + 1(即1~V_DISP)
107     
108 //竖彩条显示
109     always @ (posedge clk_vga or negedge rst_n)    
110         if (!rst_n)
111             rgb_vga <= 3'b000;
112         else 
113             begin
114                 /*if (xpos_vga > 0 && xpos_vga < 80) rgb_vga <= 3'b010;
115                 else if (xpos_vga < 160) rgb_vga <= 3'b011;
116                 else if (xpos_vga < 240) rgb_vga <= 3'b100;
117                 else if (xpos_vga < 320) rgb_vga <= 3'b101;
118                 else if (xpos_vga < 400) rgb_vga <= 3'b110;
119                 else if (xpos_vga < 480) rgb_vga <= 3'b111;
120                 else if (xpos_vga < 560) rgb_vga <= 3'b001;
121                 else if (xpos_vga < 640) rgb_vga <= 3'b010;
122                 else if (xpos_vga < 720) rgb_vga <= 3'b011;
123                 else if (xpos_vga < 800) rgb_vga <= 3'b100;
124                 else if (xpos_vga < 880) rgb_vga <= 3'b101;
125                 else if (xpos_vga < 960) rgb_vga <= 3'b110;
126                 else if (xpos_vga < 1040) rgb_vga <= 3'b111;
127                 else if (xpos_vga < 1120) rgb_vga <= 3'b001;
128                 else if (xpos_vga < 1200) rgb_vga <= 3'b010;
129                 else if (xpos_vga <= 1280) rgb_vga <= 3'b011;
130                 else rgb_vga <= 3'b000;*/
131             
132             if (xpos_vga > 0 && xpos_vga <= 80) rgb_vga <= 3'b111;//white
133             else if (xpos_vga > 80 && xpos_vga <= 160) rgb_vga <= 3'b100;//red
134             else if (xpos_vga > 160 && xpos_vga <= 240) rgb_vga <= 3'b101;//mangenta
135             else if (xpos_vga > 240 && xpos_vga <= 320) rgb_vga <= 3'b110;//yellow
136             else if (xpos_vga > 320 && xpos_vga <= 400) rgb_vga <= 3'b010;//green
137             else if (xpos_vga > 400 && xpos_vga <= 480) rgb_vga <= 3'b001;//blue
138             else if (xpos_vga > 480 && xpos_vga <= 560) rgb_vga <= 3'b011;//cyan            
139             else if (xpos_vga > 560 && xpos_vga <= 640) rgb_vga <= 3'b000;
140             
141             else if (xpos_vga > 640 && xpos_vga <= 720) rgb_vga <= 3'b111;//white
142             else if (xpos_vga > 720 && xpos_vga <= 800) rgb_vga <= 3'b100;
143             else if (xpos_vga > 800 && xpos_vga <= 880) rgb_vga <= 3'b101;
144             else if (xpos_vga > 880 && xpos_vga <= 960) rgb_vga <= 3'b110;
145             else if (xpos_vga > 960 && xpos_vga <= 1040) rgb_vga <= 3'b010;
146             else if (xpos_vga > 1040 && xpos_vga <= 1120) rgb_vga <= 3'b001;
147             else if (xpos_vga > 1120 && xpos_vga <= 1200) rgb_vga <= 3'b011;
148             else if (xpos_vga > 1200 && xpos_vga <= 1280) rgb_vga <= 3'b111;
149             else rgb_vga <= 3'b000;//black,这个很重要,不然颜色不怎么正常
150             
151         end 
152         
153 //三基色分离(可省,直接用 rgb_vga)    
154     assign r_vga = rgb_vga[2];
155     assign g_vga = rgb_vga[1];
156     assign b_vga = rgb_vga[0];
157     
158 endmodule

复制代码

调用的锁相环 IP 核 ALTPLL:

 View Code

【注】:

对时序部分代码的编写不论从哪个时期作为判断依据都无所谓,这是一个周期性循环的。后面附另一种判断的写法,其实都一样。

尽管使用的是LCD而不是CRT,消隐前后期必须保留,若令 H_FRONT、H_BACK、V_FRONT、V_BACK 为零,显示器将不能显示,亲测。

尽量按照工业标准来写,这样兼容性更强。

RTL:

Technology Map:

Modelsim 仿真

写一个简单的 testbench:

复制代码

 1 `timescale 10 ns/ 1 ps
 2 module VGA_color_line_vlg_tst();
 3     reg clk;
 4     reg rst_n;
 5     // wires                                               
 6     wire b_vga;
 7     wire g_vga;
 8     wire hs_vga;
 9     wire r_vga;
10     wire vs_vga;
11 
12     VGA_color_line i1 (  
13         .b_vga(b_vga),
14         .clk(clk),
15         .g_vga(g_vga),
16         .hs_vga(hs_vga),
17         .r_vga(r_vga),
18         .rst_n(rst_n),
19         .vs_vga(vs_vga)
20     );
21     initial                                                
22         begin                                                  
23             clk = 0;
24         end                                                    
25     always                                                                
26         begin                                                  
27             #1 clk = ~clk;                                            
28         end   
29 endmodule

复制代码

仿真波形:

可以看出波形正常。

2017-07-22--------------------------------------------------------------------------------------------------------------↓↓↓

对于VGA时序里 hcnt、xpos_vga、hs_vga 等随时钟变化存疑,故手动仿真如下:

    我们假设 H_DISP = 4, H_BACK = 1, H_SYNC = 1, H_FRONT = 1, H_TOTAL = 7。

 由此用 quartus ii 9.0 自带仿真工具测试一下:

复制代码

 1 module VGA_color_line_test (clk, rst, xpos_vga, hcnt, hs_vga);
 2     input clk, rst;
 3     output hcnt, hs_vga;
 4     output [2:0] xpos_vga;
 5 
 6     reg [2:0] hcnt;
 7     reg hs_vga;
 8     wire [2:0] xpos_vga;
 9 
10     parameter 
11     H_DISP = 3'd4,
12     H_FRONT = 3'd1,
13     H_SYNC = 3'd1,
14     H_BACK = 3'd1,
15     H_TOTAL = 3'd7;
16 
17     always @(posedge clk or negedge rst) begin
18         if (!rst) 
19             hs_vga <= 0;
20         else 
21             if (hcnt >= H_DISP + H_FRONT - 1 && hcnt < H_DISP + H_FRONT + H_SYNC - 1) 
22                 hs_vga <= 1;
23             else 
24                 hs_vga <= 0;
25     end
26 
27     always @(posedge clk or negedge rst) begin
28         if (!rst) 
29             hcnt <= 0;
30         else 
31             if (hcnt < H_TOTAL - 1) 
32                 hcnt <= hcnt + 1;
33             else 
34                 hcnt <= 0;
35     end
36 
37     assign xpos_vga = (hcnt < H_DISP) ? hcnt : 0;
38 
39 endmodule

复制代码

    从仿真波形可以看出手动分析完全正确。则原代码中 105 和 106 行 assign语句中不应该加 1。

    (assign xpos_vga = (hcnt < H_DISP) ? hcnt : 11'd0; 即可,ypos_vga同理)

    由此能得出在非显示区坐标为 0,那 0 坐标点在非显示期会不会出现异常呢,我把非显示期改为 11'dz 也能正常显示,但是这样好吗??? (希望大神们给点提示^_^)

    改为11'dz 仿真波形如下:

2017-07-22--------------------------------------------------------------------------------------------------------------↑↑↑

硬件实测

硬件采用:Cyclone IV E(EP4CE6F17C8)

引脚分配:

实际效果:

 

 上面的是比较完善的,下面粘贴第一个版本代码,能显示,稍有瑕疵,程序中有些细节可能有问题,懒得改了,直接粘贴

顶层文件:

 View Code

pll :

 View Code

testbench:

 View Code

未完待续。。。

参考:

Crazy Bingo:http://www.cnblogs.com/crazybingo/archive/2011/02/24/1963652.html

《EDA技术实用教程--Verilog HDL版(第四版)》---- 潘松、黄继业、潘明 编著

声明:此篇文章是转载一位大佬的,为了方便自己学习,特意保留下来了 ,如有侵权还望谅解!                  

猜你喜欢

转载自blog.csdn.net/QQ_Peng123/article/details/85036807