一、简介
见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 / 1psmodule 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]));
endelse 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 / 1psmodule 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)beginif(!rst_n)rstn_1ms <= 16'd0;else beginif(rstn_1ms == 16'd1000)rstn_1ms <= rstn_1ms;elserstn_1ms <= rstn_1ms + 1'b1;end
end//clk时钟配置成1MHZ,周期为1us 5000*1us = 5ms
//上电到开始配置IIC等待5ms
always @(posedge clk or negedge rstn_out) beginif(!rstn_out)start_init_cnt <= 13'b0;else if(start_init_cnt < 13'd5000) beginstart_init_cnt <= start_init_cnt + 1'b1; end
end//每个寄存器配置之间延迟20us
always @(posedge clk or negedge rstn_out) beginif(!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 ; elsecfg_delay_cnt <= cfg_delay_cnt + 8'b1; end//寄存器配置个数计数
always @(posedge clk or negedge rstn_out) beginif(!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) beginif(!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; elsei2c_exec <= 1'b0;
end //配置I2C读写控制信号
always @(posedge clk or negedge rstn_out) beginif(!rstn_out)i2c_rh_wl <= 1'b1;else i2c_rh_wl <= 1'b0;
end//初始化完成信号
always @(posedge clk or negedge rstn_out) beginif(!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) beginif(!rstn_out)i2c_data <= 24'b0;else begincase(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}; endcaseend
endendmodule
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) beginif(!rst_n) begindri_clk <= 1'b0;clk_cnt <= 10'd0;endelse if(clk_cnt == clk_divide[8:1] - 1'd1) beginclk_cnt <= 10'd0;dri_clk <= ~dri_clk;endelseclk_cnt <= clk_cnt + 1'b1;
end//(三段式状态机)同步时序描述状态转移
always @(posedge dri_clk or negedge rst_n) beginif(!rst_n)cur_state <= st_idle;elsecur_state <= next_state;
end//组合逻辑判断状态转移条件
always @(*) beginnext_state = st_idle;case(cur_state)st_idle: begin //空闲状态if(i2c_exec) beginnext_state = st_sladdr;endelsenext_state = st_idle;endst_sladdr: beginif(st_done) beginif(bit_ctrl) //判断是16位还是8位字地址next_state = st_addr16;elsenext_state = st_addr8 ;endelsenext_state = st_sladdr;endst_addr16: begin //写16位字地址if(st_done) beginnext_state = st_addr8;endelse beginnext_state = st_addr16;endendst_addr8: begin //8位字地址if(st_done) beginif(wr_flag==1'b0) //读写判断next_state = st_data_wr;elsenext_state = st_addr_rd;endelse beginnext_state = st_addr8;endendst_data_wr: begin //写数据(8 bit)if(st_done)next_state = st_stop;elsenext_state = st_data_wr;endst_addr_rd: begin //写地址以进行读数据if(st_done) beginnext_state = st_data_rd;endelse beginnext_state = st_addr_rd;endendst_data_rd: begin //读取数据(8 bit)if(st_done)next_state = st_stop;elsenext_state = st_data_rd;endst_stop: begin //结束I2C操作if(st_done)next_state = st_idle;elsenext_state = st_stop ;enddefault: next_state= st_idle;endcase
end//时序电路描述状态输出
always @(posedge dri_clk or negedge rst_n) begin//复位初始化if(!rst_n) beginscl <= 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; //开始I2C7'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; end7'd37: scl <= 1'b1;7'd38: begin //从机应答st_done <= 1'b1; if(sda_in == 1'b1) //高电平表示未应答i2c_ack <= 1'b1; //拉高应答标志位 end 7'd39: beginscl <= 1'b0;cnt <= 1'b0;enddefault : ;endcaseendst_data_rd: begin //读取数据(8 bit)case(cnt)7'd0: sda_dir <= 1'b0;7'd1: begindata_r[7] <= sda_in;scl <= 1'b1;end7'd3: scl <= 1'b0;7'd5: begindata_r[6] <= sda_in ;scl <= 1'b1 ;end7'd7: scl <= 1'b0;7'd9: begindata_r[5] <= sda_in;scl <= 1'b1 ;end7'd11: scl <= 1'b0;7'd13: begindata_r[4] <= sda_in;scl <= 1'b1 ;end7'd15: scl <= 1'b0;7'd17: begindata_r[3] <= sda_in;scl <= 1'b1 ;end7'd19: scl <= 1'b0;7'd21: begindata_r[2] <= sda_in;scl <= 1'b1 ;end7'd23: scl <= 1'b0;7'd25: begindata_r[1] <= sda_in;scl <= 1'b1 ;end7'd27: scl <= 1'b0;7'd29: begindata_r[0] <= sda_in;scl <= 1'b1 ;end7'd31: scl <= 1'b0;7'd32: beginsda_dir <= 1'b1; sda_out <= 1'b1;end7'd33: scl <= 1'b1;7'd34: st_done <= 1'b1; //非应答7'd35: beginscl <= 1'b0;cnt <= 1'b0;i2c_data_r <= data_r;enddefault : ;endcaseendst_stop: begin //结束I2C操作case(cnt)7'd0: beginsda_dir <= 1'b1; //结束I2Csda_out <= 1'b0;end7'd1 : scl <= 1'b1;7'd3 : sda_out <= 1'b1;7'd15: st_done <= 1'b1;7'd16: begincnt <= 1'b0;i2c_done <= 1'b1; //向上层模块传递I2C结束信号enddefault : ;endcaseendendcaseend
endendmodule