RGB转HDMI方案、MS7210驱动——FPGA学习笔记20

news2024/10/11 16:10:01

一、简介

见HDMI彩条显示——FPGA学习笔记12-CSDN博客

二、TMDS编码原理

        HDMI 采用 TMDS (Time Minimized Differential Signal) 最小化传输差分信号传输技术, 是美国 Silicon Image 公司开发的一项高速数据传输技术, 将视频、 音频、 控制信号进行编码并串转换后发送。 TMDS 是一种微分信号机制,采用的是差分传动方式。 利用 2 个引脚间电压差来传送信号, 由两脚间电压正负极性和大小决定传送“0” 还是“1”。采用 2 根线来传输信号, 一根线上传输原来的信号, 另一根线上传输与原来信号相反的信号, 接收端就可以通过让一根线上的信号减去另一根线上的信号的方式来屏蔽电磁干扰, 从而得到正确的信号。

(1) 使用 channel0 的 D[1:0]传输 HSYNC, VSYNC, 占用 2bit, 控制信号被编码成 10 位传输, 00、 01、 10、 11 编码后分别是 10'b1101010100, 10'b0010101011, 10'b0101010100, 和 10'b1010101011。
( 2) Preamble 控制信息, 图中的 CTLx, 可用来表示后面传输的是 data island 还是 video data。 通过 channel1 和 2的 D[1:0]传输, 占用 4bit, 控制信号被编码成 10 位传输。

三、代码实现

`timescale 1ns / 1ps

module HDMI_top(
    input           I_sysclk        ,
    input           I_rst_n         ,
    output          O_hdmi_clk_p    ,
    output          O_hdmi_clk_n    ,
    output  [2:0]   O_hdmi_tx_p     ,
    output  [2:0]   O_hdmi_tx_n    
);

wire [7:0]  rgb_r   ;
wire [7:0]  rgb_g   ;
wire [7:0]  rgb_b   ;
wire        lcd_hs  ;
wire        lcd_vs  ;
wire        lcd_de  ;



//LCD驱动时钟
clk_wiz_0 u_clk_wiz_0
(
    .clk_75M    (clk_40M    )   ,     
    .clk_375M   (clk_200M   )   ,     
    .clk_in1    (I_sysclk   )
);  


video_lcd u_video_lcd(
    .I_vid_clk   (clk_40M   )   ,   //系统时钟
    .I_vid_rstn  (I_rst_n   )   ,   //系统复位输入
    .O_vid_hs    (lcd_hs    )   ,   //hs信号
    .O_vid_vs    (lcd_vs    )   ,   //vs信号
    .O_vid_de    (lcd_de    )   ,   //视频数据有效信号
    .O_rgb_r     (rgb_r     )   ,   // RGB-红
    .O_rgb_g     (rgb_g     )   ,   // RGB-绿
    .O_rgb_b     (rgb_b     )       // RGB-蓝
);

hdmitx#
(
    .FAMILY ("7FAMILY")			
)
u_hdmitx
(
.I_rstn             (I_rst_n            )   ,  //复位
.I_hs               (lcd_hs             )   ,  //hs信号
.I_vs               (lcd_vs             )   ,  //vs信号
.I_de               (lcd_de             )   ,  //de信号
.I_rgb              ({rgb_r,rgb_g,rgb_b})   ,  //RGB数据
.I_pclkx1           (clk_40M            )   ,  //像素时钟
.I_pclkx2_5         (1'b0               )   ,  //2.5倍像素时钟,只有UFAMILY需要
.I_pclkx5           (clk_200M           )   ,  //5倍像素时钟
.O_hdmi_tx_clk_p    (O_hdmi_clk_p       )   ,  //HDMI时钟输出P端
.O_hdmi_tx_clk_n    (O_hdmi_clk_n       )   ,  //HDMI时钟输出N端
.O_hdmi_tx_p        (O_hdmi_tx_p        )   ,  //HDMI输出数据P端
.O_hdmi_tx_n        (O_hdmi_tx_n        )      //HDMI输出数据N端
);

endmodule
module hdmitx#
(
parameter  FAMILY = "ULTRASCALE"			
)
(
    input I_rstn,
    input I_vs,
    input I_hs,
    input I_de,
    input [23:0] I_rgb,
    input I_pclkx1,
    input I_pclkx2_5,
    input I_pclkx5,
    output O_hdmi_tx_clk_p,
    output O_hdmi_tx_clk_n,
    output [2:0]O_hdmi_tx_p,
    output [2:0]O_hdmi_tx_n
);

wire [7:0] RED = I_rgb[23:16];
wire [7:0] GREEN = I_rgb[15:8];
wire [7:0] BLUE = I_rgb[7:0];  
wire [9:0] intTmdsRed;
wire [9:0] intTmdsGreen;
wire [9:0] intTmdsBlue;
    
wire intRst = !I_rstn;
    //----------------------------------------------------------------------------------
    //-- DVI Encoder; DVI 1.0 Specifications
    //-- This component encodes 24-bit RGB video frames with sync signals into 10-bit
    //-- TMDS characters.
    //----------------------------------------------------------------------------------
TMDSEncoder Inst_TMDSEncoder_red
     (
     .D_I(RED),
     .C0_I(1'b0),
     .C1_I(1'b0),
     .DE_I(I_de),
     .CLK_I(I_pclkx1),
     .D_O(intTmdsRed)
     );
TMDSEncoder Inst_TMDSEncoder_green
     (
     .D_I(GREEN),
     .C0_I(1'b0),
     .C1_I(1'b0),
     .DE_I(I_de),
     .CLK_I(I_pclkx1),
     .D_O(intTmdsGreen)
     );
TMDSEncoder Inst_TMDSEncoder_blue(
     .D_I(BLUE),
     .C0_I(I_hs),
     .C1_I(I_vs),
     .DE_I(I_de),
     .CLK_I(I_pclkx1),
     .D_O(intTmdsBlue)
    );
//----------------------------------------------------------------------------------
//-- TMDS serializer; ratio of 10:1; 3 data & 1 clock channel
// -- Since the TMDS clock's period is character-long (10-bit periods), the
// -- serialization of "1111100000" will result in a 10-bit long clock period.
//----------------------------------------------------------------------------------
generate  if(FAMILY == "ULTRASCALE" || FAMILY == "ULTRASCALE_PLUS")begin : ULTRASCALE_FAMILY 

    oserdese3_10to1 #
    (
        .FAMILY(FAMILY)
    )
    Inst_clk_oserdese3_10to1
    (
        .txdata("1111100000"),
        .txrst(intRst),
        .pclk(I_pclkx1),
        .clkdiv2(I_pclkx5),
        .clkdiv4(I_pclkx2_5),
        .tx_p(O_hdmi_tx_clk_p),
        .tx_n(O_hdmi_tx_clk_n)
    );

    oserdese3_10to1#
    (
        .FAMILY(FAMILY)
    )
    Inst_d2_serializer_10_1
    (
        .txdata(intTmdsRed),
        .txrst(intRst),
        .pclk(I_pclkx1),
        .clkdiv2(I_pclkx5),
        .clkdiv4(I_pclkx2_5),
        .tx_p(O_hdmi_tx_p[2]),
        .tx_n(O_hdmi_tx_n[2])
    );

    oserdese3_10to1#
    (
        .FAMILY(FAMILY)
    )
    Inst_d1_serializer_10_1
    (
        .txdata(intTmdsGreen),
        .txrst(intRst),
        .pclk(I_pclkx1),
        .clkdiv2(I_pclkx5),
        .clkdiv4(I_pclkx2_5),
        .tx_p(O_hdmi_tx_p[1]),
        .tx_n(O_hdmi_tx_n[1])
    );

    oserdese3_10to1#
    (
        .FAMILY(FAMILY)
    )
    Inst_d0_serializer_10_1
    (
        .txdata(intTmdsBlue),
        .txrst(intRst),
        .pclk(I_pclkx1),
        .clkdiv2(I_pclkx5),
        .clkdiv4(I_pclkx2_5),
        .tx_p(O_hdmi_tx_p[0]),
        .tx_n(O_hdmi_tx_n[0])
    );
end

else if(FAMILY == "7FAMILY")begin : family_7 

    oserdese2_10to1 Inst_clk_oserdese2_10to1
    (
        .txdata("1111100000"),
        .txrst(intRst),
        .pclk(I_pclkx1),
        .clkdiv2(I_pclkx5),
        .tx_p(O_hdmi_tx_clk_p),
        .tx_n(O_hdmi_tx_clk_n)
    ); 

    oserdese2_10to1 Inst_d2_serializer_10_1
    (
        .txdata(intTmdsRed),
        .txrst(intRst),
        .pclk(I_pclkx1),
        .clkdiv2(I_pclkx5),
        .tx_p(O_hdmi_tx_p[2]),
        .tx_n(O_hdmi_tx_n[2])
    );

    oserdese2_10to1 Inst_d1_serializer_10_1
    (
        .txdata(intTmdsGreen),
        .txrst(intRst),
        .pclk(I_pclkx1),
        .clkdiv2(I_pclkx5),
        .tx_p(O_hdmi_tx_p[1]),
        .tx_n(O_hdmi_tx_n[1])
    );

    oserdese2_10to1 Inst_d0_serializer_10_1
    (
        .txdata(intTmdsBlue),
        .txrst(intRst),
        .pclk(I_pclkx1),
        .clkdiv2(I_pclkx5),
        .tx_p(O_hdmi_tx_p[0]),
        .tx_n(O_hdmi_tx_n[0])
    );
    end
endgenerate
endmodule

四、上板验证

五、MS7210驱动方案

`timescale 1ns / 1ps

