前言
上一篇我们讲解了IIC的MASTER控制读写过程,今天我们讲一下IIC的SLAVE接收和回读的篇章,今天先开始介绍IIC的从机接收写指令的过程。
一、IIC从机接收
如下图示:
从机接收可以理解为是主机发送的逆过程,识别开始信号,主从地址握手,识别寄存器,获取寄存器值和数据值,最终完成数据接收和控制。
二、代码内容
在这里插module iic_slave #
(
parameter BYTE = 4 ,
parameter DWID = 32 ,
parameter UCNT = 25
)
(
input clk_sys ,
input rst_sys ,
//
input [6:0] slave_addr ,
input contin_mode ,
output reg iic_ena ,
output reg [7:0] iic_adr ,
output reg [DWID-1:0] iic_dat ,
//
input i2c_scl_m_i ,
output i2c_scl_m_o ,
output i2c_scl_m_t ,
input i2c_sda_m_i ,
output i2c_sda_m_o ,
output i2c_sda_m_t
);
// ********************************************************
// localparam
// ********************************************************
localparam IDLE = 4'd0 ;
localparam SLAV = 4'd1 ;
localparam REGS = 4'd2 ;
localparam MODE = 4'd3 ;
localparam CMWRTIE = 4'd4 ;
localparam CMREAD = 4'd5 ;
localparam OMWRITER = 4'd6 ;
localparam OMREAD = 4'd7 ;
localparam STOP = 4'd8 ;
// ********************************************************
// signal
// ********************************************************
reg [3:0] cstate = IDLE ;
reg [3:0] nstate = IDLE ;
wire start_pedge ;
wire start_nedge ;
wire stop_pedge ;
wire stop_nedge ;
reg read_start = 0 ;
reg write_read = 0 ;
wire scl_pedge ;
wire scl_nedge ;
reg [7:0] cnt_div = 0 ;
reg [15:0] cnt_us = 0 ;
reg [15:0] reg_us = 0 ;
reg [7:0] cnt_scl = 0 ;
reg [7:0] cnt_sda = 0 ;
reg [7:0] cnt_tags = 0 ;
reg [6:0] iic_slave = 0 ;
reg [7:0] reg_slave = 0 ;
reg [7:0] reg_regsi = 0 ;
reg [7:0] reg_data = 0 ;
reg reg_stop = 0 ;
reg [3:0] shift_cnt = 0 ;
reg [3:0] shift_reg = 0 ;
reg [5:0] shift_dat = 0 ;
reg readback = 0 ;
reg [31:0] data_value ;
reg reg_sda_o = 1 ;
wire dly_sda_o ;
// ********************************************************
// process
// ********************************************************
always @ ( posedge clk_sys )
begin
if( rst_sys )
cstate = IDLE ;
else
cstate = nstate ;
end
always @ ( * )
begin
case ( cstate )
IDLE : begin
if( read_start )
nstate = SLAV ;
else
nstate = IDLE ;
end
SLAV : begin
if(( slave_addr == iic_slave ) && readback )
nstate = REGS ;
else
nstate = SLAV ;
end
REGS : begin
if( readback )
nstate = MODE ;
else
nstate = REGS ;
end
MODE : begin
if( contin_mode && write_read == 0 )
nstate = CMWRTIE ;
else if( contin_mode && write_read == 1 )
nstate = CMREAD ;
else if( contin_mode == 0 && write_read == 0 )
nstate = OMWRITER ;
else if( contin_mode == 0 && write_read == 1 )
nstate = OMREAD ;
else
nstate = MODE ;
end
CMWRTIE : begin
if( readback && cnt_tags == BYTE - 1 )
nstate = STOP ;
else
nstate = CMWRTIE ;
end
OMWRITER : begin
if( readback )
nstate = STOP ;
else
nstate = OMWRITER ;
end
STOP : begin
if( stop_nedge )
nstate = IDLE ;
else
nstate = STOP ;
end
default : nstate = IDLE ;
endcase
end
// ========================================================
// TIME
// ========================================================
always @ ( posedge clk_sys ) // 1s == 25M 1us == 25次
begin
if( rst_sys )
cnt_div <= 0 ;
else if( cnt_div == UCNT - 1 )
cnt_div <= 0 ;
else
cnt_div <= cnt_div + 1 ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys || cstate == IDLE )
cnt_us <= 0 ;
else if( cnt_div == UCNT - 1 )
cnt_us <= cnt_us + 1 ;
else ;
end
// ========================================================
// READBACK
// ========================================================
always @ ( posedge clk_sys )
begin
if( rst_sys )
cnt_scl <= 0 ;
else if( i2c_scl_m_i && cstate == IDLE && cnt_div == UCNT - 1 )
cnt_scl <= cnt_scl + ~&cnt_scl ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys )
cnt_sda <= 0 ;
else if( i2c_sda_m_i && cstate == IDLE && cnt_div == UCNT - 1 )
cnt_sda <= cnt_sda + ~&cnt_sda ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys )
readback <= 0 ;
else if( i2c_sda_m_o == 1'b0 && i2c_sda_m_t == 1'b0 && scl_pedge )
readback <= 1 ;
else
readback <= 0 ;
end
// ========================================================
// RECV
// ========================================================
always @ ( posedge clk_sys )
begin
if( rst_sys )
read_start <= 0 ;
else if( cstate == IDLE && start_nedge )
read_start <= 1 ;
else
read_start <= 0 ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys )
reg_slave <= 0 ;
else if( cstate == SLAV && scl_pedge )
reg_slave <= { reg_slave[6:0],i2c_sda_m_i };
else ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys || cstate != SLAV)
shift_cnt <= 0 ;
else if( cstate == SLAV && scl_pedge )
shift_cnt <= shift_cnt + ~&shift_cnt ;
else ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys )
iic_slave <= 0 ;
else if( cstate == SLAV && shift_cnt == 7 )
iic_slave <= reg_slave ;
else ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys || cstate == IDLE )
write_read <= 0 ;
else if( cstate == SLAV && shift_cnt == 8 && scl_pedge )
begin
if( i2c_sda_m_i == 0 )
write_read <= 0 ;
else if( i2c_sda_m_i == 1 )
write_read <= 1 ;
else ;
end
end
// ========================================================
// REG ADDR
// ========================================================
always @ ( posedge clk_sys )
begin
if( rst_sys || cstate != REGS )
shift_reg <= 0 ;
else if( cstate == REGS && scl_pedge )
shift_reg <= shift_reg + ~&shift_reg ;
else ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys )
reg_regsi <= 0 ;
else if( cstate == REGS && scl_pedge && shift_reg < 8 )
reg_regsi <= {reg_regsi[6:0],i2c_sda_m_i};
else ;
end
// ========================================================
//
// ========================================================
always @ ( posedge clk_sys )
begin
if( rst_sys || cstate == IDLE || readback )
shift_dat <= 0 ;
else if((cstate == OMWRITER || cstate == CMWRTIE ) && scl_pedge )
shift_dat <= shift_dat + ~&shift_dat ;
else ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys )
reg_data <= 0 ;
else if((cstate == CMWRTIE || cstate == OMWRITER )&& scl_pedge )
reg_data <= {reg_data[6:0],i2c_sda_m_i };
else ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys || cstate == IDLE )
cnt_tags <= 0 ;
else if( cstate == CMWRTIE && readback )
cnt_tags <= cnt_tags + 1 ;
else ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys )
data_value <= 0 ;
else if( cstate == CMWRTIE && shift_dat == 8 && scl_pedge )
data_value <= {data_value[23:0],reg_data};
else ;
end
// ========================================================
//
// ========================================================
always @ ( posedge clk_sys )
begin
if( rst_sys )
iic_ena <= 0 ;
else if( cstate == STOP && scl_pedge )
iic_ena <= 1 ;
else
iic_ena <= 0 ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys )
iic_adr <= 0 ;
else if( cstate == STOP && scl_pedge )
iic_adr <= reg_regsi ;
else ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys )
iic_dat <= 0 ;
else if( cstate == STOP && scl_pedge && contin_mode == 0 )
iic_dat <= reg_data ;
else if( cstate == STOP && scl_pedge && contin_mode == 1 )
iic_dat <= data_value ;
end
// ========================================================
//
// ========================================================
always @ ( posedge clk_sys )
begin
if( cstate == STOP && scl_pedge )
reg_stop <= 1 ;
else if( cstate == STOP && start_pedge )
reg_stop <= 0 ;
else ;
end
// ========================================================
//
// ========================================================
always @ ( posedge clk_sys )
begin
if( rst_sys )
reg_sda_o <= 1 ;
else if( cstate == SLAV && shift_cnt == 8 && scl_nedge )
reg_sda_o <= 0 ;
else if( cstate == REGS && shift_reg == 0 && scl_nedge )
reg_sda_o <= 1 ;
else if( cstate == REGS && shift_reg == 8 && scl_nedge )
reg_sda_o <= 0 ;
else if( cstate == REGS && shift_reg == 9 && scl_nedge )
reg_sda_o <= 1 ;
else if( cstate == OMWRITER && shift_dat == 0 && scl_nedge )
reg_sda_o <= 1 ;
else if((cstate == OMWRITER || cstate == CMWRTIE ) && shift_dat == 8 && scl_nedge )
reg_sda_o <= 0 ;
else if( cstate == OMWRITER && shift_dat == 9 && scl_nedge )
reg_sda_o <= 1 ;
else if( cstate == CMWRTIE && shift_dat == 0 && scl_nedge )
reg_sda_o <= 1 ;
else if((cstate == STOP && scl_nedge )|| cstate == IDLE )
reg_sda_o <= 1 ;
else ;
end
assign i2c_sda_m_o = reg_sda_o ;
assign i2c_sda_m_t = reg_sda_o ;
// ========================================================
//
// ========================================================
edge_detect u0_edge_detect
(
//input
.clk ( clk_sys ),
.sig_in ( i2c_sda_m_i ),
//output
.p_edge ( start_pedge ),
.n_edge ( start_nedge )
);
edge_detect u1_edge_detect
(
//input
.clk ( clk_sys ),
.sig_in ( i2c_scl_m_i ),
//output
.p_edge ( scl_pedge ),
.n_edge ( scl_nedge )
);
edge_detect u2_edge_detect
(
//input
.clk ( clk_sys ),
.sig_in ( reg_stop ),
//output
.p_edge ( stop_pedge ),
.n_edge ( stop_nedge )
);
delay_base_reg #
(
.DATA_WIDTH ( 1 ),
.DELAY_TAPS ( 2 )
)u_delay_base_reg
(
//input
.clk ( clk_sys ),
.wrenin ( 1'b1 ),
.datain ( reg_sda_o ),
//output
.wrenout ( ),
.dataout ( dly_sda_o )
);
endmodule入代码片
调试总结
由图可看知,下发的IIC与解析出的IIC一致