一、前言
VTC(Video Timing Controller)是一种用于产生视频时序的控制器,在图像领域经常会输出各种分辨率和帧率的视频格式。因此为了方便,设置一个VTC控制模块,只需要在顶层修改一下需要输出的视频格式,就能自动的产生对应的时序,这样能方便处理一点。Xilinx Vivado 也有专门用于生成视频时序的 IP,叫做 Video Timing Controller 核,官方的说明在 PG016,本文使用Verilog实现VTC的基本功能。
二、视频时序控制原理
在《VGA接口时序以及FPGA实现》文章中,我们实现了1080P的时序并且成功的在显示器上显示出来了。VTC原理和VGA一样,只需要在给正确的时钟频率产生出正确的VS和HS信号即可。视频显示原理如下:
显示器显示图像主要由行同步信号和场同步信号构成:
- 每一行又分为:行同步信号H_SYNC;行后沿信号H_BACK_PORCH;行数据有效信号H_ACTIVE;行前沿信号 H_FRONT_PORCH。
- 每一列同样分为:场同步信号V_SYNC;场后沿信号V_BACK_PORCH;场数据有效信号V_ACTIVE;场前沿信号V_FRONT_PORCH。
- 只有在行数据有效信号H_ACTIVE和场数据有效信号V_ACTIVE都有效时,才输出de信号。
常见分辨率视频所对应的各信号长度如下:
显示模式 | 时钟/Mhz | 行同步hsync | 行后沿 | 行有效 | 行前沿 | 行总共 | 场同步vsync | 场后沿 | 场有效 | 场前沿 | 场总共 |
---|---|---|---|---|---|---|---|---|---|---|---|
640×480@60Hz | 25.2 | 96 | 48 | 640 | 16 | 800 | 2 | 33 | 480 | 10 | 525 |
800×600@60Hz | 40 | 128 | 88 | 800 | 40 | 1056 | 4 | 23 | 600 | 1 | 628 |
1024×768@60Hz | 65 | 136 | 160 | 1024 | 24 | 1344 | 6 | 29 | 768 | 3 | 806 |
1280×720@60Hz | 74.25 | 40 | 220 | 1280 | 110 | 1650 | 5 | 20 | 720 | 5 | 750 |
1280×1024@60Hz | 108 | 112 | 248 | 1280 | 48 | 1688 | 3 | 38 | 1024 | 1 | 1066 |
1920×1080@60Hz | 148.5 | 44 | 148 | 1920 | 88 | 2200 | 5 | 36 | 1080 | 4 | 1125 |
3840×2160@60Hz | 594 | 88 | 296 | 3840 | 196 | 4400 | 10 | 72 | 2160 | 8 | 2250 |
三、Verilog实现
3.1 代码
module vtc#
(
parameter H_SYNC = 96, //行同步信号
parameter H_BACK_PORCH = 48, //行后沿
parameter H_ACTIVE = 640, //行有效数据
parameter H_FRONT_PORCH = 16, //行前沿
parameter V_SYNC = 2, //场同步信号
parameter V_BACK_PORCH = 33, //场后沿
parameter V_ACTIVE = 480, //场有效信号
parameter V_FRONT_PORCH = 10 //场前沿
)
(
input clk , //系统时钟
input rst_n , //系统复位
output vs , //场同步输出
output hs , //行同步输出
output de //视频数据有效
);
localparam hcnt_max = H_SYNC + H_BACK_PORCH + H_ACTIVE + H_FRONT_PORCH; //行扫描最大计数值
localparam vcnt_max = V_SYNC + V_BACK_PORCH + V_ACTIVE + V_FRONT_PORCH; //列扫描最大计数值
reg [11:0] hcnt = 12'd0; //视频水平方向,列计数器
reg [11:0] vcnt = 12'd0; //视频垂直方向,行计数器
reg [2 :0] rst_cnt = 3'd0; //复位计数器,寄存器
wire rst_sync ; //同步复位
wire hs_valid ;
wire vs_valid ;
always @(posedge clk or negedge rst_n)begin //通过计数器产生同步复位
if(rst_n == 1'b0)
rst_cnt <= 3'd0;
else if(rst_cnt[2] == 1'b0)
rst_cnt <= rst_cnt + 1'b1;
else
rst_cnt <= rst_cnt;
end
assign rst_sync = rst_cnt[2];
//视频水平方向,列计数器
always @(posedge clk)begin
if(rst_sync == 1'b0)
hcnt <= 12'd0;
else if(hcnt < (hcnt_max - 1'b1))
hcnt <= hcnt + 1'b1;
else
hcnt <= 12'd0;
end
//视频垂直方向,行计数器,用于计数已经完成的行视频信号
always @(posedge clk)begin
if(rst_sync == 1'b0)
vcnt <= 12'd0;
else if(hcnt == (hcnt_max - 1'b1)) begin
if(vcnt == (vcnt_max - 1'b1))
vcnt <= 12'd0;
else
vcnt <= vcnt + 1'b1;
end
else
vcnt <= vcnt;
end
assign hs_valid = ((hcnt >= H_SYNC +H_BACK_PORCH)&&(hcnt < H_SYNC +H_BACK_PORCH +H_ACTIVE))? 1'b1 : 1'b0; //行信号有效像素部分
assign vs_valid = ((vcnt >=V_SYNC + V_BACK_PORCH)&&(vcnt<V_SYNC+V_BACK_PORCH+V_ACTIVE))? 1'b1 : 1'b0; //场信号有效像素部分
assign hs = (hcnt < H_SYNC) ? 1'b1 : 1'b0;//产生hs,行同步信号
assign vs = (vcnt < V_SYNC) ? 1'b1 : 1'b0;//产生vs,场同步信号
assign de = hs_valid && vs_valid;//只有当视频水平方向,列有效和视频垂直方向,行同时有效,视频数据部分才是有效
endmodule
3.2 仿真以及分析
tb文件编写
`timescale 1ns / 1ps
module tb_vtc();
reg clk ;
reg rst_n ;
wire de ;
wire hs ;
wire vs ;
initial begin
clk = 0;
rst_n = 0;
#200;
rst_n = 1;
end
always # 10 clk = ~clk;
vtc#(
.H_SYNC ( 96 ),
.H_BACK_PORCH ( 48 ),
.H_ACTIVE ( 640 ),
.H_FRONT_PORCH ( 16 ),
.V_SYNC ( 2 ),
.V_BACK_PORCH ( 33 ),
.V_ACTIVE ( 480 ),
.V_FRONT_PORCH ( 10 )
)u_vtc(
.clk ( clk ),
.rst_n ( rst_n ),
.vs ( vs ),
.hs ( hs ),
.de ( de )
);
endmodule
仿真结果如下:
由上图可以看出,V_SYNC信号共占2个v_cnt周期。
由上图可以看出,H_SYNC信号共有1920ns,1920/20=96个h_cnt周期。
由上图可以看出,de信号共有12800ns,12800/20=640个h_cnt周期。综上,本次仿真符合640*480@60hz的视频时序