module HDMI_top(
    input           sys_clk_p   ,
    input           sys_clk_n   ,
    input           I_rst_n     ,
    
    output          lcd_clk     ,
    output          lcd_hs      ,
    output          lcd_vs      ,
    // output          lcd_bl      ,
    output          lcd_de      ,
    output          lcd_rst     ,
    inout  [23:0]   lcd_rgb     ,
    output          iic_scl     ,
    inout           iic_sda     //
);

//转换差分信号
IBUFDS diff_clock (
    .O          (I_sysclk   )   ,   //输出系统时钟
    .I          (sys_clk_p  )   ,   //系统差分输入时钟P端
    .IB         (sys_clk_n  )       //系统差分输入时钟N端
);

clk_wiz_0 u_clk_wiz_0
(
    .clk_out1   (clk_10M    )   ,     
    .clk_out2   (clk_40M    )   ,     
    .locked     (locked     )   ,      
    .reset      (~I_rst_n   )   ,

    .clk_in1    (I_sysclk   )  
);

assign  rst_n =  I_rst_n & locked;

// wire    rstn_out    ;

LCD u_LCD(
    .I_lcd_clk   (clk_40M   )   ,
    .I_rst_n     (rst_n     )   ,
    .lcd_clk     (lcd_clk   )   ,
    .lcd_hs      (lcd_hs    )   ,
    .lcd_vs      (lcd_vs    )   ,
    .lcd_bl      (lcd_bl    )   ,
    .lcd_de      (lcd_de    )   ,
    .lcd_rst     (   )   ,
    .lcd_rgb     (lcd_rgb   )      
);

ms72xx_ctl u_ms72xx_ctl(
    .clk         (clk_10M   )   ,   
    .rst_n       (rst_n     )   ,   
    .rstn_out    (lcd_rst   )   ,   //芯片复位信号,低有效
    .int_over    (init_over )   ,   //配置全部完成标志
    .iic_scl     (iic_scl   )   ,      
    .iic_sda     (iic_sda   )                 
);

endmodule
module ms72xx_ctl(
    input       clk,
    input       rst_n,
    
    output      rstn_out,    //芯片复位信号,低有效
    output      int_over,   //配置全部完成标志
    output      iic_scl ,   
    inout       iic_sda         //
);


//parameter define
parameter SLAVE_ADDR = 7'h2b          ; //器件地址
parameter BIT_CTRL   = 1'b1           ; //字节地址为16位  0:8位 1:16位
parameter CLK_FREQ   = 27'd10_000_000 ; //i2c_dri模块的驱动时钟频率 
parameter I2C_FREQ   = 18'd250_000    ; //I2C的SCL时钟频率,不超过400KHz

