VGA的缩写来自Video Graphics Array,视频图形阵列,一种使用模拟信号进行视频传输的标准,DE2-115开发板上的VGA接口如下:
VGA的引脚定义如下:
pin 1 2 3 是模拟输入,输入范围是0v到0.714v,pin 13 pin14是数字信号输入(TTL电平):
行同步时序:
图像由一个个像素点组成, 从左到右进行扫描。一行扫描结束以后,回到下一行继续扫描。每扫描一行所需的时间称为一个行扫描周期。
场同步时序:
每扫描一帧,是一个场扫描周期。
总结:
- 行同步时序的基本单位是完成一个像素点显示所需要的时间, a、b、c、d、e 的基本单位 是一个像素的扫描周期。
- 场同步时序的基本单位是完成一行图像显示所需要的时间, o、p、q、r、d 的基本单位 是一个完整的行扫描周期。
分辨率:
640x480@60的解释:
640是指VGA显示器每一行有640个像素点;
480是指VGA显示器每一帧有640行;
60 是VGA 显示器每秒钟刷新60次,也就是每秒显示60帧图像。
像素时钟的计算:
从分辨率和行同步时序以及场同步时序我们可以计算得到像素的时钟, 比如640*480@60分辨率的像素时钟是800*525*60=25.2M。
VGA传输的是模拟信号, 所以视频信号需要通过数模转换的过程然后才能输出到 外部的VGA接口上去,这个过程可以用专门的视频转换芯片ADV7123去实现,也可以用更简单的方案:使用一系列的电阻将数字信号转换撑模拟信号(权电阻网络)。
权电阻网络:
采用权电阻网络实现DAC转换过程:
电阻的阻值 成倍数关系。 电阻越大,权值越小。
为啥DE10-Lite 和 DE0-CV 上的权电阻网络选用的是2K排阻和1K排组呢?
首先,因为VGA显示器的VGA接口内部有一个 75ohm的电阻 用于阻抗匹配 :
根据权电阻网络要求电阻值成倍数关系,我们可以计算如下:
(因为贴片电阻没有一模一样阻值的电阻, 所以我们选择阻值相近的电阻即可,2037ohm 就近似选择2K的贴片电阻)
DE2-115的VGA 硬件:
ADV7123芯片是一款高速数模转换器。它内置三个高速、位宽为10位、带互补输出的视频DAC。该芯片的主要功能是将RGB888的颜色数据转换成模拟的电压信号,然后送到VGA接口的3个通道(R、G、B)。RGB888颜色数据是24位(R、G、B分量各占8位),共可显示 2^24=16777216 种颜色。
DE10-Standard、DE1-SOC、DE2-115开发板上都有一个15 引脚的D-SUB连接器,用于VGA输出。
与权电阻网络相比, ADV7123多了VGA_CLK和VGA_BLANK_N和VGA_SYNC_N三个信号,这三个信号根据ADV7123手册定义:
CLOCK是像素时钟信号,由FPGA端VGA_CLK信号提供,如果我们选择显示分辨率640*480@60Hz的话,VGA_CLK信号应该输出25.2MHz(800*525*60)时钟。
SYNC是复合同步控制IOG输入信号,这里我们不用这个模式就直接将该信号拉低为0(assign VGA_SYNC_N=1'b0)。
BLANK为复合消隐信号,低电平有效。我们先来了解消隐。
在显示器的扫描过程中,扫描总是从显示器的左上角开始,水平向前行进,同时扫描点也以较慢的速率向下移动。当扫描点到达显示器右侧边缘时,扫描点快速返回左侧,重新开始在第1行的起点下面进行第2行扫描。当扫描完显示器上所有的行,就形成一帧,然后电子束要从显示器的右下角返回到显示器的左上角,开始新一帧的扫描。
电子束在行与行之间的返回过程以及在帧与帧之间的返回过程(从右下角返回到显示器的左上角)都称之为逆程,扫描正程期间传送图像信号,逆程期间不传送图像信号。电子束逆程扫描在荧光屏上出现回扫线,将对正程的图像造成干扰,影响图像的清晰度。因此需使显示器在行、场扫描逆程期间电子束截止,以消除行、场逆程回扫线,即实现消隐。
当BLANK为低电平时,模拟视频输出消隐电平,此时从R7~R0,G7~G0,B7~B0输入的所有数据被忽略。所以在本设计中VGA_BLANK_N信号应该这样设计:在VGA有效显示区为高,有效显示区之外为低。
至此三个信号分析完毕,完整代码也就可以写出来了。
top文件代码:
module vga( input wire clk, input wire rst_n, output wire [23:0] vga_rgb, output wire vga_hs, output wire vga_vs, output wire vga_clk, output wire vga_blank_n, output wire vga_sync_n ); wire clk_25m; wire pll_locked; pll pll_inst ( .rst ( ~rst_n ), .refclk ( clk ), .outclk_0 ( clk_25m ), .locked ( pll_locked) ); vga_ctrl vga_ctrl_inst ( .clk (clk_25m ), .rst_n (pll_locked ), .vga_rgb (vga_rgb ), .vga_hs (vga_hs ), .vga_blanck_n (vga_blank_n), .vga_vs (vga_vs ) ); assign vga_sync_n=1'b0; //If not IOG, Sync input should be tied to 0; assign vga_clk=clk_25m; endmodule
VGA控制代码:
`define VGA_640x480x60 // choose different video standard,revise PLL clk ,alter cnt WIDTH//`define VGA_640X480X75 //`define VGA_800X600X60 //`define VGA_800X600X75 //`define VGA_1024X768X60 //`define VGA_1024X768X75 //`define VGA_1280X1024X60 //`define VGA_1280X800X60 //`define VGA_1440X900X60 module vga_ctrl ( input wire clk, input wire rst_n, output reg [23:0] vga_rgb, output reg vga_hs, output reg vga_vs, output wire vga_blanck_n ); //================ VGA_640X480X60 ========================================================= `ifdef VGA_640x480x60 // PLL clk = 25M = 640x480x60 localparam HS_A = 96; // synchronous pulse, horizontal localparam HS_B = 48; // back porch pulse localparam HS_C = 640; // display interval localparam HS_D = 16; // Front porch localparam HS_E = 800; // horizontal cycles localparam VS_A = 2; // synchronous pulse, vertical localparam VS_B = 33; localparam VS_C = 480; localparam VS_D = 10; localparam VS_E = 525; localparam HS_WIDTH = 10; localparam VS_WIDTH = 10; `endif parameter CNT_VS_R = 195; // 195 = 2+33+160, 35~195 is red 有效数据q段是480行,第一个160行是红 parameter CNT_VS_G = 355; // 195~355 is green 第二个160行是绿,剩下的就是蓝了 reg [HS_WIDTH - 1:0] cnt_hs; // counter for vertical synchronous signal reg [VS_WIDTH - 1:0] cnt_vs; // counter for horizontal synchrous signal wire en_hs; // dsiplay horizontal enable wire en_vs; // display vertical enable wire en; // effective display zone wire en_vs_r; // red stripe enable wire en_vs_g; // green stripe enable always @ (posedge clk, negedge rst_n) if (!rst_n) cnt_hs <= 0; else if (cnt_hs < HS_E - 1) cnt_hs <= cnt_hs + 1'b1; else cnt_hs <= 0; always @ (posedge clk, negedge rst_n) if (!rst_n) cnt_vs <= 0; else if (cnt_hs == HS_E - 1) if (cnt_vs < VS_E - 1) cnt_vs <= cnt_vs + 1'b1; else cnt_vs <= 0; else cnt_vs <= cnt_vs; always @ (posedge clk, negedge rst_n) if (!rst_n) vga_hs <= 1'b1; else if (cnt_hs < HS_A - 1) //同步之前vga_hs信号都是低, 同步之后(a)vga_hs信号是高 vga_hs <= 1'b0; else vga_hs <= 1'b1; always @ (posedge clk, negedge rst_n) if (!rst_n) vga_vs <= 1'b1; else if (cnt_vs < VS_A - 1) //同步之前vga_vs 信号都是低, 同步之后(a)vga_vs 信号是高 vga_vs <= 1'b0; else vga_vs <= 1'b1; assign en_hs = (cnt_hs > HS_A + HS_B - 1)&& (cnt_hs < HS_E - HS_D);//en_hs 将有效数据c段标出来了,有效数据c段en_hs 才为高,否则为低 assign en_vs = (cnt_vs > VS_A + VS_B - 1) && (cnt_vs < VS_E - VS_D);//en_vs 将有效数据q段标出来了,有效数据q段en_hs 才为高,否则为低 assign en_vs_r = (cnt_vs > VS_A + VS_B - 1) && (cnt_vs < CNT_VS_R);//场同步的第一个160行 assign en_vs_g = (cnt_vs > CNT_VS_R - 1) && (cnt_vs < CNT_VS_G); //场同步的第二个160行 assign en = en_hs && en_vs;//将vga显示的有效像素点位置全部标注出来了 assign vga_blanck_n = en; always @ (posedge clk, negedge rst_n) if (!rst_n) vga_rgb <= 24'b000000000000000000000000; else if (en) if (en_vs_r) vga_rgb <= 24'b000000000000000011111111; // 红 else if (en_vs_g) vga_rgb <= 24'b000000001111111100000000; // 绿 else vga_rgb <= 24'b111111110000000000000000; // 蓝 else vga_rgb <= 24'b000000000000000000000000; endmodule
PLL IP 设置:
管脚分配:
DE10-Standard的VGA 管脚分配:
DE2-115的VGA 管脚分配:
DE1-SOC的VGA 管脚分配:
下板验证:
DE10-Standard:
参考视频:
参考代码:
https://zhuanlan.zhihu.com/p/531695750
往期精彩
基于友晶科技 FPGA开发板 DE2-115、DE1-SOC 和 DE10-STANDARD 的VGA图片显示(ADV7123)
基于友晶科技FPGA开发板 DE0-CV、DE10-Lite 上实现VGA彩条显示设计(权电阻网络) - Doreen的FPGA自留地 - 博客园 (cnblogs.com)
友晶科技FPGA开发板DE10-NANO开发板的HDMI 方块移动实验 - Doreen的FPGA自留地 - 博客园 (cnblogs.com)