module i2c_ctrl
#(
parameter DEVICE_ADDR = 7'b1010_000 , //i2c 设备地址
parameter SYS_CLK_FREQ = 26'd50_000_000 , //输入系统时钟频率
parameter SCL_FREQ = 18'd250_000 //i2c 设备 scl 时钟频率
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire wr_en ,
input wire rd_en ,
input wire addr_num ,
input wire [15:0] byte_addr ,
input wire [7:0] wr_data ,
input wire i2c_start ,
output reg i2c_clk ,
output reg i2c_end ,
output reg [7:0] rd_data ,
output reg i2c_scl ,
inout wire i2c_sda
);
/参数定义//
//这里是为了生成i2c_clk信号,这个信号频率是i2c_scl的4倍,因为从系统时钟/i2c_clk时钟表示一个周期的计数值,
//i2c_clk半个周期翻转一次,所以要÷2,为了生成i2c_clk又要÷4,所以一共是÷8
localparam CNT_CLK_MAX = (SYS_CLK_FREQ/SCL_FREQ) >> 2'd3 ;
//状态机变量
localparam IDLE = 'd0 ,
START_1 = 'd1 ,
SEND_D_ADDR = 'd2 ,
ACK_1 = 'd3 ,
SEND_B_ADDR_H = 'd4 ,
ACK_2 = 'd5 ,
SEND_B_ADDR_L = 'd6 ,
ACK_3 = 'd7 ,
WR_DATA = 'd8 ,
ACK_4 = 'd9 ,
STOP = 'd10 ,
START_2 = 'd11 ,
SEND_RD_ADDR = 'd12 ,
ACK_5 = 'd13 ,
RD_DATA = 'd14 ,
N_ACK = 'd15 ;
/线网变量定义//
wire w_sda_en ;
wire w_sda_in ;
/寄存器变量定义
reg [7:0] r_cnt_clk ;
reg [3:0] r_state ;
reg [2:0] r_cnt_i2c_clk ;
reg [2:0] r_cnt_bit ;
reg r_cnt_i2c_clk_en ;
reg r_ack ;
reg r_i2c_sda_reg ;
reg [7:0] r_rd_data_reg ;
/程序/
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
r_cnt_clk <= 'd0;
else if(r_cnt_clk == CNT_CLK_MAX -1'd1)
r_cnt_clk <= 'd0;
else
r_cnt_clk <= r_cnt_clk +'d1;
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
i2c_clk <= 'd0;
else if(r_cnt_clk == CNT_CLK_MAX -1'd1)
i2c_clk <= ~i2c_clk;
else
i2c_clk <= i2c_clk;
end
//状态转移
always@(posedge i2c_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
r_state <= IDLE;
else
case(r_state)
IDLE :
begin
if(i2c_start == 'd1)
r_state <= START_1;
else
r_state <= IDLE;
end
START_1 :
begin
if(r_cnt_i2c_clk == 'd3)
r_state <= SEND_D_ADDR;
else
r_state <= START_1;
end
SEND_D_ADDR :
begin
if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
r_state <= ACK_1;
else
r_state <= SEND_D_ADDR;
end
ACK_1 :
begin
if(r_ack == 'd0 && r_cnt_i2c_clk == 'd3)begin
if(addr_num == 'd1)
r_state <= SEND_B_ADDR_H;
else
r_state <= SEND_B_ADDR_L;
end
else
r_state <= ACK_1;
end
SEND_B_ADDR_H:
begin
if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
r_state <= ACK_2;
else
r_state <= SEND_B_ADDR_H;
end
ACK_2 :
begin
if(r_cnt_i2c_clk == 'd3 && r_ack == 'd0)
r_state <= SEND_B_ADDR_L;
else
r_state <= ACK_2;
end
SEND_B_ADDR_L:
begin
if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
r_state <= ACK_3;
else
r_state <= SEND_B_ADDR_L;
end
ACK_3 :
begin
if(r_ack == 'd0 && r_cnt_i2c_clk == 'd3)begin
if(wr_en == 'd1)
r_state <= WR_DATA;
else if(rd_en == 'd1)
r_state <= START_2;
else
r_state <= r_state;
end
else
r_state <= ACK_3;
end
WR_DATA :
begin
if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
r_state <= ACK_4;
else
r_state <= WR_DATA;
end
ACK_4 :
begin
if(r_cnt_i2c_clk == 'd3 && r_ack == 'd0)
r_state <= STOP;
else
r_state <= ACK_4;
end
STOP :
begin
if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd3)
r_state <= IDLE;
else
r_state <= STOP;
end
START_2 :
begin
if(r_cnt_i2c_clk == 'd3)
r_state <= SEND_RD_ADDR;
else
r_state <= START_2;
end
SEND_RD_ADDR :
begin
if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
r_state <= ACK_5;
else
r_state <= SEND_RD_ADDR;
end
ACK_5 :
begin
if(r_cnt_i2c_clk == 'd3 && r_ack == 'd0)
r_state <= RD_DATA;
else
r_state <= ACK_5;
end
RD_DATA :
begin
if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
r_state <= N_ACK;
else
r_state <= RD_DATA;
end
N_ACK :
begin
if(r_cnt_i2c_clk == 'd3)
r_state <= STOP;
else
r_state <= N_ACK;
end
default :r_state <= IDLE;
endcase
end
always@(posedge i2c_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
r_cnt_i2c_clk_en <= 'd0;
else if(r_state == STOP && r_cnt_bit == 'd3 && r_cnt_i2c_clk == 'd3)
r_cnt_i2c_clk_en <= 'd0;
else if(i2c_start == 'd1)
r_cnt_i2c_clk_en <= 'd1;
else
r_cnt_i2c_clk_en <= r_cnt_i2c_clk_en;
end
always@(posedge i2c_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
r_cnt_i2c_clk <= 'd0;
else if(r_cnt_i2c_clk == 'd3)
r_cnt_i2c_clk <= 'd0;
else if(r_cnt_i2c_clk_en == 'd1)
r_cnt_i2c_clk <= r_cnt_i2c_clk + 'd1;
else
r_cnt_i2c_clk <= 'd0;
end
always@(posedge i2c_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
r_cnt_bit <= 'd0;
else if(r_state == START_1 || r_state == ACK_1 || r_state == ACK_2
||r_state == ACK_3 || r_state == ACK_4 || r_state == ACK_5
||r_state == START_2 || r_state == IDLE || r_state == N_ACK
)
r_cnt_bit <= 'd0;
else if((r_state == SEND_D_ADDR || r_state == SEND_B_ADDR_H || r_state == SEND_B_ADDR_L
|| r_state == SEND_RD_ADDR || r_state == RD_DATA || r_state == WR_DATA)
&& r_cnt_bit == 'd7 && r_cnt_i2c_clk == 'd3)
r_cnt_bit <= 'd0;
else if(r_state == STOP && r_cnt_bit == 'd3 && r_cnt_i2c_clk == 'd3)
r_cnt_bit <= 'd0;
else if(r_cnt_i2c_clk_en == 'd1 && r_cnt_i2c_clk == 'd3)
r_cnt_bit <= r_cnt_bit+'d1;
else
r_cnt_bit <= r_cnt_bit;
end
always@(posedge i2c_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
i2c_end <= 'd0;
else if(r_state == STOP && r_cnt_bit == 'd3 && r_cnt_i2c_clk == 'd3)
i2c_end <= 'd1;
else
i2c_end <= 'd0;
end
always@(*)begin
if(!sys_rst_n)
i2c_scl <= 'd1;
case(r_state)
IDLE :
i2c_scl <= 'd1;
START_1 :
begin
if(r_cnt_i2c_clk == 'd3)
i2c_scl <= 'd0;
else
i2c_scl <= 'd1;
end
SEND_D_ADDR , SEND_B_ADDR_H , SEND_B_ADDR_L , WR_DATA
, ACK_1 , ACK_2 , ACK_3 , ACK_4 , ACK_5 , RD_DATA
, START_2 , SEND_RD_ADDR , N_ACK:
begin
if(r_cnt_i2c_clk == 'd1 || r_cnt_i2c_clk == 'd2)
i2c_scl <= 'd1;
else
i2c_scl <= 'd0;
end
STOP :
begin
if(r_cnt_i2c_clk == 'd0 && r_cnt_bit == 'd0)
i2c_scl <= 'd0;
else
i2c_scl <= 'd1;
end
default : i2c_scl <= 'd1;
endcase
end
always@(*)begin
if(!sys_rst_n)
r_i2c_sda_reg <= 'd0;
else
case(r_state)
IDLE :r_i2c_sda_reg <= 'd1;
START_1 :
begin
if(r_cnt_i2c_clk <= 'd0)
r_i2c_sda_reg <= 'd1;
else
r_i2c_sda_reg <= 'd0;
end
SEND_D_ADDR :
begin
if(r_cnt_bit <= 'd6)
r_i2c_sda_reg <= DEVICE_ADDR[6 -r_cnt_bit];
else
r_i2c_sda_reg <= 'd0;
end
ACK_1 :r_i2c_sda_reg <= 'd1;
SEND_B_ADDR_H :r_i2c_sda_reg <= byte_addr[15 - r_cnt_bit];
ACK_2 :r_i2c_sda_reg <= 'd1;
SEND_B_ADDR_L :r_i2c_sda_reg <= byte_addr[7 - r_cnt_bit];
ACK_3 :r_i2c_sda_reg <= 'd1;
WR_DATA :r_i2c_sda_reg <= wr_data[7 - r_cnt_bit];
ACK_4 :r_i2c_sda_reg <= 'd1;
STOP :
begin
if(r_cnt_bit == 'd0 && r_cnt_i2c_clk < 'd3)
r_i2c_sda_reg <= 'd0;
else
r_i2c_sda_reg <= 'd1;
end
START_2 :
begin
if(r_cnt_i2c_clk <='d1)
r_i2c_sda_reg <= 'd1;
else
r_i2c_sda_reg <= 'd0;
end
SEND_RD_ADDR :
begin
if(r_cnt_bit <= 'd6)
r_i2c_sda_reg <= DEVICE_ADDR[6 -r_cnt_bit];
else
r_i2c_sda_reg <= 'd1;
end
ACK_5 :r_i2c_sda_reg <= 'd1;
RD_DATA :r_i2c_sda_reg <= 'd1;
N_ACK :r_i2c_sda_reg <= 'd1;
default :r_i2c_sda_reg <= 'd1;
endcase
end
assign w_sda_en = ((r_state == ACK_1) || (r_state == ACK_2) || (r_state == ACK_3) || (r_state == ACK_4)
|| (r_state == ACK_5) || (r_state == RD_DATA))? 'd0 : 'd1;
assign i2c_sda = (w_sda_en == 'd1)? r_i2c_sda_reg : 'bz;
assign w_sda_in = i2c_sda;
/*用组合逻辑自己赋值自己会锁存
always@(*)begin
if(!sys_rst_n)
r_ack <= 'd0;
else
case(r_state)
IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L,
WR_DATA,START_2,SEND_RD_ADDR,RD_DATA,N_ACK:
r_ack <= 1'd1;
ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
if(r_cnt_i2c_clk == 'd0)
r_ack <= w_sda_in;
else
r_ack <= r_ack;
default:r_ack <= 1'd1;
endcase
end
*/
always@(posedge i2c_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
r_ack <= 'd0;
else
case(r_state)
IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L,
WR_DATA,START_2,SEND_RD_ADDR,RD_DATA,N_ACK:
r_ack <= 1'd1;
ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
if(r_cnt_i2c_clk == 'd1)
r_ack <= w_sda_in;
else
r_ack <= r_ack;
default:r_ack <= 1'd1;
endcase
end
/*用组合逻辑自己赋值自己会锁存
always@(*)begin
if(!sys_rst_n)
r_rd_data_reg <= 'd0;
else
case(r_state)
IDLE:
r_rd_data_reg <= 'd0;
RD_DATA:
begin
if(r_cnt_i2c_clk == 'd2)
r_rd_data_reg[7-r_cnt_bit] <= w_sda_in;
else
r_rd_data_reg <= r_rd_data_reg;
end
default :r_rd_data_reg <= r_rd_data_reg;
endcase
end
*/
always@(posedge i2c_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
r_rd_data_reg <= 'd0;
else
case(r_state)
IDLE:
r_rd_data_reg <= 'd0;
RD_DATA:
begin
if(r_cnt_i2c_clk == 'd1)
r_rd_data_reg[7-r_cnt_bit] <= w_sda_in;
else
r_rd_data_reg <= r_rd_data_reg;
end
default :r_rd_data_reg <= r_rd_data_reg;
endcase
end
always@(posedge i2c_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
rd_data <= 8'd0;
else if(r_state == RD_DATA && r_cnt_bit == 3'd7 && r_cnt_i2c_clk == 2'd3)
rd_data <= r_rd_data_reg;
else
rd_data <= rd_data;
end
endmodule
注意:和野火官方的代码相比我这里做了两处修改,分别是对r_ack,和r_rd_data_reg信号的赋值上。如果用野火的代码,它用的是组合逻辑赋值的方式,但是在组合逻辑中如果把自己的值赋值给自己会产生锁存器,这是我们不想要的,在编译的时候会报警告
所以在这里,我们r_ack和r_rd_data_reg用时序逻辑的方式进行赋值,效果是一样的。这里讲一下r_ack的赋值,野火的代码逻辑是一到ACK_1,ACK_2,ACK_3,ACK_4,ACK_5,就采样信号的输入,然后保持不变。但是我们在时序逻辑中r_ack的赋值时ACK_1,ACK_2,ACK_3,ACK_4,ACK_5状态下信号稳定的时候再去采样,感觉会更好一点,同时能产生锁存器的问题