以前自己调试过视频信号,无非就时钟加行场同步加数据线,如果视频信号出问题,第一看现象,第二测频率,反正出问题不是消隐信号出问题,就是时钟频率出问题。通过这种方式也调试成功过几个显示屏,然后就以为自己对视频信号的调试已经有丰富的经验了,直到这次需要自己用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。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?