FPGA实现BT.1120编码

mac2024-09-30  63

研究了两天的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)     );

最新回复(0)