//reg define


//wire define
wire        i2c_exec       ;  //I2C触发执行信号
wire [23:0] i2c_data       ;  //I2C要配置的地址与数据(高16位地址,低8位数据)          
wire        i2c_done       ;  //I2C寄存器配置完成信号
wire        i2c_dri_clk    ;  //I2C操作时钟
wire [ 7:0] i2c_data_r     ;  //I2C读出的数据
wire        i2c_rh_wl      ;  //I2C读写控制信号

//*****************************************************
//**                    main code
//*****************************************************


//I2C配置模块
i2c_ms7210_cfg u_i2c_ms7210_cfg(
    .clk                (i2c_dri_clk),
    .rst_n              (rst_n),
            
    .i2c_exec           (i2c_exec),
    .i2c_data           (i2c_data),
    .i2c_rh_wl          (i2c_rh_wl),        //I2C读写控制信号
    .i2c_done           (i2c_done), 
    .i2c_data_r         (i2c_data_r),   
    .rstn_out           (rstn_out),       
    .init_done          (init_over)         //配置全部完成标志
    );    

//I2C驱动模块
i2c_dri #(
    .SLAVE_ADDR         (SLAVE_ADDR),       //参数传递
    .CLK_FREQ           (CLK_FREQ  ),              
    .I2C_FREQ           (I2C_FREQ  ) 
    )
u_i2c_dri(
    .clk                (clk),
    .rst_n              (rst_n     ),

    .i2c_exec           (i2c_exec  ),   
    .bit_ctrl           (BIT_CTRL ),   
    .i2c_rh_wl          (i2c_rh_wl),        //固定为0,只用到了IIC驱动的写操作   
    .i2c_addr           ({i2c_data[15:8],i2c_data[23:16]}),   
    .i2c_data_w         (i2c_data[7:0]),   
    .i2c_data_r         (i2c_data_r),   
    .i2c_done           (i2c_done  ),
    
    .scl                (iic_scl   ),   
    .sda                (iic_sda   ),   

    .dri_clk            (i2c_dri_clk)       //I2C操作时钟
    );	

    
endmodule
module i2c_ms7210_cfg
   (  
    input                clk      ,     //时钟信号
    input                rst_n    ,     //复位信号,低电平有效
    
    input        [7:0]   i2c_data_r,    //I2C读出的数据
    input                i2c_done ,     //I2C寄存器配置完成信号
    output  reg          i2c_exec ,     //I2C触发执行信号   
    output  reg  [23:0]  i2c_data ,     //I2C要配置的地址与数据(高16位地址,低8位数据)
    output  reg          i2c_rh_wl,     //I2C读写控制信号
    output                rstn_out,     //芯片复位信号,低有效
    output  reg          init_done      //初始化完成信号
    );

//parameter define
localparam  REG_NUM = 8'd50  ;       //总共需要配置的寄存器个数

//reg define
reg    [12:0]   start_init_cnt;        //等待延时计数器
reg    [7:0]    init_reg_cnt  ;        //寄存器配置个数计数器
reg    [13:0]   cfg_delay_cnt  ;       //寄存器配置之间延迟计数器
reg    [15:0]   rstn_1ms  = 16'd0     ;

//*****************************************************
//**                    main code
//*****************************************************

