这个BT1120接口是在1080P 60Hz的视频中验证的,其它频率的视频使用时要修改对应的参数。另外由于接口代码里面例化了一个深度位512的FIFO(quartus),所以在做仿真测试时需要quartus和modelsim联合仿真。
bt1120接口最重要的部分是结束码和起始码(FF 00 00 XYZ)
前面3字节的FF 00 00 是固定不变的,最后一字节需要根据F V H来编码,当FVH确定时P3 P2 P1 P0也确定了。使用8bit的数据位宽时保留高8位,舍去低2位。
整理后的接口
接口代码文章来源:https://www.toymoban.com/news/detail-602883.html
/* 定时基准码 <0xff 0x00 0x00 xxx>
* 其中xxx为如下的取值范围:
* 1 0 1 0 1 0 1 1 0 0 0xab(帧消隐期间,SAV)
* 1 0 1 1 0 1 1 0 0 0 0xb6(帧消隐期间,EAV)
* 1 0 0 0 0 0 0 0 0 0 0x80(视频有效区时间,SAV)
* 1 0 0 1 1 1 0 1 0 0 0x9d(视频有效区时间,EAV)
*/
`timescale 1ns / 1ps
module ycbcr422_to_bt1120(
input rst_n,
input clk ,
input data_de,
input hsync,
input vsync,
input [15:0] ycbcr,
output bt1120_pclk,
output reg [15:0] bt1120_ycbcr
);
localparam BLANKING = 4'd0; //消隐阶段
//SAV
localparam CODE_SAV1 = 4'd1; //数据开始码阶段
localparam CODE_SAV2 = 4'd2;
localparam CODE_SAV3 = 4'd3;
localparam CODE_SAV4 = 4'd4;
//EAV
localparam CODE_EAV1 = 4'd5; //数据结束码阶段
localparam CODE_EAV2 = 4'd6;
localparam CODE_EAV3 = 4'd7;
localparam CODE_EAV4 = 4'd8;
localparam VAILD_VIDEO = 4'd9; //数据有效阶段
//在行场消隐区填充STUFF
localparam STUFF = 16'h8010;
localparam BSAV = 8'hab;
localparam BEAV = 8'hb6;
localparam VSAV = 8'h80;
localparam VEAV = 8'h9d;
//1080p 60hz
localparam WIDTH_TOTAL = 12'd2200 ; //一行的宽度
localparam HEIGHT_TOTAL= 12'd1125 ; //一帧的高度
localparam VIDEO_BEFORE_BLANK_NUM = 6'd41 ; //一帧开始前的帧消隐行数
localparam VIDEO_AFTER_BLANK_NUM = 3'd4 ; //一帧结束后的帧消隐行数
localparam BLANK_NUM = 12'd280 ;
wire full ;
reg rd_en ;
wire[15:0] rd_data ;
wire empty ;
reg [3:0] state_c ;
reg [3:0] state_n ;
wire blank2sav ;
wire video2eav ;
reg data_de0 ;
reg hsync0 ;
reg vsync0 ;
reg [15:0] ycbcr0 ;
reg data_de1 ;
reg hsync1 ;
reg vsync1 ;
reg [15:0] ycbcr1 ;
wire v_pos ;
reg [11:0] cnt_h ;
reg [11:0] cnt_v ;
wire[7:0] Lumi ;
wire[7:0] cbcr ;
assign bt1120_pclk = clk ;
yc2bt_fifo yc2bt_fifo_inst0
(
.clock (clk ),
.data (ycbcr1 ),
.wrreq (data_de1),
.full (full ),
.rdreq (rd_en ),
.q (rd_data ),
.empty (empty )
);
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= BLANKING;
end
else begin
state_c <= state_n;
end
end
always@(*)begin
case(state_c)
BLANKING:begin
if(blank2sav)begin
state_n = CODE_SAV1;
end
else begin
state_n = state_c;
end
end
CODE_SAV1:begin
state_n = CODE_SAV2;
end
CODE_SAV2:begin
state_n = CODE_SAV3;
end
CODE_SAV3:begin
state_n = CODE_SAV4;
end
CODE_SAV4:begin
state_n = VAILD_VIDEO;
end
VAILD_VIDEO:begin
if(video2eav)begin
state_n = CODE_EAV1;
end
else begin
state_n = state_c;
end
end
CODE_EAV1:begin
state_n = CODE_EAV2;
end
CODE_EAV2:begin
state_n = CODE_EAV3;
end
CODE_EAV3:begin
state_n = CODE_EAV4;
end
CODE_EAV4:begin
state_n = BLANKING;
end
default:begin
state_n = BLANKING;
end
endcase
end
assign blank2sav = state_c==BLANKING && cnt_h==12'd275;
assign video2eav = state_c==VAILD_VIDEO && cnt_h==12'd2199;
//输入打拍
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
data_de0 <= 1'b0 ;
hsync0 <= 1'b0 ;
vsync0 <= 1'b0 ;
ycbcr0 <= 16'b0 ;
end
else begin
data_de0 <= data_de ;
hsync0 <= hsync ;
vsync0 <= vsync ;
ycbcr0 <= ycbcr ;
data_de1 <= data_de0 ;
hsync1 <= hsync0 ;
vsync1 <= vsync0 ;
ycbcr1 <= ycbcr0 ;
end
end
//获取场信号上升沿
assign v_pos = !vsync1 && vsync0 ;
//一行计数器
always @(posedge clk )begin
if(v_pos || cnt_h == WIDTH_TOTAL-1)
cnt_h <= 12'b0;
else
cnt_h <= cnt_h + 1;
end
//一帧计数器
always @(posedge clk )begin
if(v_pos)
cnt_v <= 12'b0;
else if(cnt_h == WIDTH_TOTAL-1)begin
if(cnt_v == HEIGHT_TOTAL-1)
cnt_v <= 12'b0;
else
cnt_v <= cnt_v + 1;
end
end
//出数据
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
bt1120_ycbcr <= STUFF ;
end
else begin
case(state_c)
BLANKING:begin
bt1120_ycbcr<= STUFF ;
end
CODE_SAV1:begin
bt1120_ycbcr<= 16'hffff ;
end
CODE_SAV2:begin
bt1120_ycbcr<= 16'h0 ;
end
CODE_SAV3:begin
bt1120_ycbcr<= 16'h0 ;
end
CODE_SAV4:begin
if(cnt_v<12'd41 ||(cnt_v >= 12'd1121 && cnt_v < 12'd1125))
bt1120_ycbcr<= 16'habab ;
else if(cnt_v>=12'd41 && cnt_v<12'd1121)
bt1120_ycbcr<= 16'h8080 ;
end
VAILD_VIDEO:begin
bt1120_ycbcr<= rd_data ;
end
CODE_EAV1:begin
bt1120_ycbcr <= 16'hffff;
end
CODE_EAV2:begin
bt1120_ycbcr <= 16'h0;
end
CODE_EAV3:begin
bt1120_ycbcr <= 16'h0;
end
CODE_EAV4:begin
if(cnt_v<12'd41 ||(cnt_v >= 12'd1121 && cnt_v < 12'd1125))
bt1120_ycbcr<= 16'hb6b6 ;
else if(cnt_v>=12'd41 && cnt_v<12'd1121)
bt1120_ycbcr<= 16'h9d9d ;
end
endcase
end
end
assign Lumi = bt1120_ycbcr[15:8] ;
assign cbcr = bt1120_ycbcr[7:0] ;
//read en
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rd_en <= 1'b0 ;
end
else if(cnt_v>=12'd41 && cnt_v < 12'd1121 )begin
if(cnt_h>=12'd279 && cnt_h<12'd2200)
rd_en <= 1'b1 ;
else
rd_en <= 1'b0 ;
end
else begin
rd_en <= 1'b0 ;
end
end
endmodule
测试文件文章来源地址https://www.toymoban.com/news/detail-602883.html
`timescale 1 ns/1ps
module tb_bt1120();
reg clk ;
reg rst_n;
wire[15:0] ycrcb;
reg de ;
reg vsync;
reg hsync;
reg[11:0] cnt_h;
reg[11:0] cnt_v;
reg[7:0] lumi ;
reg[7:0] cbcr ;
//uut的输出信号
wire bt1120_clk;
wire[15:0] bt1120_data;
//时钟周期,单位为ns,可在此修改时钟周期。
parameter CYCLE = 7;
//复位时间,此时表示复位3个时钟周期的时间。
parameter RST_TIME = 20 ;
ycbcr422_to_bt1120 u_ycbcr422_to_bt1120(
.rst_n (rst_n ), //input
.clk (clk ), //input
.data_de (de ), //input [15:0]
.hsync (hsync ), //input
.vsync (vsync ), //input
.ycbcr (ycrcb ), //input
//bt.1120接口
.bt1120_pclk (bt1120_clk ) , //output
.bt1120_ycbcr(bt1120_data) //output reg[15:0]
);
//1080P 60Hz
parameter h_total = 12'd2200;
parameter hsync_pw = 6'd44 ; //行消隐脉冲宽度,以时钟为单位
parameter v_total = 12'd1125;
parameter vsync_pw = 3'd5 ; //行消隐脉冲宽度,以行为单位
parameter data_f_enabel = 6'd42 ; //有效数据的第一行,以行为单位
parameter data_e_enabel = 12'd1120; //有效数据的最后一行,以行为单位
parameter data_de_start = 8'd192 ; //数据有效开始,以时钟为单位
parameter data_de_end = 12'd2112; //数据有效结束,以时钟为单位
//生成本地时钟50M
initial begin
clk = 0;
forever
#(CYCLE/2)
clk=~clk;
end
//产生复位信号
initial begin
rst_n = 1;
#2;
rst_n = 0;
#(CYCLE*RST_TIME);
rst_n = 1;
end
//一行数据的计数器
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
cnt_h <= 12'b0 ;
end
else begin
if(cnt_h == h_total-1)
cnt_h <= 12'b0 ;
else
cnt_h <= cnt_h + 1 ;
end
end
//一帧数据的计数器,1080P 60hz的一帧数据共有1125行
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
cnt_v <= 12'b0 ;
end
else if(cnt_h == h_total-1)begin
if(cnt_v == v_total-1)
cnt_v <= 12'b0 ;
else
cnt_v <= cnt_v + 1 ;
end
end
//产生行信号
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
hsync <= 1'b0 ;
end
else if(cnt_h < hsync_pw )begin
hsync <= 1'b1 ;
end
else begin
hsync <= 1'b0 ;
end
end
//产生场信号
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
vsync <= 1'b0 ;
end
else if(cnt_v < vsync_pw )begin
vsync <= 1'b1 ;
end
else begin
vsync <= 1'b0 ;
end
end
//产生数据有效信号
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
de <= 1'b0 ;
lumi <= 8'b0;
cbcr <= 8'b0;
end
else if(cnt_v >= data_f_enabel-1 && cnt_v <= data_e_enabel)begin
if(cnt_h >= data_de_start-1 && cnt_h < data_de_end-1 )begin
de <= 1'b1 ;
lumi <= lumi+1;
cbcr <= cbcr+1;
end
else begin
de <= 1'b0 ;
lumi <= 8'b0;
cbcr <= 8'b0;
end
end
end
assign ycrcb = {lumi,cbcr} ;
endmodule
到了这里,关于基于FPGA的1080P 60Hz BT1120接口调试过程记录的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!