`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2020/03/15 15:59:00
// Design Name:
// Module Name: iic_tx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module iic_master(
input clk,
input arst,
input i_send_en,
input i_recv_en,//
input [6:0] i_dev_addr,
input [7:0] i_word_addr,
input [7:0] i_write_dat,
output reg o_send_done,
output reg o_recv_done,//
output reg [7:0] o_recv_dat,
output reg sda_mode,
output reg sda_reg,
output o_scl,
inout io_sda
);
parameter C_DIV_SEL = 10'd500;
parameter C_DIV_SEL0 = (C_DIV_SEL>>2) -1, // 用来产生IIC总线SCL高电平最中间的标志位
C_DIV_SEL1 = (C_DIV_SEL>>1) -1,
C_DIV_SEL2 = (C_DIV_SEL0 + C_DIV_SEL1) +1, // 用来产生IIC总线SCL低电平最中间的标志位
C_DIV_SEL3 = (C_DIV_SEL>>1) +1; // 用来产生IIC总线SCL下降沿标志位
parameter IDLE = 4'd0, LOAD_DEV_ADDR = 4'd1, LOAD_WORD_ADDR = 4'd2, LOAD_DATA = 4'd3,
START_SIG = 4'd4, SEND_BYTE = 4'd5, WAIT_ACK = 4'd6, CHECK_ACK = 4'd7,
STOP_SIG = 4'd8, W_OR_R_DONE = 4'd9, SEND_NOACK =4'd10, LOAD_DEV_ADDR_R = 4'd11,
GET_BYTE = 4'd12, RDY_FOR_STOP = 4'd13;
//
reg [9:0] scl_cnt;
reg scl_en;
reg [3:0] state;
//reg sda_mode;
//reg sda_reg;
reg [7:0] load_dat;
reg [3:0] bit_cnt;
reg ack_flag;
reg [3:0] jump_state;
reg [7:0] read_dat;
wire scl_l_mid;
wire scl_h_mid;
wire scl_neg,scl_pos;
// assign io_sda = sda_mode ? sda_reg: 1'bz;//inout
//
reg [1:0] scl_r;
assign scl_neg = (scl_r == 2'b10) ? 1:0;
assign scl_pos = (scl_r == 2'b01) ? 1:0;
always@(posedge clk , posedge arst)
if(arst)
scl_r <= 0;
else
scl_r <= {scl_r[0],o_scl};
always@(posedge clk ,posedge arst )
if(arst)
scl_cnt <= 0;
else if(scl_en)
if(scl_cnt == C_DIV_SEL-1)
scl_cnt <= 0;
else
scl_cnt <= scl_cnt + 1;
else
scl_cnt <= 0;
assign o_scl = (scl_cnt <= C_DIV_SEL1) ? 1 : 0;
assign scl_l_mid = (scl_cnt == C_DIV_SEL2) ? 1 : 0;
assign scl_h_mid = (scl_cnt == C_DIV_SEL0) ? 1 : 0;
///
always @(posedge clk ,posedge arst)
if(arst)begin
state <= IDLE;
sda_mode <= 1; //默认输出
sda_reg <= 1; //空闲状态为1
scl_en <= 0;
load_dat <= 0;
bit_cnt <= 0;
o_send_done <= 0;
jump_state <= 0;
ack_flag <= 0;
read_dat <= 0; //
o_recv_dat <= 0; //
o_recv_done <= 0; //
end
else if(i_send_en || i_recv_en)
case(state)
IDLE:
begin
state <= LOAD_DEV_ADDR;
sda_mode <= 1;
sda_reg <= 1;
scl_en <= 0;
load_dat <= 0;
bit_cnt <= 0;
o_send_done <= 0;
jump_state <= 0;
ack_flag <= 0;
end
LOAD_DEV_ADDR:
begin
state <= START_SIG;
load_dat <= {i_dev_addr,1'b0};//第一次写或者读操作都是0
jump_state <= LOAD_WORD_ADDR;
//其他保持
end
LOAD_WORD_ADDR:
begin
state <= SEND_BYTE;
load_dat <= i_word_addr;
if(i_send_en)
begin
jump_state <= LOAD_DATA;
end
else if(i_recv_en)
begin
jump_state <= LOAD_DEV_ADDR_R;//第二次加载,末尾为1
end
end
LOAD_DEV_ADDR_R:
begin
state <= START_SIG;
load_dat <= {i_dev_addr,1'b1};//读操作第二次是1
jump_state <= GET_BYTE;
end
LOAD_DATA:
begin
state <= SEND_BYTE;
load_dat <= i_write_dat;
jump_state <= STOP_SIG;
end
START_SIG:
begin
scl_en <= 1;
sda_mode <= 1;
if(scl_h_mid)
begin
state <= SEND_BYTE;
sda_reg <= 0;
end
else
begin
state <= START_SIG;
sda_reg <= sda_reg;
end
end
SEND_BYTE:
begin
scl_en <= 1;
sda_mode <= 1;
if(scl_l_mid)
if(bit_cnt == 8)
begin
bit_cnt <= 0;
state <= WAIT_ACK;
sda_reg <= sda_reg;
end
else
begin
state <= SEND_BYTE;
sda_reg <= load_dat[7-bit_cnt] ;
bit_cnt <= bit_cnt + 1;
end
else
state <= SEND_BYTE;//其他信号保持
end
WAIT_ACK:
begin
scl_en <= 1;
sda_mode <= 0;//
if(scl_h_mid)
begin
ack_flag <= io_sda;
state <= CHECK_ACK;
end
else
begin
ack_flag <= ack_flag;
state <= WAIT_ACK;
end
end
CHECK_ACK:
begin
scl_en <= 1;
if(!ack_flag)//ACK有效
if(scl_neg)
begin
state <= jump_state;
sda_mode <= 1;
if(i_send_en)sda_reg <= 0;//读取完应答信号以后要把SDA信号设置成输出并拉低,因为如果这个状态后面是停止状态的话,需要SDA信号的上升沿,所以这里提前拉低它
else if(i_recv_en) sda_reg <= 1;//把SDA信号设置成输出并拉高方便产生第二次起始信号
else sda_reg <= 0;
end
else
state <= CHECK_ACK;
else
state <= IDLE;//出错?
end
GET_BYTE:
begin
scl_en <= 1;
sda_mode <= 0;
sda_reg <= 0;
/
if( scl_neg && bit_cnt == 8) //需要在低电平中间进行ACK
begin
bit_cnt <= 0;
state <= SEND_NOACK; //注意
read_dat <= read_dat ;
o_recv_dat <= read_dat;
o_recv_done <= 1;
end
else if( scl_h_mid && o_scl) //在一次写结束后到下一次写开始的scl的周期会大于500,导致一个周期多次采样,因此要加上&& i_scl
begin
bit_cnt <= bit_cnt + 1;
state <= GET_BYTE;
read_dat <= {read_dat[6:0],io_sda};
o_recv_dat <= 0;
o_recv_done <= 0;
end
else
state <= GET_BYTE;
//
end
SEND_NOACK:
begin
scl_en <= 1;
sda_mode <= 1;
if(scl_l_mid)
begin
state <= RDY_FOR_STOP;
sda_reg <= 1; //注意 noack 为1
end
else
begin
state <= SEND_NOACK;
end
end
RDY_FOR_STOP:
begin
scl_en <= 1;
sda_mode <= 1;
if(scl_neg)
begin
state <= STOP_SIG;
sda_reg <= 0; //后面是停止状态,需要SDA信号的上升沿,所以这里提前拉低它
end
else
begin
state <= RDY_FOR_STOP;
end
end
STOP_SIG:
begin
scl_en <= 1;
sda_mode <= 1;
if(scl_h_mid)
begin
state <= W_OR_R_DONE;
sda_reg <= 1;
end
else
state <= STOP_SIG;
end
W_OR_R_DONE:
begin
state <= IDLE;
scl_en <= 0;
sda_mode <= 1;
sda_reg <= 1;
load_dat <= 0;
bit_cnt <= 0;
o_send_done <= 1;
jump_state <= 0;
ack_flag <= 0;
o_recv_dat <= 0;
o_recv_done <= 0;
end
default: state <= IDLE;
endcase
else
begin
state <= IDLE;
scl_en <= 0;
sda_mode <= 1;
sda_reg <= 1;
load_dat <= 0;
bit_cnt <= 0;
o_send_done <= 0;
jump_state <= 0;
ack_flag <= 0;
end
endmodule