assign rstn_out = (rstn_1ms == 16'd1000) && rst_n;

//产生芯片复位信号的延迟计数器
always @(posedge clk)begin
    if(!rst_n)
    	 rstn_1ms <= 16'd0;
    else begin
    	if(rstn_1ms == 16'd1000)
    		rstn_1ms <= rstn_1ms;
        else
    		rstn_1ms <= rstn_1ms + 1'b1;
    end
end

//clk时钟配置成1MHZ,周期为1us 5000*1us = 5ms
//上电到开始配置IIC等待5ms
always @(posedge clk or negedge rstn_out) begin
    if(!rstn_out)
        start_init_cnt <= 13'b0;
    else if(start_init_cnt < 13'd5000) begin
        start_init_cnt <= start_init_cnt + 1'b1;                    
    end
end

//每个寄存器配置之间延迟20us    
always @(posedge clk or negedge rstn_out) begin
    if(!rstn_out)
        cfg_delay_cnt <= 8'd0;
    else if(i2c_done)   
        cfg_delay_cnt <= 8'd0;
	else if(cfg_delay_cnt >6000)
        cfg_delay_cnt <= cfg_delay_cnt ;		
	else
        cfg_delay_cnt <= cfg_delay_cnt + 8'b1;	
		
end

//寄存器配置个数计数    
always @(posedge clk or negedge rstn_out) begin
    if(!rstn_out)
        init_reg_cnt <= 8'd0;
    else if(i2c_exec)   
        init_reg_cnt <= init_reg_cnt + 8'b1;
end

//i2c触发执行信号   
always @(posedge clk or negedge rstn_out) begin
    if(!rstn_out)
        i2c_exec <= 1'b0;
    else if(start_init_cnt == 13'd4999)
        i2c_exec <= 1'b1;
    else if(i2c_done && (init_reg_cnt < REG_NUM) && (init_reg_cnt != 21))
        i2c_exec <= 1'b1;
    else if((cfg_delay_cnt == 5999) && (init_reg_cnt == 21))
        i2c_exec <= 1'b1;		
    else
        i2c_exec <= 1'b0;
end 

//配置I2C读写控制信号
always @(posedge clk or negedge rstn_out) begin
    if(!rstn_out)
        i2c_rh_wl <= 1'b1;
    else 
        i2c_rh_wl <= 1'b0;  
end

//初始化完成信号
always @(posedge clk or negedge rstn_out) begin
    if(!rstn_out)
        init_done <= 1'b0;
    else if((init_reg_cnt == REG_NUM) && i2c_done)  
        init_done <= 1'b1;  
end

//配置寄存器地址与数据
always @(posedge clk or negedge rstn_out) begin
    if(!rstn_out)
        i2c_data <= 24'b0;
    else begin
        case(init_reg_cnt)
            8'd0  : i2c_data <= {16'h0003,8'h5a}; 
            8'd1  : i2c_data <= {16'h1281,8'h04}; 
            8'd2  : i2c_data <= {16'h0016,8'h04}; 
            8'd3  : i2c_data <= {16'h0009,8'h01}; 
            8'd4  : i2c_data <= {16'h0007,8'h09}; 
            8'd5  : i2c_data <= {16'h0008,8'hF0};
            8'd6  : i2c_data <= {16'h000A,8'hF0};
            8'd7  : i2c_data <= {16'h0006,8'h11}; 
            8'd8  : i2c_data <= {16'h0531,8'h84}; 
            8'd9  : i2c_data <= {16'h0900,8'h20};
            8'd10 : i2c_data <= {16'h0901,8'h47};
            8'd11 : i2c_data <= {16'h0904,8'h09};
            8'd12 : i2c_data <= {16'h0923,8'h07};
            8'd13 : i2c_data <= {16'h0924,8'h44};
            8'd14 : i2c_data <= {16'h0925,8'h44};
            8'd15 : i2c_data <= {16'h090F,8'h80};
            8'd16 : i2c_data <= {16'h091F,8'h07};
            8'd17 : i2c_data <= {16'h0920,8'h1E};
            8'd18 : i2c_data <= {16'h0018,8'h20};
            8'd19 : i2c_data <= {16'h05c0,8'hFE};
            8'd20 : i2c_data <= {16'h000B,8'h00};
            8'd21 : i2c_data <= {16'h0507,8'h06};
            8'd22 : i2c_data <= {16'h0906,8'h04};
            8'd23 : i2c_data <= {16'h0920,8'h5E};
            8'd24 : i2c_data <= {16'h0926,8'hDD}; 
            8'd25 : i2c_data <= {16'h0927,8'h0D}; 
            8'd26 : i2c_data <= {16'h0928,8'h88}; 
            8'd27 : i2c_data <= {16'h0929,8'h08};
            8'd28 : i2c_data <= {16'h0910,8'h01};
            8'd29 : i2c_data <= {16'h000B,8'h11};
            8'd30 : i2c_data <= {16'h050E,8'h00}; 
            8'd31 : i2c_data <= {16'h050A,8'h82}; 
            8'd32 : i2c_data <= {16'h0509,8'h02}; 
            8'd33 : i2c_data <= {16'h050B,8'h0D};
            8'd34 : i2c_data <= {16'h050D,8'h06};
            8'd35 : i2c_data <= {16'h050D,8'h11};
            8'd36 : i2c_data <= {16'h050D,8'h58};
            8'd37 : i2c_data <= {16'h050D,8'h00};
            8'd38 : i2c_data <= {16'h050D,8'h00};
            8'd39 : i2c_data <= {16'h050D,8'h00};
            8'd40 : i2c_data <= {16'h050D,8'h00}; 
            8'd41 : i2c_data <= {16'h050D,8'h00}; 
            8'd42 : i2c_data <= {16'h050D,8'h00}; 
            8'd43 : i2c_data <= {16'h050D,8'h00}; 
            8'd44 : i2c_data <= {16'h050D,8'h00}; 
            8'd45 : i2c_data <= {16'h050D,8'h00}; 
            8'd46 : i2c_data <= {16'h050D,8'h00}; 
            8'd47 : i2c_data <= {16'h050D,8'h00}; 
            8'd48 : i2c_data <= {16'h050E,8'h40}; 
            8'd49 : i2c_data <= {16'h0507,8'h00};
            default : i2c_data <= {16'h0003,8'h5a}; 
        endcase
    end
end

endmodule
module i2c_dri
    #(
      parameter   SLAVE_ADDR = 7'h2b        ,  //器件地址
      parameter   CLK_FREQ   = 26'd10_000_000, //模块输入的时钟频率
      parameter   I2C_FREQ   = 18'd250_000     //IIC_SCL的时钟频率
    )
   (                                                            
    input                clk        ,    
    input                rst_n      ,   
                                         
    //i2c interface                      
    input                i2c_exec   ,  //I2C触发执行信号
    input                bit_ctrl   ,  //字地址位控制(16b/8b)
    input                i2c_rh_wl  ,  //I2C读写控制信号
    input        [15:0]  i2c_addr   ,  //I2C器件内地址
    input        [7:0]   i2c_data_w ,  //I2C要写的数据
    output  reg  [7:0]   i2c_data_r ,  //I2C读出的数据
    output  reg          i2c_done   ,  //I2C一次操作完成
    output  reg          i2c_ack    ,  //I2C应答标志 0:应答 1:未应答
    output  reg          scl        ,  //I2C的SCL时钟信号
    inout                sda        ,  //I2C的SDA信号
//    input                 sda_in,
//    output                sda_out,
//    output                sda_dir,    


                                       
    //user interface                   
    output  reg          dri_clk       //驱动I2C操作的驱动时钟
     );

//localparam define
localparam  st_idle     = 8'b0000_0001; //空闲状态
localparam  st_sladdr   = 8'b0000_0010; //发送器件地址(slave address)
localparam  st_addr16   = 8'b0000_0100; //发送16位字地址
localparam  st_addr8    = 8'b0000_1000; //发送8位字地址
localparam  st_data_wr  = 8'b0001_0000; //写数据(8 bit)
localparam  st_addr_rd  = 8'b0010_0000; //发送器件地址读
localparam  st_data_rd  = 8'b0100_0000; //读数据(8 bit)
localparam  st_stop     = 8'b1000_0000; //结束I2C操作

//reg define
reg            sda_dir   ; //I2C数据(SDA)方向控制
reg            sda_out   ; //SDA输出信号
reg            st_done   ; //状态结束
reg            wr_flag   ; //写标志
reg    [ 6:0]  cnt       ; //计数
reg    [ 7:0]  cur_state ; //状态机当前状态
reg    [ 7:0]  next_state; //状态机下一状态
reg    [15:0]  addr_t    ; //地址
reg    [ 7:0]  data_r    ; //读取的数据
reg    [ 7:0]  data_wr_t ; //I2C需写的数据的临时寄存
reg    [ 9:0]  clk_cnt   ; //分频时钟计数

//wire define
wire          sda_in     ; //SDA输入信号
wire   [8:0]  clk_divide ; //模块驱动时钟的分频系数

//*****************************************************
//**                    main code
//*****************************************************

//SDA控制
assign  sda     = sda_dir ?  sda_out : 1'bz;     //SDA数据输出或高阻
assign  sda_in  = sda ;                          //SDA数据输入
assign  clk_divide = (CLK_FREQ/I2C_FREQ) >> 2'd2;//模块驱动时钟的分频系数

//生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        dri_clk <=  1'b0;
        clk_cnt <= 10'd0;
    end
    else if(clk_cnt == clk_divide[8:1] - 1'd1) begin
        clk_cnt <= 10'd0;
        dri_clk <= ~dri_clk;
    end
    else
        clk_cnt <= clk_cnt + 1'b1;
end

//(三段式状态机)同步时序描述状态转移
always @(posedge dri_clk or negedge rst_n) begin
    if(!rst_n)
        cur_state <= st_idle;
    else
        cur_state <= next_state;
end

//组合逻辑判断状态转移条件
always @(*) begin
    next_state = st_idle;
    case(cur_state)
        st_idle: begin                          //空闲状态
           if(i2c_exec) begin
               next_state = st_sladdr;
           end
           else
               next_state = st_idle;
        end
        st_sladdr: begin
            if(st_done) begin
                if(bit_ctrl)                    //判断是16位还是8位字地址
                   next_state = st_addr16;
                else
                   next_state = st_addr8 ;
            end
            else
                next_state = st_sladdr;
        end
        st_addr16: begin                        //写16位字地址
            if(st_done) begin
                next_state = st_addr8;
            end
            else begin
                next_state = st_addr16;
            end
        end
        st_addr8: begin                         //8位字地址
            if(st_done) begin
                if(wr_flag==1'b0)               //读写判断
                    next_state = st_data_wr;
                else
                    next_state = st_addr_rd;
            end
            else begin
                next_state = st_addr8;
            end
        end
        st_data_wr: begin                       //写数据(8 bit)
            if(st_done)
                next_state = st_stop;
            else
                next_state = st_data_wr;
        end
        st_addr_rd: begin                       //写地址以进行读数据
            if(st_done) begin
                next_state = st_data_rd;
            end
            else begin
                next_state = st_addr_rd;
            end
        end
        st_data_rd: begin                       //读取数据(8 bit)
            if(st_done)
                next_state = st_stop;
            else
                next_state = st_data_rd;
        end
        st_stop: begin                          //结束I2C操作
            if(st_done)
                next_state = st_idle;
            else
                next_state = st_stop ;
        end
        default: next_state= st_idle;
    endcase
end

//时序电路描述状态输出
always @(posedge dri_clk or negedge rst_n) begin
    //复位初始化
    if(!rst_n) begin
        scl       <= 1'b1;
        sda_out   <= 1'b1;
        sda_dir   <= 1'b1;                          
        i2c_done  <= 1'b0;                          
        i2c_ack   <= 1'b0;                          
        cnt       <= 1'b0;                          
        st_done   <= 1'b0;                          
        data_r    <= 1'b0;                          
        i2c_data_r<= 1'b0;                          
        wr_flag   <= 1'b0;                          
        addr_t    <= 1'b0;                          
        data_wr_t <= 1'b0;                          
    end                                              
    else begin                                       
        st_done <= 1'b0 ;                            
        cnt     <= cnt +1'b1 ;                       
        case(cur_state)                              
             st_idle: begin                          //空闲状态
                scl     <= 1'b1;                     
                sda_out <= 1'b1;                     
                sda_dir <= 1'b1;                     
                i2c_done<= 1'b0;                     
                cnt     <= 7'b0;               
                if(i2c_exec) begin                   
                    wr_flag   <= i2c_rh_wl ;         
                    addr_t    <= i2c_addr  ;         
                    data_wr_t <= i2c_data_w;  
                    i2c_ack <= 1'b0;                      
                end                                  
            end                                      
            st_sladdr: begin                         //写地址(器件地址和字地址)
                case(cnt)                            
                    7'd1 : sda_out <= 1'b0;          //开始I2C
                    7'd3 : scl <= 1'b0;              
                    7'd4 : sda_out <= SLAVE_ADDR[6]; //传送器件地址
                    7'd5 : scl <= 1'b1;              
                    7'd7 : scl <= 1'b0;              
                    7'd8 : sda_out <= SLAVE_ADDR[5]; 
                    7'd9 : scl <= 1'b1;              
                    7'd11: scl <= 1'b0;              
                    7'd12: sda_out <= SLAVE_ADDR[4]; 
                    7'd13: scl <= 1'b1;              
                    7'd15: scl <= 1'b0;              
                    7'd16: sda_out <= SLAVE_ADDR[3]; 
                    7'd17: scl <= 1'b1;              
                    7'd19: scl <= 1'b0;              
                    7'd20: sda_out <= SLAVE_ADDR[2]; 
                    7'd21: scl <= 1'b1;              
                    7'd23: scl <= 1'b0;              
                    7'd24: sda_out <= SLAVE_ADDR[1]; 
                    7'd25: scl <= 1'b1;              
                    7'd27: scl <= 1'b0;              
                    7'd28: sda_out <= SLAVE_ADDR[0]; 
                    7'd29: scl <= 1'b1;              
                    7'd31: scl <= 1'b0;              
                    7'd32: sda_out <= 1'b0;          //0:写
                    7'd33: scl <= 1'b1;              
                    7'd35: scl <= 1'b0;              
                    7'd36: begin                     
                        sda_dir <= 1'b0;             
                        sda_out <= 1'b1;                         
                    end                              
                    7'd37: scl     <= 1'b1;            
                    7'd38: begin                     //从机应答 
                        st_done <= 1'b1;
                        if(sda_in == 1'b1)           //高电平表示未应答
                            i2c_ack <= 1'b1;         //拉高应答标志位     
                    end                                          
                    7'd39: begin                     
                        scl <= 1'b0;                 
                        cnt <= 1'b0;                 
                    end                              
                    default :  ;                     
                endcase                              
            end                                      
            st_addr16: begin                         
                case(cnt)                            
                    7'd0 : begin                     
                        sda_dir <= 1'b1 ;            
                        sda_out <= addr_t[15];       //传送字地址
                    end                              
                    7'd1 : scl <= 1'b1;              
                    7'd3 : scl <= 1'b0;              
                    7'd4 : sda_out <= addr_t[14];    
                    7'd5 : scl <= 1'b1;              
                    7'd7 : scl <= 1'b0;              
                    7'd8 : sda_out <= addr_t[13];    
                    7'd9 : scl <= 1'b1;              
                    7'd11: scl <= 1'b0;              
                    7'd12: sda_out <= addr_t[12];    
                    7'd13: scl <= 1'b1;              
                    7'd15: scl <= 1'b0;              
                    7'd16: sda_out <= addr_t[11];    
                    7'd17: scl <= 1'b1;              
                    7'd19: scl <= 1'b0;              
                    7'd20: sda_out <= addr_t[10];    
                    7'd21: scl <= 1'b1;              
                    7'd23: scl <= 1'b0;              
                    7'd24: sda_out <= addr_t[9];     
                    7'd25: scl <= 1'b1;              
                    7'd27: scl <= 1'b0;              
                    7'd28: sda_out <= addr_t[8];     
                    7'd29: scl <= 1'b1;              
                    7'd31: scl <= 1'b0;              
                    7'd32: begin                     
                        sda_dir <= 1'b0;             
                        sda_out <= 1'b1;   
                    end                              
                    7'd33: scl  <= 1'b1;             
                    7'd34: begin                     //从机应答
                        st_done <= 1'b1;     
                        if(sda_in == 1'b1)           //高电平表示未应答
                            i2c_ack <= 1'b1;         //拉高应答标志位    
                    end        
                    7'd35: begin                     
                        scl <= 1'b0;                 
                        cnt <= 1'b0;                 
                    end                              
                    default :  ;                     
                endcase                              
            end                                      
            st_addr8: begin                          
                case(cnt)                            
                    7'd0: begin                      
                       sda_dir <= 1'b1 ;             
                       sda_out <= addr_t[7];         //字地址
                    end                              
                    7'd1 : scl <= 1'b1;              
                    7'd3 : scl <= 1'b0;              
                    7'd4 : sda_out <= addr_t[6];     
                    7'd5 : scl <= 1'b1;              
                    7'd7 : scl <= 1'b0;              
                    7'd8 : sda_out <= addr_t[5];     
                    7'd9 : scl <= 1'b1;              
                    7'd11: scl <= 1'b0;              
                    7'd12: sda_out <= addr_t[4];     
                    7'd13: scl <= 1'b1;              
                    7'd15: scl <= 1'b0;              
                    7'd16: sda_out <= addr_t[3];     
                    7'd17: scl <= 1'b1;              
                    7'd19: scl <= 1'b0;              
                    7'd20: sda_out <= addr_t[2];     
                    7'd21: scl <= 1'b1;              
                    7'd23: scl <= 1'b0;              
                    7'd24: sda_out <= addr_t[1];     
                    7'd25: scl <= 1'b1;              
                    7'd27: scl <= 1'b0;              
                    7'd28: sda_out <= addr_t[0];     
                    7'd29: scl <= 1'b1;              
                    7'd31: scl <= 1'b0;              
                    7'd32: begin                     
                        sda_dir <= 1'b0;         
                        sda_out <= 1'b1;                    
                    end                              
                    7'd33: scl     <= 1'b1;          
                    7'd34: begin                     //从机应答
                        st_done <= 1'b1;     
                        if(sda_in == 1'b1)           //高电平表示未应答
                            i2c_ack <= 1'b1;         //拉高应答标志位    
                    end   
                    7'd35: begin                     
                        scl <= 1'b0;                 
                        cnt <= 1'b0;                 
                    end                              
                    default :  ;                     
                endcase                              
            end                                      
            st_data_wr: begin                        //写数据(8 bit)
                case(cnt)                            
                    7'd0: begin                      
                        sda_out <= data_wr_t[7];     //I2C写8位数据
                        sda_dir <= 1'b1;             
                    end                              
                    7'd1 : scl <= 1'b1;              
                    7'd3 : scl <= 1'b0;              
                    7'd4 : sda_out <= data_wr_t[6];  
                    7'd5 : scl <= 1'b1;              
                    7'd7 : scl <= 1'b0;              
                    7'd8 : sda_out <= data_wr_t[5];  
                    7'd9 : scl <= 1'b1;              
                    7'd11: scl <= 1'b0;              
                    7'd12: sda_out <= data_wr_t[4];  
                    7'd13: scl <= 1'b1;              
                    7'd15: scl <= 1'b0;              
                    7'd16: sda_out <= data_wr_t[3];  
                    7'd17: scl <= 1'b1;              
                    7'd19: scl <= 1'b0;              
                    7'd20: sda_out <= data_wr_t[2];  
                    7'd21: scl <= 1'b1;              
                    7'd23: scl <= 1'b0;              
                    7'd24: sda_out <= data_wr_t[1];  
                    7'd25: scl <= 1'b1;              
                    7'd27: scl <= 1'b0;              
                    7'd28: sda_out <= data_wr_t[0];  
                    7'd29: scl <= 1'b1;              
                    7'd31: scl <= 1'b0;              
                    7'd32: begin                     
                        sda_dir <= 1'b0;           
                        sda_out <= 1'b1;                              
                    end                              
                    7'd33: scl <= 1'b1;              
                    7'd34: begin                     //从机应答
                        st_done <= 1'b1;     
                        if(sda_in == 1'b1)           //高电平表示未应答
                            i2c_ack <= 1'b1;         //拉高应答标志位    
                    end          
                    7'd35: begin                     
                        scl  <= 1'b0;                
                        cnt  <= 1'b0;                
                    end                              
                    default  :  ;                    
                endcase                              
            end                                      
            st_addr_rd: begin                        //写地址以进行读数据
                case(cnt)                            
                    7'd0 : begin                     
                        sda_dir <= 1'b1;             
                        sda_out <= 1'b1;             
                    end                              
                    7'd1 : scl <= 1'b1;              
                    7'd2 : sda_out <= 1'b0;          //重新开始
                    7'd3 : scl <= 1'b0;              
                    7'd4 : sda_out <= SLAVE_ADDR[6]; //传送器件地址
                    7'd5 : scl <= 1'b1;              
                    7'd7 : scl <= 1'b0;              
                    7'd8 : sda_out <= SLAVE_ADDR[5]; 
                    7'd9 : scl <= 1'b1;              
                    7'd11: scl <= 1'b0;              
                    7'd12: sda_out <= SLAVE_ADDR[4]; 
                    7'd13: scl <= 1'b1;              
                    7'd15: scl <= 1'b0;              
                    7'd16: sda_out <= SLAVE_ADDR[3]; 
                    7'd17: scl <= 1'b1;              
                    7'd19: scl <= 1'b0;              
                    7'd20: sda_out <= SLAVE_ADDR[2]; 
                    7'd21: scl <= 1'b1;              
                    7'd23: scl <= 1'b0;              
                    7'd24: sda_out <= SLAVE_ADDR[1]; 
                    7'd25: scl <= 1'b1;              
                    7'd27: scl <= 1'b0;              
                    7'd28: sda_out <= SLAVE_ADDR[0]; 
                    7'd29: scl <= 1'b1;              
                    7'd31: scl <= 1'b0;              
                    7'd32: sda_out <= 1'b1;          //1:读
                    7'd33: scl <= 1'b1;              
                    7'd35: scl <= 1'b0;              
                    7'd36: begin                     
                        sda_dir <= 1'b0;            
                        sda_out <= 1'b1;                    
                    end
                    7'd37: scl     <= 1'b1;
                    7'd38: begin                     //从机应答
                        st_done <= 1'b1;     
                        if(sda_in == 1'b1)           //高电平表示未应答
                            i2c_ack <= 1'b1;         //拉高应答标志位    
                    end   
                    7'd39: begin
                        scl <= 1'b0;
                        cnt <= 1'b0;
                    end
                    default : ;
                endcase
            end
            st_data_rd: begin                        //读取数据(8 bit)
                case(cnt)
                    7'd0: sda_dir <= 1'b0;
                    7'd1: begin
                        data_r[7] <= sda_in;
                        scl       <= 1'b1;
                    end
                    7'd3: scl  <= 1'b0;
                    7'd5: begin
                        data_r[6] <= sda_in ;
                        scl       <= 1'b1   ;
                    end
                    7'd7: scl  <= 1'b0;
                    7'd9: begin
                        data_r[5] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd11: scl  <= 1'b0;
                    7'd13: begin
                        data_r[4] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd15: scl  <= 1'b0;
                    7'd17: begin
                        data_r[3] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd19: scl  <= 1'b0;
                    7'd21: begin
                        data_r[2] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd23: scl  <= 1'b0;
                    7'd25: begin
                        data_r[1] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd27: scl  <= 1'b0;
                    7'd29: begin
                        data_r[0] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd31: scl  <= 1'b0;
                    7'd32: begin
                        sda_dir <= 1'b1;             
                        sda_out <= 1'b1;
                    end
                    7'd33: scl     <= 1'b1;
                    7'd34: st_done <= 1'b1;          //非应答
                    7'd35: begin
                        scl <= 1'b0;
                        cnt <= 1'b0;
                        i2c_data_r <= data_r;
                    end
                    default  :  ;
                endcase
            end
            st_stop: begin                           //结束I2C操作
                case(cnt)
                    7'd0: begin
                        sda_dir <= 1'b1;             //结束I2C
                        sda_out <= 1'b0;
                    end
                    7'd1 : scl     <= 1'b1;
                    7'd3 : sda_out <= 1'b1;
                    7'd15: st_done <= 1'b1;
                    7'd16: begin
                        cnt      <= 1'b0;
                        i2c_done <= 1'b1;            //向上层模块传递I2C结束信号
                    end
                    default  : ;
                endcase
            end
        endcase
    end
end

endmodule

六、上版验证

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2205183.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电脑上下载配置Flutter Sdk及如何配置Flutter国内镜像

电脑上面下载配置 Flutter Sdk &#xff08;所有版本方法一 样&#xff09;&#xff1a; 1 、下载 Flutter SDK https://flutter.dev/docs/development/tools/sdk/releases#windows 2 、把下载好的 Flutter SDK 随便减压到你想安装 Sdk 的目录 如减压到 &#xff08; F:\flu…

Flink job的提交流程

在Flink中&#xff0c;作业&#xff08;Job&#xff09;的提交流程是一个复杂的过程&#xff0c;涉及多个组件和模块&#xff0c;包括作业的编译、优化、序列化、任务分发、任务调度、资源分配等。Flink通过分布式架构来管理作业的生命周期&#xff0c;确保作业在不同节点上以高…

【Java】多线程代码案例

多线程代码案例 单例模式初步了解饿汉模式懒汉模式线程安全问题分析存在的问题 生产者消费者模型初识生产者消费者模型初识阻塞队列生产者消费者模型的意义BlockingQueue阻塞队列模拟实现 定时器初识计时器初识Timer类初识 schedule() 方法简易定时器的实现思路讲解代码书写 线…

面试字节跳动精选20道产品经理面试题分析回答

分享20道字节跳动产品经理的面试题&#xff0c;产品经理的面试很多会跟项目强关联&#xff0c;比如面试电商产品经理&#xff0c;就要多聊电商的业务&#xff0c;所以我们选了一些比较通用的&#xff0c;面试题及我们的分析回答。 01 20道面试题&#xff08;前10道&#xff0…

react中的重定向Redirect

1, 首先引入 import {BrowserRouter,Route,Switch,Redirect} from react-router-dom 2,使用 一般写在所有路由注册的最下方&#xff0c;当所有路由都无法匹配时&#xff0c;跳转到Redirect指定的路由 <Switch><Route path"/about" component{About}/>…

探索SLMi823x系列 40V, 1A/4A 具有不同配置 双通道死区可编程 隔离驱动器

SLMi823x系列选型型号&#xff1a; SLMi8230BDCG-DG SLMi8230DDCG-DG SLMi8231BDCG-DG SLMi8231DDCG-DG SLMi8232BDCG-DG SLMi8232DDCG-DG SLMi8233BDCG-DG SLMi8233DDCG-DG SLMi8234BDCG-DG SLMi8234DDCG-DG …

codeforces- 973-div2----补题

1、求最小时间 思路&#xff1a;简单的模拟 木桶效应 #include<iostream> #include<algorithm> using namespace std; typedef long long ll; int dx[] { 0,1,0,-1 }; int dy[] { 1,0,-1,0 }; const ll N 2e5 5; const ll mod 1e9 7; ll a[N]; void solve…

液相/气象色谱仪原理

色谱仪 1.液相色谱 常用的是柱色谱 高效液相色谱 在 HPLC 系统中&#xff0c;** 色谱柱内的固定相通常是由颗粒很细的填充物组成。这些细小的填充物具有更大的比表面积&#xff0c;能够提供更多的与样品分子相互作用的机会&#xff0c;从而提高分离效率和分辨率 **。然而&am…

双十一买什么东西好?五大双11好物推荐分享!

双十一购物节即将来临&#xff0c;作为一年一度的购物盛事&#xff0c;许多人都在考虑如何挑选心仪的商品。在这个特殊的日子里&#xff0c;各大电商平台纷纷推出了令人心动的优惠活动&#xff0c;琳琅满目的商品让人眼花缭乱。无论是为自己添置新物品&#xff0c;还是提前采购…

E系列I/O模块在锂电装备制造系统的应用

为了满足电池生产线对稳定性和生产效率的严苛要求&#xff0c;ZLG致远电子推出高速I/O应用方案&#xff0c;它不仅稳定可靠&#xff0c;而且速度快&#xff0c;能够迅速响应生产需求。 锂电池的生产工艺较为复杂&#xff0c;大致分为三个主要阶段&#xff1a;极片制作、电芯制作…

若依 从字典类型跳到字典数据跳到了404

描述&#xff1a; 在字典类型从表中字典类型跳转到详情的字典数据时跳到了404 解决过程&#xff1a; 由于我的id统一是用GUID&#xff0c;所以想到了路由表相关路由的正则校验&#xff0c;若依是int类型&#xff0c;我直接删掉了&#xff0c;改了之后还是跳404 后面想是路由表…

利用编程思维做题之反转链表

牛客网题目 1. 理解问题 给到我们的是一个单链表的头节点 pHead&#xff0c;要求反转后&#xff0c;返回新链表的头节点。 首先在心里设想能够快速理解的例子&#xff0c;如给你123序列&#xff0c;要你反转此序列如何回答&#xff1f;将最后一个数字3作为头&#xff0c;然后修…

如何修复 Windows 10 /11上 CrowdStrike 导致的蓝屏死机问题

CrowdStrike 是一家领先的网络安全技术提供商&#xff0c;为终端、云工作负载、身份和数据提供安全服务。其 Falcon 平台是一种统一的、云交付的安全解决方案&#xff0c;旨在防止所有类型的攻击&#xff0c;包括恶意软件等。然而&#xff0c;Windows 上 Falcon Sensor 代理的最…

ENSP静态路由实验 10.11

0x01 拓扑图 0x02 配置各接口和PC1、2的IP地址 PC1&#xff1a; PC2&#xff1a; AR1&#xff1a; 配置AR1&#xff0c;改名为R1&#xff0c;并配置各接口IP&#xff0c;随后保存。 <R1>system-view [Huawei]sysname R1 [R1]int g0/0/2 [R1-GigabitEthernet0/0/2]ip ad…

R语言结构方程模型(SEM)在生态学领域中的应用

结构方程模型&#xff08;Sructural Equation Model&#xff09;是一种建立、估计和检验研究系统中多变量间因果关系的模型方法&#xff0c;它可以替代多元回归、因子分析、协方差分析等方法&#xff0c;利用图形化模型方式清晰展示研究系统中变量间的因果网络关系&#xff0c;…

【向上管理第一步】2小时速成专业级看板,让高效汇报触手可及!

面对突如其来的重要汇报&#xff0c;您是否曾陷入时间紧迫与完美呈现的双重困境&#xff1f;想要在短时间内打造出既全面又引人注目的可视化看板&#xff0c;却苦于设计技巧的匮乏与审美眼光的局限&#xff1f;别担心&#xff0c;JVS-BI您的智慧汇报加速器&#xff0c;正蓄势待…

Java面试宝典-WEB学习

Java web学习 目录 Java web学习 1、说说 Servlet 的基本架构 2、说一说 Servlet 的生命周期? 3、如何实现一个自定义的 servlet&#xff1f; 4、servlet中有哪些核心类&#xff1f;都有什么特点&#xff1f; 5、什么情况下调用 doGet()和 doPost()&#xff1f; 6、request.ge…

自动化测试 | UnitTest框架

1. TestCase&#xff08;测试用例&#xff09; 步骤&#xff1a; 1. 导包 import unittest 2. 新建测试类并继承 unittest.TestCase 3. 测试方法必须以test字母开头 运行&#xff1a; 1. 运行测试类所有的测试方法&#xff…

国家海洋环境预报中心李本霞:全国首个海浪智能预报系统已投入业务化运行,AI助力海浪预报多项突破

近日&#xff0c;在第 20 届 CCF HPC China 2024 大会中&#xff0c;第六届海洋数值预报与高性能计算论坛圆满举办。在本次大会上&#xff0c;国家海洋环境预报中心海浪预报室主任李本霞以「人工智能在海浪预报中的应用」为主题展开演讲&#xff0c; HyperAI超神经在不违原意的…

配置MAC地址安全

概述 MAC地址安全配置是确保网络设备和通信安全的重要措施&#xff0c;通过限制、监控和管理设备的物理地址来防止未授权访问和潜在的网络威胁。以下是对MAC地址安全的概述&#xff1a; 基本概念 定义&#xff1a;MAC地址&#xff08;Media Access Control Address&#xff09…