以前自己调试过视频信号,无非就时钟加行场同步加数据线,如果视频信号出问题,第一看现象,第二测频率,反正出问题不是消隐信号出问题,就是时钟频率出问题。通过这种方式也调试成功过几个显示屏,然后就以为自己对视频信号的调试已经有丰富的经验了,直到这次需要自己用FPGA写一个VGA的驱动程序,才发现原来自己还是半桶水。

  这次谈一下将RGB888的视频信号转换为VGA信号时的调试心得,使用ADV7123将数字信号转换为VGA信号。

  首先谈一下VGA,在这个网站上可以能看到各种VGA信号的标准:http://tinyvga.com/vga-timing

  以1600*1200分辨率为例(这次我就是要产生一个这样的视频)

 

 

   假如1秒显示60场,一场是1200行,一行有1600个像素点,那么时钟频率就是60*1200*1600=115.2MHz,但是实际上不能这样满打满算。如果从现在的思维来考虑,因为行和场同步信号需要一定的建立时间,因此需要的时钟频率会更高一些,但是只要高一点点,给行场同步信号一定的建立时间就可以了,但是实际频率162MHz与计算得到的115.2MHz差别未免也太大了吧。因为这是一个历史问题,以前的显示器是用电子枪扫描的,电子枪扫完一行后需要回到行首,这段时间叫行消隐时间,电子枪扫完一场(到达屏幕的右下角)后需要回到左上角,这段时间叫场消隐时间。由于行消隐和场消隐的存在,以至于实际扫描一帧的时间要比算出来的满打满算的时间要多。

  消隐时间在以前是为了让电子枪归位而设定的,今天虽然行场同步的建立时间可以很短,但是毕竟是一代标准,不能轻易改变,因此消隐信号仍然保留了。

  在网上很多地方可以找到VGA的时序说明,我这边粗略说一下。

  一个行同步周期内包含4个段:行消隐前肩、行同步时间、行消隐后肩、行有效时间,如上图所示,1600*1200的标准中,行有效时间包含了1600个时钟,对应了1600个有效像素点,行消隐前肩包含64个时钟,行同步时间包含192个时钟,行消隐后肩包含304个时钟,所以一行加起来一共2160个时钟。

  同样的,一个场同步周期内包含4个段:场消隐前肩、场同步时间、场消隐后肩、场有效时间,如上图所示,1600*1200的标准中,场有效时间包含了1200个行周期,对应显示器能显示出来的1200行,场消隐前肩包含1个行周期,场同步时间包含3个行周期,场消隐后肩包含46个行周期,所以一场加起来一共有1250个行周期。

  接下来是重点了,行和场都包含有4个时间段,这4个时间段究竟是怎么排布的,网上我找到两种说法:

 

 

   我使用这两种时序产生VGA信号,然后我用ADV7604来采,结果显示老不正常,我一直以为是BLANK信号(ADV7123有个BLANK信号,其实就是消隐信号)的原因,改来改去总不对,但是它采标准的1600*1200的图像是正常的,因此我干脆直接自己采一下标准的1600*1200的VGA信号。

  我使用逻辑分析仪采行场同步和红色通道的(也可以是其他通道)信号,行场同步可以直接接逻辑分析仪,而彩色信号是一个模拟信号,最大电压是0.714V,逻辑分析仪采不到。当红色通道为0.714V时表示的是最红的那个颜色,因此我使用了一个放大器,将0.714V放大到5V,然后再接入逻辑分析仪。接着只要采一个纯红的视频就OK了,但问题是纯红的视频哪里来,我找到一个很妙的办法,我外接了一个HDMI转VGA的接头,这样相当于外扩了一个VGA的显示器,然后新建1个PPT,把背景改为纯红,然后PPT设定为演讲者模式(具体操作我忘了,我是早就设好的,这个网上一搜就能搜到),按win+R键将投影选择扩展,然后放映PPT。这个时候神奇的事情来了,第二显示器显示的是纯红的,没有半点杂质,而我的主显示器的可以将PPT最小化,这样就能在主显示器中运行逻辑分析仪软件了。

  运行之后,采到的波形如下图所示:

 

  注意到1个问题,场同步的同步时间段中包含有3个完整的行同步,同步时间段之前有1个没有数据的行同步,同步时间段之后包含46个没有数据的行同步,那这不就水落石出了吗,4个时间段的排序如下图,前肩后面是同步时间,接着是后肩,然后是有效数据,所以之前找到的那两种说法似乎不太对。

 

 

  同样的,行同步信号中的4个时间段也是这样排的。

 

  上图中abcd4个段分别表示行消隐前肩、行同步时间、行消隐后肩和行有效时间。

