视频时序与BT1120的关系 && FPGA实现BT.1120编码

CEA-861-D视频时序:




水平方向对比:





垂直方向对比:




BT.1120整体数据格式:


具体实现可参考我另外一篇文章《FPGA实现BT.1120编码》

仅作参考,modulesim仿真通过,未做时序优化

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

例化后就能使用了

```c 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)
);




这段代码看后,用了一个三段式状态机来实现行场时序的转换,相当清晰。其中, video_pararm_enable_i这个输入,代码中是用了|| 操作来对这个模块实现使能,如实现画面的黑屏,可以使用这个位,按道理来说,改为&&是好的。
  • 3
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
BT1120是一种FPGA调试技术,用于通过FPGA控制和分析外部视频接口信号。在进行BT1120FPGA调试时,需要以下步骤: 1. 确定调试目标:首先确定要调试的外部视频接口信号类型和问题。该接口通常用于传输高清视频信号,如720p或1080p。如果存在信号传输问题或其他异常,需要确定具体的调试目标。 2. 配置FPGA:将FPGA配置成基于BT1120视频接口信号处理器。这可以通过编程FPGA实现,编写适当的Verilog或VHDL代码,以实现接口信号的读取和处理。 3. 连接外部接口:将FPGA与外部视频接口连接起来。确保连接正确并稳定。 4. 信号分析和调试工具:使用适当的信号分析和调试工具,例如示波器,逻辑分析仪或者其他测试设备,来监测和分析BT1120接口的信号。这将帮助我们识别和解决可能存在的问题,例如信号损坏、时序问题等。 5. 调试过程中的观察:监测和记录外部接口信号的特征和变化。观察时序波形,检查信号的时钟、数据和同步等方面的正确性。根据观察到的问题,尝试利用FPGA的编程能力进行调整和优化。 6. 修改FPGA代码:根据观察结果和信号分析,适时修改FPGA代码,以修复可能存在的问题。这可能包括修改时序逻辑、优化信号处理算法或调整接口参数等。 7. 重复步骤:根据调试过程中的反馈和观测结果,多次重复上述过程,直到成功解决问题并满足调试目标。 总之,BT1120FPGA调试过程需要结合观察、信号分析和FPGA编程等技术,通过不断的优化和修改来解决问题,最终实现对外部视频接口信号的稳定和高质量传输。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值