友晶科技的DE10-Nano 开发板和 C5G 开发板上都配备ADV7513芯片,这两个板子都能支持最高1080P高清HDMI 显示接口。
 
关于ADV7513芯片的控制实现分三个.v 文件:

I2C_HDMI_Config.v解析:

系统时钟是50MHz,但是I2C 的时钟不能太高,从ADV7513手册上看, 最大不能超过400KHz:
 
 
 
 
所以代码里面设计了一个分频电路,设置I2C频率是20K:
 
 
I2C_HDMI_Config.v状态机框图:
 
 
关于状态图里面的 mI2C_ACK 信号, 可以参考下面 I2C_WRITE_WDATA.v 的时序图, 正好每27bit传输完ACK信号就是0了。
 
关于ADV7513的寄存器配置, 代码里面主要配置了31个寄存器,其他的没有配置的就表示使用的是默认的配置,具体可以参考ADV7513芯片控制解读(中文版)--基于DE10-Nano开发板--C5G 开发板
 

I2C_HDMI_Config.v代码:

 

module I2C_HDMI_Config (    //    Host Side
                    iCLK,
                    iRST_N,
                    //    I2C Side
                    I2C_SCLK,
                    I2C_SDAT,
                    HDMI_TX_INT,
                    READY
                     );
//    Host Side
input                iCLK;
input                iRST_N;
//    I2C Side
output            I2C_SCLK;
inout                I2C_SDAT;
input                HDMI_TX_INT;
output READY ; 

//    Internal Registers/Wires
reg    [15:0]    mI2C_CLK_DIV;
reg    [23:0]    mI2C_DATA;
reg                mI2C_CTRL_CLK;
reg                mI2C_GO;
wire                mI2C_END;
wire                mI2C_ACK;
reg    [15:0]    LUT_DATA;
reg    [5:0]        LUT_INDEX;
reg    [3:0]        mSetup_ST;
reg READY ; 

//    Clock Setting
parameter    CLK_Freq    =    50000000;    //    50    MHz
parameter    I2C_Freq    =    20000;        //    20    KHz
//    LUT Data Number
parameter    LUT_SIZE    =    31;

/////////////////////    I2C Control Clock    ////////////////////////
always@(posedge iCLK or negedge iRST_N)
begin
    if(!iRST_N)
    begin
        mI2C_CTRL_CLK    <=    0;
        mI2C_CLK_DIV    <=    0;
    end
    else
    begin
        if( mI2C_CLK_DIV    < (CLK_Freq/I2C_Freq) )
            mI2C_CLK_DIV    <=    mI2C_CLK_DIV+1;
        else
        begin
            mI2C_CLK_DIV    <=    0;
            mI2C_CTRL_CLK    <=    ~mI2C_CTRL_CLK;
        end
    end
end
////////////////////////////////////////////////////////////////////
I2C_Controller     u0    (    .CLOCK(mI2C_CTRL_CLK),    //    Controller Work Clock
                        .I2C_SCLK(I2C_SCLK),                //    I2C CLOCK
                              .I2C_SDAT(I2C_SDAT),                //    I2C DATA
                        .I2C_DATA(mI2C_DATA),            //    DATA:[SLAVE_ADDR,SUB_ADDR,DATA]
                        .GO(mI2C_GO),                        //    GO transfor
                        .END(mI2C_END),                    //    END transfor 
                        .ACK(mI2C_ACK),                    //    ACK
                        .RESET(iRST_N)    );