还有一个比较重要的点,之前没提到,就是ADV7123的BLANK信号,这个信号用于关断输出,当BLANK信号为低时,关断输出,当BLANK信号为高时才能有输出,当然如果此时RGB信号本来就为0那肯定还是没有输出的,因此如果把RGB信号控制好,是完全不需要BLANK信号的。

  有了这个,那程序就好写了,直接附上程序:

  1 module vga_out (
  2     input sys_clk,
  3     input sys_rst_n,
  4     
  5     output vga_clk,
  6     output reg vga_hs,
  7     output reg vga_vs,
  8     output reg vga_blk,
  9     
 10     output reg [7:0] vga_r,
 11     output reg [7:0] vga_g,
 12     output reg [7:0] vga_b
 13 );
 14 
 15 wire locked;
 16 wire rst_n;
 17 
 18 reg [11:0] hcnt;
 19 reg [11:0] vcnt;
 20 
 21 assign rst_n = sys_rst_n & locked;
 22 
 23 pll_clk u_pll_clk (
 24     .areset (~sys_rst_n),
 25     .inclk0 (sys_clk),
 26     .c0     (vga_clk),
 27     .locked (locked)
 28 );
 29 
 30 //162M标准,1600*1200*60 75KHz
 31 parameter   H_DISP      =   12'd1600;    // 有效行数
 32 parameter   H_FRONT     =   12'd64;      // 行消隐前肩
 33 parameter   H_SYNC      =   12'd192;     // 行同步
 34 parameter   H_BACK      =   12'd304;     // 行消隐后肩
 35 parameter   H_TOTAL     =   12'd2160;    // 行总数
 36 
 37 parameter   V_DISP      =   12'd1200;    // 有效列数
 38 parameter   V_FRONT     =   12'd1;       // 列消隐前肩
 39 parameter   V_SYNC      =   12'd3;       // 列同步
 40 parameter   V_BACK      =   12'd46;      // 列消隐后肩
 41 parameter   V_TOTAL     =   12'd1250;    // 列总数
 42 
 43 
 44 //行同步信号发生器
 45 always @ (posedge vga_clk or negedge rst_n)
 46 begin
 47     if (!rst_n) begin
 48         hcnt    <= 0;
 49         vga_hs  <= 1;
 50     end
 51     
 52     else begin
 53         if (hcnt < H_TOTAL-1)
 54             hcnt <= hcnt+1'b1;
 55         else
 56             hcnt <= 0;
 57             
 58         if((hcnt >= H_FRONT) && (hcnt < H_FRONT+H_SYNC))
 59             vga_hs <= 0;
 60         else
 61             vga_hs <= 1;
 62     end
 63 end
 64 
 65 // 场同步信号发生器
 66 always @ (negedge vga_hs or negedge rst_n)
 67 begin
 68     if (!rst_n) begin
 69         vcnt    <= 0;
 70         vga_vs  <= 1;
 71     end
 72     
 73     else begin
 74         if (vcnt < V_TOTAL-1)
 75             vcnt <= vcnt+1'b1;
 76         else
 77             vcnt <= 0;  
 78 
 79         if((vcnt >= V_FRONT) && (vcnt < V_FRONT+V_SYNC))
 80             vga_vs <= 0;
 81         else
 82             vga_vs <= 1;        
 83     end
 84 end
 85 
 86 //消影信号
 87 always @(posedge vga_clk or negedge rst_n) begin
 88     if (!rst_n) 
 89         vga_blk <= 1'b0;
 90         
 91     if (vga_clk) begin
 92         if (vcnt < V_FRONT + V_SYNC + V_BACK)
 93             vga_blk <= 1'b0;
 94         else if (hcnt < H_FRONT + H_SYNC + H_BACK)
 95             vga_blk <= 1'b0;
 96         else 
 97             vga_blk <= 1'b1;
 98     end
 99 end
