声明:本博客并非严格的、完整的技术型文章,只是将本人所接触到的内容作学习记录,如有错误欢迎大家在评论区指正。如果这篇博客对你有帮助的话,请给我点一个免费的赞或关注,谢谢。
引言
BT1120协议的全称是"ITU-R BT.1120"。"ITU"是"国际电信联盟International Telecommunication Union"的简称。BT1120技术标准由国际电信联盟(ITU-T)负责制定和维护。最新的标准是BT.1120-9 (12/2017),用于1080P视屏的传输,其属于BT族协议的一部分。下面的链接可以找到相关的技术文档(有中文版本)。
BT1120 / 2017
BT族协议
我们常见的除了BT1120,还有BT656。其协议格式基本一样,只是图像的分辨率不同。本博客只提供BT1120的部分相关知识,其余内容可以在BT族协议链接中找到。注意:BT1120的协议手册中的一些内容本博客也没有讲到,具体可以查看原版协议。
BT1120规范
首先要明确什么是逐行扫描和隔行扫描。拿1080p和1080i举个例子。
1080i的特点:
- 1080i是高清电视的一种分辨率格式,其中的“i”代表隔行扫描(interlaced),意味着图像数据是分两次扫描完成的,每次扫描一半的图像线。
- 1080i的分辨率为1920x1080,但实际显示的图像分辨率为1920x540,因为图像数据是分两次扫描完成的。
- 1080i的图像质量相对较低,因为图像数据是通过隔行扫描的方式显示的,这可能导致图像边缘出现锯齿状的闪烁。
1080p的特点:
- 1080p是高清电视的另一种分辨率格式,其中的“p”代表逐行扫描(progressive),意味着图像数据是一次性完整扫描完成的。
- 1080p的分辨率为1920x1080,并且实际显示的图像分辨率也是1920x1080,因为图像数据是一次性完整扫描完成的。
- 1080p的图像质量相对较高,因为图像数据是通过逐行扫描的方式显示的,这可以提供更加平滑和清晰的图像效果。
下图是BT1120中隔行与逐行的图像格式示意图。其中SAV和EAV称之为定时基准码。SAV代表每个视频数据块的起始,EAV代表每个视频数据位的结束。有效视频所在的行其余数据为行消隐,有效视频不在的行所有内容为场消隐。
下面图是分别对隔行与逐行的行号数做定义。
定时基准码分为4个字,每个字拥有10个或8个bit,其具体的bit分配可以通过下面的图推断出来。其中前三个字是固定的,第四个字的FVH分别由隔行/逐行扫描、场/帧消隐期、SAV/EAV决定,而保护比特P0、P1、P2、P3是通过FVH对应的查找表得到。
当然我这里也给出我们最常用的1080p的SAV与EAV的第4个字的数据。其他前面的三个字分别为8‘hFF(或10‘h3FF)、00、00。
FPGA中的BT1120
在FPGA中BT1120格式常用于SDI高速接口、FPGA与外设芯片(如Hi3519)等传递图像数据。
管脚绑定
最常用的管脚是1bit的随路时钟与16bit的数据,其中16bit数据是YUV422的格式,8bit为Y代表亮度分量,8bit为U/V代表颜色分量。
此外BT1120可以通过SDI高速接口发送,或通过1bit差分时钟加4bit差分数据的方式与Hi3519等芯片交互。具体的硬件接口需要与实际工程相结合。
代码示例
下面提供一份代码示例,输入是DVP图像数据格式,输出是BT1120数据。
module dvp_to_bt1120(
//--- bt1120 interface ---//
o_bt1120_data,
//--- dvp interface ---//
i_vs, i_hs, i_de, i_data,
//--- system interface ---//
i_clk, i_rst
);
//--------------------------------------------------------------------------------
// input and output ports
//--------------------------------------------------------------------------------
//--- system interface ---//
input i_clk;
input i_rst;
//--- dvp interface ---//
input i_vs;
input i_hs;
input i_de;
input [19:0] i_data; // YUV422 {Y[9:0], UV[9:0]}
//--- bt1120 interface ---//
output [19:0] o_bt1120_data;
//--------------------------------------------------------------------------------
// register and wire declaration for output ports
//--------------------------------------------------------------------------------
reg [19:0] o_bt1120_data;
//--------------------------------------------------------------------------------
// register and wire declaration for internal signals
//--------------------------------------------------------------------------------
//--- delay signals
reg [3:0] vs_in_dly;
reg hs_in_dly;
reg [4:0] de_in_dly;
reg [19:0] data_in_dly [4:0];
//--- gen sav/eav valid
wire hs_in_pos;
wire hs_in_neg;
reg sav_de;
reg eav_de;
reg [2:0] sav_cnt;
reg [2:0] eav_cnt;
//********************************************************************************
// functional block start
//********************************************************************************
//--- delay signals
always @(posedge i_clk or posedge i_rst) begin
if (i_rst) begin
vs_in_dly <= 4'd0;
end else begin
vs_in_dly <= {vs_in_dly[2:0], i_vs};
end
end
always @(posedge i_clk or posedge i_rst) begin
if (i_rst) begin
hs_in_dly <= 1'b0;
end else begin
hs_in_dly <= i_hs;
end
end
always @(posedge i_clk or posedge i_rst) begin
if (i_rst) begin
de_in_dly <= 5'd0;
end else begin
de_in_dly <= {de_in_dly[3:0], i_de};
end
end
always @(posedge i_clk or posedge i_rst) begin
if (i_rst) begin
data_in_dly[0] <= 20'd0;
data_in_dly[1] <= 20'd0;
data_in_dly[2] <= 20'd0;
data_in_dly[3] <= 20'd0;
data_in_dly[4] <= 20'd0;
end else begin
data_in_dly[0] <= i_data;
data_in_dly[1] <= data_in_dly[0];
data_in_dly[2] <= data_in_dly[1];
data_in_dly[3] <= data_in_dly[2];
data_in_dly[4] <= data_in_dly[3];
end
end
//--- gen sav/eav valid
assign hs_in_pos = i_hs & (~hs_in_dly);
assign hs_in_neg = (~i_hs) & hs_in_dly;
always @(posedge i_clk or posedge i_rst) begin
if (i_rst) begin
sav_de <= 1'b0;
end else if(sav_cnt == 3'd3) begin
sav_de <= 1'b0;
end else if(hs_in_pos) begin
sav_de <= 1'b1;
end else begin
sav_de <= sav_de;
end
end
always @(posedge i_clk or posedge i_rst) begin
if (i_rst) begin
sav_cnt <= 3'd0;
end else if(sav_cnt == 3'd3) begin
sav_cnt <= 3'd0;
end else if(sav_de) begin
sav_cnt <= sav_cnt + 3'd1;
end else begin
sav_cnt <= sav_cnt;
end
end
always @(posedge i_clk or posedge i_rst) begin
if (i_rst) begin
eav_de <= 1'b0;
end else if(eav_cnt == 3'd7) begin
eav_de <= 1'b0;
end else if(hs_in_neg) begin
eav_de <= 1'b1;
end else begin
eav_de <= eav_de;
end
end
always @(posedge i_clk or posedge i_rst) begin
if (i_rst) begin
eav_cnt <= 3'd0;
end else if(eav_cnt == 3'd7) begin
eav_cnt <= 3'd0;
end else if(eav_de) begin
eav_cnt <= eav_cnt + 3'd1;
end else begin
eav_cnt <= eav_cnt;
end
end
//--- gen outputs
always @(posedge i_clk or posedge i_rst) begin
if (i_rst) begin
o_bt1120_data <= 20'd0;
end else if(sav_de) begin
case (sav_cnt)
3'd0: o_bt1120_data <= {10'h3ff, 10'h3ff};
3'd1: o_bt1120_data <= {10'h000, 10'h000};
3'd2: o_bt1120_data <= {10'h000, 10'h000};
3'd3: begin
if (vs_in_dly[3] == 1'b1) begin
o_bt1120_data <= {8'h80,2'b00,8'h80,2'b00};
end else begin
o_bt1120_data <= {8'hab,2'b00,8'hab,2'b00};
end
end
default: o_bt1120_data <= {8'h80,2'b00,8'h10,2'b00};
endcase
end else if(eav_de && (eav_cnt > 3'd3)) begin
case (eav_cnt)
3'd4: o_bt1120_data <= {10'h3ff, 10'h3ff};
3'd5: o_bt1120_data <= {10'h000, 10'h000};
3'd6: o_bt1120_data <= {10'h000, 10'h000};
3'd7: begin
if (vs_in_dly[3] == 1'b1) begin
o_bt1120_data <= {8'h9d,2'b00,8'h9d,2'b00};
end else begin
o_bt1120_data <= {8'hb6,2'b00,8'hb6,2'b00};
end
end
default: o_bt1120_data <= {8'h80,2'b00,8'h10,2'b00};
endcase
end else if(de_in_dly[4] == 1'b1) begin
o_bt1120_data <= data_in_dly[4];
end else begin
o_bt1120_data <= {8'h80,2'b00,8'h10,2'b00};
end
end