一、简介
见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 位传输。
三、代码实现
Haskell
`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
Haskell
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驱动方案
Haskell
`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
Haskell
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
Haskell
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
Haskell
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