100 
101 /*
102 //横条纹
103 always @(posedge vga_clk) begin
104     if (vcnt < (V_DISP >> 3) * 1 + V_FRONT + V_SYNC + V_BACK) begin
105         vga_r <= 8'b1111_1111;
106         vga_g <= 8'b0000_0000;
107         vga_b <= 8'b1000_0000;
108     end
109     
110     else if (vcnt < (V_DISP >> 3) * 2 + V_FRONT + V_SYNC + V_BACK) begin
111         vga_r <= 8'b0000_0000;
112         vga_g <= 8'b0000_0000;
113         vga_b <= 8'b1111_1111;
114     end
115 
116     else if (vcnt < (V_DISP >> 3) * 3 + V_FRONT + V_SYNC + V_BACK) begin
117         vga_r <= 8'b0000_0000;
118         vga_g <= 8'b1111_1111;
119         vga_b <= 8'b0000_0000;
120     end
121 
122     else if (vcnt < (V_DISP >> 3) * 4 + V_FRONT + V_SYNC + V_BACK) begin
123         vga_r <= 8'b0000_0000;
124         vga_g <= 8'b1111_1111;
125         vga_b <= 8'b1111_1111;
126     end
127 
128     else if (vcnt < (V_DISP >> 3) * 5 + V_FRONT + V_SYNC + V_BACK) begin
129         vga_r <= 8'b1111_1111;
130         vga_g <= 8'b0000_0000;
131         vga_b <= 8'b0000_0000;
132     end
133 
134     else if (vcnt < (V_DISP >> 3) * 6 + V_FRONT + V_SYNC + V_BACK) begin
135         vga_r <= 8'b1111_1111;
136         vga_g <= 8'b0000_0000;
137         vga_b <= 8'b1111_1111;
138     end
139 
140     else if (vcnt < (V_DISP >> 3) * 7 + V_FRONT + V_SYNC + V_BACK) begin
141         vga_r <= 8'b1111_1111;
142         vga_g <= 8'b1111_1111;
143         vga_b <= 8'b0000_0000;
144     end
145 
146     else if (vcnt < V_TOTAL) begin
147         vga_r <= 8'b1111_1111;
148         vga_g <= 8'b1111_1111;
149         vga_b <= 8'b1111_1111;
150     end
151 end 
152 */
153 
154 
155 //竖条纹
156 always @(posedge vga_clk) begin
157     if (hcnt < H_FRONT + H_SYNC + H_BACK) begin //前消隐+同步时间段+后消隐期无颜色(黑色)
158         vga_r <= 8'b0000_0000;
159         vga_g <= 8'b0000_0000;
160         vga_b <= 8'b0000_0000;
161     end
162     
163     else if (hcnt < (H_DISP >> 3) * 1 + H_FRONT + H_SYNC + H_BACK) begin
164         vga_r <= 8'b1111_1111;
165         vga_g <= 8'b0000_0000;
166         vga_b <= 8'b1000_0000;
167     end
168     
169     else if (hcnt < (H_DISP >> 3) * 2 + H_FRONT + H_SYNC + H_BACK) begin
170         vga_r <= 8'b0000_0000;
171         vga_g <= 8'b0000_0000;
172         vga_b <= 8'b1111_1111;
173     end
174 
175     else if (hcnt < (H_DISP >> 3) * 3 + H_FRONT + H_SYNC + H_BACK) begin
176         vga_r <= 8'b0000_0000;
177         vga_g <= 8'b1111_1111;
178         vga_b <= 8'b0000_0000;
179     end
180 
181     else if (hcnt < (H_DISP >> 3) * 4 + H_FRONT + H_SYNC + H_BACK) begin
182         vga_r <= 8'b0000_0000;
183         vga_g <= 8'b1111_1111;
184         vga_b <= 8'b1111_1111;
185     end
186 
187     else if (hcnt < (H_DISP >> 3) * 5 + H_FRONT + H_SYNC + H_BACK) begin
188         vga_r <= 8'b1111_1111;
189         vga_g <= 8'b0000_0000;
190         vga_b <= 8'b0000_0000;
191     end
192 
193     else if (hcnt < (H_DISP >> 3) * 6 + H_FRONT + H_SYNC + H_BACK) begin
194         vga_r <= 8'b1111_1111;
195         vga_g <= 8'b0000_0000;
196         vga_b <= 8'b1111_1111;
197     end
198 
199     else if (hcnt < (H_DISP >> 3) * 7 + H_FRONT + H_SYNC + H_BACK) begin
200         vga_r <= 8'b1111_1111;
201         vga_g <= 8'b1111_1111;
202         vga_b <= 8'b0000_0000;
203     end
204 
205     else if (hcnt < H_TOTAL) begin
206         vga_r <= 8'b1111_1111;
207         vga_g <= 8'b1111_1111;
208         vga_b <= 8'b1111_1111;
209     end
210 end
211 
212 endmodule

需要注意的是,PLL锁相环产生的时钟要是162MHz。