研究了两天的BT1120规范,以及CEA-861-D视频时序,找到了一些对应关系(见我另外一篇文章《视频时序与BT1120的关系》)。于是写了如下的verilog代码,可对接sii9134芯片的行场内嵌方式,其中sii9134配置C代码如下:
void sii9134_init(void) { u8 u8Data = 0; I2C_WriteByte(0x05, 0x08, 0x72); //value reg_addr device_addr I2C_ReadByte(&u8Data, sizeof(u8), 0x33, 0x72); I2C_WriteByte(u8Data & 0x8f, 0x33, 0x72); I2C_WriteByte(0x6e, 0x40, 0x72); I2C_WriteByte(0x28, 0x44, 0x72); I2C_WriteByte(0x00, 0x45, 0x72); I2C_WriteByte(0x05, 0x46, 0x72); I2C_WriteByte(0x05, 0x47, 0x72); I2C_WriteByte(0x30, 0x48, 0x72); I2C_WriteByte(0x3d, 0x4a, 0x72); }
FPGA实验板使用黑金的AV6045开发板。
FPGA端的bt1120 verilog代码如下(行场同步信号hsync和vsync为高电平有效):
另外,如果发现像素数据为0xff或0x00则需要替换为0xfe和0x01,防止对端误判。(本文没有做这一步)
/** * author : mkelehk@gmail.com * time : 2017/8/28 * function : ycbcr4:2:2 embed_hs_vs encoder module, compatible bt.1120 * * 定时基准码 <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 / 100ps module embed_hs_vs_enc( input rst, input yc422_pclk_i, input [15:0] yc422_data_i, input yc422_de_i, input yc422_vs_i, input yc422_hs_i, //-------------------------------------------------------- //视频时序参数,由CPU 通过i2c配置得到 input [11:0] width_i, // l input [11:0] height_i, // input [11:0] hs_rising_to_de_i, input [11:0] hor_total_g_i, input [11:0] vs_rising_to_de_i, //L2 input [11:0] ver_total_g_i, //L6 //---------------------------------------------------------- input video_pararm_enable_i, //---------------------------------------------------------- //bt.1120接口 output embed_hs_vs_pclk_o, output reg[15:0] embed_hs_vs_yc422_o ); //状态机状态 localparam INVAILD_BLANKING = 4'd0; localparam VAILD_VIDEO = 4'd1; localparam SYNC1_SAV = 4'd2; localparam SYNC2_SAV = 4'd3; localparam SYNC3_SAV = 4'd4; localparam SYNC1_EAV = 4'd5; localparam SYNC2_EAV = 4'd6; localparam SYNC3_EAV = 4'd7; localparam SAV_BKANKING = 4'd8; localparam EAV_BKANKING = 4'd9; localparam SAV_VIDEO = 4'd10; localparam EAV_VIDEO = 4'd11; //在行场消隐区填充STUFF localparam STUFF = 16'h8010; wire hs_rising; //rising or falling wire vs_rising; reg [11:0] vs_cnt;//场计数器 reg [11:0] hs_cnt;//行计数器 //复位信号要处理好,否则以下寄存器值未初始化 reg [11:0] width_g; reg [11:0] height_g; reg [11:0] hs_rising_to_de_g; reg [11:0] hor_total_g; reg [11:0] vs_rising_to_de_g; reg [11:0] ver_total_g; reg [3:0]state_cs;//当前状态 需注意寄存器变量的位宽,防止溢出 reg [3:0]state_ns;//下一个状态 //对信号进行延时操作,打1拍 //reg[15:0] yc422_data_d1; reg yc422_de_d1; reg yc422_vs_d1; reg yc422_hs_d1; always @(posedge yc422_pclk_i) begin if(rst) begin //yc422_data_d1 <= 16'h00; yc422_de_d1 <= 1'b0; yc422_vs_d1 <= 1'b0; yc422_hs_d1 <= 1'b0; end else begin //yc422_data_d1 <= yc422_data_i; yc422_de_d1 <= yc422_de_i; yc422_vs_d1 <= yc422_vs_i; yc422_hs_d1 <= yc422_hs_i; end end assign embed_hs_vs_pclk_o = yc422_pclk_i; assign hs_rising = ~yc422_hs_d1 & yc422_hs_i; assign vs_rising = ~yc422_vs_d1 & yc422_vs_i; always @(posedge yc422_pclk_i) begin if(rst) begin width_g <= 12'd1920; height_g <= 12'd1080; hs_rising_to_de_g <= 12'd192; hor_total_g <= 12'd2200; vs_rising_to_de_g <= 12'd41; ver_total_g <= 12'd1125; end else if(video_pararm_enable_i)begin width_g <= width_i; height_g <= height_i; hs_rising_to_de_g <= hs_rising_to_de_i; hor_total_g <= hor_total_g_i; vs_rising_to_de_g <= vs_rising_to_de_i; ver_total_g <= ver_total_g_i; end end //行计数 always @(posedge yc422_pclk_i) begin if(rst) hs_cnt <= 0; else if(hs_rising || video_pararm_enable_i) hs_cnt <= 0; else if(hs_cnt == hor_total_g - 1'b1) hs_cnt <= 0; else hs_cnt <= hs_cnt + 1'b1; end //帧计数 always @(posedge yc422_pclk_i) begin if(rst) vs_cnt <= 0; else if(vs_rising || video_pararm_enable_i) vs_cnt <= 0; else if(hs_cnt == hor_total_g - 1'b1) if(vs_cnt == ver_total_g - 1'b1) vs_cnt <= 0; else vs_cnt <= vs_cnt + 1'b1; else vs_cnt <= vs_cnt; end always @(posedge yc422_pclk_i) begin if(rst) state_cs <= INVAILD_BLANKING; else state_cs <= state_ns; end always @(*) begin case(state_cs) INVAILD_BLANKING : begin if(hs_cnt == hs_rising_to_de_g - 3'd6) //SAV 提前4个时钟,因为SAV和EAV要包含4个时钟周期,并且补偿state_cs和第三段带来的2拍延时 state_ns = SYNC1_SAV; else if(hs_cnt == hs_rising_to_de_g + width_g - 3'd2) //EAV 不需要提前4个时钟,但需要补偿state_cs和第三段带来的2拍延时 state_ns = SYNC1_EAV; else state_ns = INVAILD_BLANKING; end //SAV部分 SYNC1_SAV : state_ns = SYNC2_SAV; SYNC2_SAV : state_ns = SYNC3_SAV; SYNC3_SAV : if((vs_cnt >= vs_rising_to_de_g - 1'b1) && (vs_cnt <= height_g + vs_rising_to_de_g - 1'b1))//VILD_VIDEO state_ns = SAV_VIDEO; else state_ns = SAV_BKANKING; //EAV部分 SYNC1_EAV : state_ns = SYNC2_EAV; SYNC2_EAV : state_ns = SYNC3_EAV; SYNC3_EAV : if((vs_cnt >= vs_rising_to_de_g - 1'b1) && (vs_cnt <= height_g + vs_rising_to_de_g - 1'b1))//VILD_VIDEO state_ns = EAV_VIDEO; else state_ns = EAV_BKANKING; SAV_BKANKING : state_ns = INVAILD_BLANKING; EAV_BKANKING : state_ns = INVAILD_BLANKING; SAV_VIDEO : state_ns = VAILD_VIDEO; EAV_VIDEO : state_ns = INVAILD_BLANKING; VAILD_VIDEO : if(hs_cnt == hs_rising_to_de_g + width_g - 1'b1 - 1'b1) state_ns = SYNC1_EAV;//去EAV部分 else state_ns = VAILD_VIDEO; default : state_ns = INVAILD_BLANKING; endcase end always @(posedge yc422_pclk_i) begin if(rst) embed_hs_vs_yc422_o <= STUFF; else begin if(state_cs == INVAILD_BLANKING) embed_hs_vs_yc422_o <= STUFF; else if(state_cs == VAILD_VIDEO) embed_hs_vs_yc422_o <= yc422_data_i; //yc422_data_d1 else if((state_cs == SYNC1_SAV) || (state_cs == SYNC1_EAV)) embed_hs_vs_yc422_o <= 16'hffff; else if((state_cs == SYNC2_SAV) || (state_cs == SYNC2_EAV)) embed_hs_vs_yc422_o <= 16'h0000; else if((state_cs == SYNC3_SAV) || (state_cs == SYNC3_EAV)) embed_hs_vs_yc422_o <= 16'h0000; else if(state_cs == SAV_BKANKING) embed_hs_vs_yc422_o <= 16'habab; else if(state_cs == EAV_BKANKING) embed_hs_vs_yc422_o <= 16'hb6b6; else if(state_cs == SAV_VIDEO) embed_hs_vs_yc422_o <= 16'h8080; else if(state_cs == EAV_VIDEO) embed_hs_vs_yc422_o <= 16'h9d9d; else embed_hs_vs_yc422_o <= STUFF; end end endmodule
embed_hs_vs_enc U_embed_hs_vs_enc_0( . rst(rst), . yc422_pclk_i(video_clk_148m5), . yc422_data_i({yc_c,yc_y}), . yc422_de_i(yc_de), . yc422_vs_i(yc_vs), . yc422_hs_i(yc_hs), //-------------------------------------------------------- //视频时序参数,由CPU 通过i2c配置得到 . width_i(12'd1920), // l . height_i(12'd1080), // . hs_rising_to_de_i(12'd192), . hor_total_g_i(12'd2200), . vs_rising_to_de_i(12'd41), //L2 . ver_total_g_i(12'd1125), //L6 //---------------------------------------------------------- . video_pararm_enable_i(video_pararm_enable), //---------------------------------------------------------- //bt.1120接口 . embed_hs_vs_pclk_o(bt1120_clk), . embed_hs_vs_yc422_o(bt1120_yc) );