////////////////////////////////////////////////////////////////////
//////////////////////    Config Control    ////////////////////////////
always@(posedge mI2C_CTRL_CLK or negedge iRST_N)
begin
    if(!iRST_N)
    begin
    READY<=0;
        LUT_INDEX    <=    0;
        mSetup_ST    <=    0;
        mI2C_GO        <=    0;
    end
    else
    begin
        if(LUT_INDEX<LUT_SIZE)
        begin
        READY<=0;
            case(mSetup_ST)
            0:    begin
                    mI2C_DATA    <=    {8'h72,LUT_DATA};
                    mI2C_GO        <=    1;
                    mSetup_ST    <=    1;
                end
            1:    begin
                    if(mI2C_END)
                    begin
                        if(!mI2C_ACK)
                        mSetup_ST    <=    2;
                        else
                        mSetup_ST    <=    0;                            
                        mI2C_GO        <=    0;
                    end
                end
            2:    begin
                    LUT_INDEX    <=    LUT_INDEX+1;
                    mSetup_ST    <=    0;
                end
            endcase
        end
        else
        begin
          READY<=1; 
          if(!HDMI_TX_INT)
          begin
            LUT_INDEX <= 0;
          end
          else
            LUT_INDEX <= LUT_INDEX;
        end
    end
end
////////////////////////////////////////////////////////////////////
/////////////////////    Config Data LUT      //////////////////////////    
always
begin
    case(LUT_INDEX)
    
    //    Video Config Data 每一个16位值的前8bit是寄存器的地址,后面的8bit是要填入寄存器的值。这里设定的只是我们用到的,其他的没有设定的直接用default值即可。

    0    :    LUT_DATA    <=    16'h9803;  //Must be set to 0x03 for proper operation
    1    :    LUT_DATA    <=    16'h0100;  //Set 'N' value at 6144
    2    :    LUT_DATA    <=    16'h0218;  //Set 'N' value at 6144
    3    :    LUT_DATA    <=    16'h0300;  //Set 'N' value at 6144
    4    :    LUT_DATA    <=    16'h1470;  // Set Ch count in the channel status to 8.
    5    :    LUT_DATA    <=    16'h1520;  //Input 444 (RGB or YCrCb) with Separate Syncs, 48kHz fs
    6    :    LUT_DATA    <=    16'h1630;  //Output format 444, 24-bit input
    7    :    LUT_DATA    <=    16'h1846;  //Disable CSC
    8    :    LUT_DATA    <=    16'h4080;  //General control packet enable
    9    :    LUT_DATA    <=    16'h4110;  //Power down control
    10    :    LUT_DATA    <=    16'h49A8;  //Set dither mode - 12-to-10 bit
    11    :    LUT_DATA    <=    16'h5510;  //Set RGB in AVI infoframe
    12    :    LUT_DATA    <=    16'h5608;  //Set active format aspect
    13    :    LUT_DATA    <=    16'h96F6;  //Set interrup
    14    :    LUT_DATA    <=    16'h7307;  //Info frame Ch count to 8
    15    :    LUT_DATA    <=    16'h761f;  //Set speaker allocation for 8 channels
    16    :    LUT_DATA    <=    16'h9803;  //Must be set to 0x03 for proper operation
    17    :    LUT_DATA    <=    16'h9902;  //Must be set to Default Value
    18    :    LUT_DATA    <=    16'h9ae0;  //Must be set to 0b1110000
    19    :    LUT_DATA    <=    16'h9c30;  //PLL filter R1 value
    20    :    LUT_DATA    <=    16'h9d61;  //Set clock divide
    21    :    LUT_DATA    <=    16'ha2a4;  //Must be set to 0xA4 for proper operation
    22    :    LUT_DATA    <=    16'ha3a4;  //Must be set to 0xA4 for proper operation
    23    :    LUT_DATA    <=    16'ha504;  //Must be set to Default Value
    24    :    LUT_DATA    <=    16'hab40;  //Must be set to Default Value
    25    :    LUT_DATA    <=    16'haf16;  //Select HDMI mode
    26    :    LUT_DATA    <=    16'hba60;  //No clock delay
    27    :    LUT_DATA    <=    16'hd1ff;  //Must be set to Default Value
    28    :    LUT_DATA    <=    16'hde10;  //Must be set to Default for proper operation
    29    :    LUT_DATA    <=    16'he460;  //Must be set to Default Value
    30    :    LUT_DATA    <=    16'hfa7d;  //Nbr of times to look for good phase

    default:        LUT_DATA    <=    16'h9803;
    endcase
end
////////////////////////////////////////////////////////////////////
endmodule

 

 另外说明下HDMI_TX_INT信号:低电平有效。ADV7513_Hardware_User's_Guide_R0.pdf的第17页:
 
 
DE10-Nano 的ADV7513电路
 

I2C_Controller.v解析:

例化了一个I2C_WRITE_WDATA.v模块,将I2C_WRITE_WDATA.v模块的SDAI 和SDAO 之间增加了一个三态缓冲电路,具体原理可参考博客:
 
 I2C_Controller .v代码:
 
 
module I2C_Controller (
    input  CLOCK,
    input  [23:0]I2C_DATA,    
    input  GO,
    input  RESET,    
    input  W_R,
     inout  I2C_SDAT,    
    output I2C_SCLK,
    output END,    
    output ACK
);

wire SDAO ; 

assign I2C_SDAT = SDAO?1'bz :0  ; 

I2C_WRITE_WDATA  wrd(
   .RESET_N  ( RESET),
    .PT_CK    ( CLOCK),
    .GO       ( GO   ),
    .END_OK   ( END  ),
    .ACK_OK   ( ACK  ),
    .BYTE_NUM ( 2    ),  //2byte    
    .SDAI     ( I2C_SDAT ),//IN
    .SDAO     ( SDAO     ),//OUT
    .SCLO     ( I2C_SCLK ),    
    .SLAVE_ADDRESS( I2C_DATA[23:16] ),
    .REG_DATA     ( I2C_DATA[15:0]  )    
);    
    
    

endmodule
 

I2C_WRITE_WDATA.v解析:

实现I2C 的写时序。根据I2C协议:
  • I2C 的开始条件是 SCLK保持高电平时,SDA电平有一个由高到低的变化。
  • 接着开始传输地址和数据。
  • I2C的结束条件是 SCLK保持高电平时,SDA电平有一个由低到高的变化。
I2C写时序:
 
 
状态机框图
 
 
时序框图
 
I2C_WRITE_WDATA.v 代码:
 
module I2C_WRITE_WDATA  (
   input                  RESET_N ,                //复位信号,低有效
    input                    PT_CK,                    //输入时钟
    input                    GO,                        //I2C起始标志,1表示起始,0表示停止
    input      [15:0] REG_DATA,                //REG_DATA[15:8] 是register number,REG_DATA[7:0]是data
    input      [7:0]     SLAVE_ADDRESS,            //I2C Slave address 8bit输入
    input                SDAI,                        //I2C data输入
    output reg           SDAO,                        //I2C data输出
    output reg           SCLO,                        //I2C的clock (scl)輸出
    output reg           END_OK,                    //I2C 传输结束通知, 1表示结束,0 表示还在传输
    
    //--for test 
    output reg [7:0]     ST ,                        //状态机的状态值
    output reg [7:0]     CNT,                        //计数传输了数据第几bit
    output reg [7:0]     BYTE,                        //要输入到寄存器的 数据 是在第 1~2 byte
    output reg           ACK_OK,                    //I2C的ack回应,0表示有ack,1表示没有ack
   input      [7:0]  BYTE_NUM  // 4 : 4 byte     輸入設定要寫入register是幾byte的資料 ,”1”表 寫入1byte資料 ,”2”表寫入2byte 資料
);

//===reg/wire  
reg   [8:0]A ;

always @( negedge RESET_N or posedge  PT_CK )begin
if (!RESET_N  ) ST <=0;
else 
      case (ST)
        0: begin  //start               
              SDAO   <=1; 
             SCLO   <=1;
             ACK_OK <=0;
             CNT    <=0;
             END_OK <=1;
             BYTE   <=0;    
             if (GO) ST  <=30 ; // inital                             
            end      
        1: begin  //start 
              ST <=2 ; 
               { SDAO,  SCLO } <= 2'b01; 
                A <= {SLAVE_ADDRESS ,1'b1 };//WRITE COMMAND
            end
        2: begin  //start 
              ST <=3 ; 
               { SDAO,  SCLO } <= 2'b00; 
            end
             
        3: begin  
              ST <=4 ; 
               { SDAO, A } <= { A ,1'b0 }; 
            end
        4: begin  
              ST <=5 ; 
               SCLO <= 1'b1 ; 
                CNT <= CNT +1 ;
            end
             
        5: begin  
               SCLO <= 1'b0 ; 
               if (CNT==9) begin
                     if ( BYTE == BYTE_NUM )  ST <= 6 ; 
                      else  begin 
                               CNT <=0 ; 
                               ST <= 2 ;
                                    if ( BYTE ==0 ) begin BYTE <=1  ; A <= {REG_DATA[15:8] ,1'b1 }; end 
                               else if ( BYTE ==1 ) begin BYTE <=2  ; A <= {REG_DATA[7:0] ,1'b1 }; end 
                              end
                      if (SDAI ) ACK_OK <=1 ; 
                 end
                 else ST <= 2;
            end

        6: begin          //stop
              ST <=7 ; 
               { SDAO,  SCLO } <= 2'b00; 
         end

        7: begin          //stop
              ST <=8 ; 
               { SDAO,  SCLO } <= 2'b01; 
         end
        8: begin          //stop
              ST <=9 ; 
               { SDAO,  SCLO } <= 2'b11; 
                        
         end 
        9:    begin
              ST     <= 30; 
                SDAO   <=1; 
             SCLO   <=1;
             CNT    <=0;
             END_OK <=1;
             BYTE   <=0;
             end
        //--- END ---
           30: begin
            if (!GO) ST  <=31;
          end            
           31: begin  //
              END_OK<=0;
                ACK_OK<=0;
                ST    <=1;    
            end    
      endcase 
 end
 
endmodule

 

 

 

 

相关阅读:
友晶科技FPGA开发板DE10-Nano的HDMI彩条实验(一)——HDMI简介
友晶科技FPGA开发板DE10-Nano的HDMI彩条实验(二)——TMDS算法

友晶科技FPGA开发板DE10-Nano的HDMI彩条实验(三)——DE10-Nano 的HDMI电路简介

友晶科技FPGA开发板DE10-Nano的HDMI彩条实验(四)——实验源码及现象演示

ADV7513芯片控制解读(中文版)——基于DE10-NANO开发板——C5G 开发板

DE10-Nano 的HDMI显示彩条实验遇到显示器显示“超频”

友晶科技FPGA开发板DE10-NANO的HDMI 方块移动实验