(三)、主机读
上一篇文章完成了IIC写程序代码,这篇文章主要是要实现IIC读程,有了IIC写程序的经验,对于IIC读来说就不会有一开始的迷茫,话不多说,先看下IIC读所需要的过程如何,如下图示:
如图所示,上述包含了单字节读和多字节连续读的时序过程,分别为开始信号+从机地址+写信号+应答+寄存器地址+应答+再次的开始信号+从机地址+读信号+应答+data0+...+datan+PEC+应答+停止信号。
对于PEC,他是IIC得一种校验码,这里先不用管它,因为大多数场景不需要使用,但是这个d代码也可以接收,此外,相对于IIC写代码来说,读代码会比写代码要复杂一点,另外还需注意中间得起始信号时序。
话不多说,直接上代码,请各位注意版权问题,其次,代码在仿真和项目中已经初步实现读取功能,但是是否可应用于大多数场景还是未知得,若出现bug还需再次优化。
// ********************************************************
// Copyright: 优快云 account juningc
// The email address is: junpingc@noreply.gitcode.com
// Unauthorized copying is prohibited. If you need to use it, please contact the author
// All interpretation rights belong to the copyright author
// 2025年04月11日
// ********************************************************
module iic_read_master #
(
parameter DWID = 8 ,
parameter UCNT = 25 ,
parameter BUILD_TIME = 5 , //us
parameter HOLD_TIME = 4 //us
)
(
input clk_sys ,
input rst_sys ,
//
input write_dena ,
input [6:0] slave_addr ,
input [7:0] regis_addr ,
input [2:0] read_mode ,
output reg read_valid ,
output reg [7:0] read_tdata ,
//
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 START = 4'd1 ;
localparam SADDR = 4'd2 ;
localparam RADDR = 4'd3 ;
localparam AGAIN = 4'd4 ;
localparam DADDR = 4'd5 ;
localparam RDATA = 4'd6 ;
localparam RACK = 4'd7 ;
localparam STOP = 4'd8 ;
// ********************************************************
// signal
// ********************************************************
reg [3:0] cstate = IDLE ;
reg [3:0] nstate = IDLE ;
wire write_start ;
reg write_sack ;
wire pedge_sack ;
reg [15:0] cnt_us = 0 ;
reg [7:0] cnt_div = 0 ;
reg [3:0] cnt_stop = 0 ;
reg [3:0] cnt_tags = 0 ;
reg [2:0] shift_wcnt = 0 ;
reg [3:0] shift_rcnt = 0 ;
reg [7:0] shift_dat = 0 ;
reg reg_str = 0 ;
reg reg_scl ;
reg reg_sda ;
wire scl_pedge ;
wire scl_nedge ;
wire sda_pedge ;
wire sda_nedge ;
// ********************************************************
// process
// ********************************************************
always @ ( posedge clk_sys )
begin
if( rst_sys || &cnt_stop )
reg_str <= 0 ;
else if( write_start )
reg_str <= 1 ;
else ;
end
always @ ( posedge clk_sys ) // 1s == 25M 1us == 25次
begin
if( rst_sys || &cnt_stop )
cnt_div <= 0 ;
else if( cnt_div == UCNT - 1 )
cnt_div <= 0 ;
else if( reg_str )
cnt_div <= cnt_div + 1 ;
else ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys || cstate == STOP )
cnt_us <= 0 ;
else if( reg_str == 1 && cnt_div == UCNT - 1 )
cnt_us <= cnt_us + 1 ;
else ;
end
// ========================================================
//
// ========================================================
always @ ( posedge clk_sys )
begin
if( rst_sys )
cstate = IDLE ;
else
cstate = nstate ;
end
always @ ( * )
begin
case( cstate )
IDLE : begin
if( write_start )
nstate = START ;
else
nstate = IDLE ;
end
START : begin
if( cnt_us == BUILD_TIME + HOLD_TIME )
nstate = SADDR ;
else
nstate = START ;
end
SADDR : begin
if( pedge_sack )
nstate = RADDR ;
else
nstate = SADDR ;
end
RADDR : begin
if( pedge_sack )
nstate = AGAIN ;
else
nstate = RADDR ;
end
AGAIN : begin
if( cnt_us == 16'h2f + 16'h5 + 16'h4 )
nstate = DADDR ;
else
nstate = AGAIN ;
end
DADDR : begin
if( pedge_sack )
nstate = RDATA ;
else
nstate = DADDR ;
end
RDATA : begin
if( shift_rcnt == 0 && scl_nedge && read_mode[2] == 0 )
nstate = RACK ;
else if( shift_rcnt == 0 && scl_nedge && read_mode[2] == 1 && cnt_tags == read_mode[1:0] + 1 )
nstate = RACK ;
else
nstate = RDATA ;
end
RACK : begin
if( scl_pedge )
nstate = STOP ;
else
nstate = RACK ;
end
STOP : begin
if( &cnt_stop )
nstate = IDLE ;
else
nstate = STOP ;
end
default:nstate = IDLE ;
endcase
end
// ========================================================
//
// ========================================================
always @ ( posedge clk_sys )
begin
if( rst_sys || cstate == STOP )
reg_scl <= 1 ;
else if( cnt_us <= BUILD_TIME + HOLD_TIME )
reg_scl <= 1 ;
else if( cstate == AGAIN && cnt_us >= 16'h2f && cnt_us < 16'h38 )
reg_scl <= 1 ;
else if( cstate == AGAIN && ( cnt_us == 16'h2f + 16'h5 + 16'h4 ) )
reg_scl <= 0 ;
else if( cstate == RACK && scl_pedge )
reg_scl <= 1 ;
else if( cnt_us % 2 == 0 )
reg_scl <= 0 ;
else if( cnt_us % 2 == 1 )
reg_scl <= 1 ;
else ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys || pedge_sack )
shift_wcnt <= 0 ;
else if( i2c_sda_m_t == 1'b0 && i2c_sda_m_i == 1'b0 )
shift_wcnt <= shift_wcnt ;
else if(( cstate == SADDR || cstate == RADDR || cstate == DADDR ) && scl_nedge )
shift_wcnt <= shift_wcnt + ~&shift_wcnt ;
else ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys )
reg_sda <= 1 ;
else if(( cstate == IDLE || cstate == START || cstate == SADDR || cstate == RADDR || cstate == AGAIN || cstate == DADDR ) && ( i2c_sda_m_i == 0 ))
reg_sda <= 1'bz ;
else if( reg_str == 1 && cnt_us == BUILD_TIME )
reg_sda <= 0 ;
else if( cstate == AGAIN && cnt_us == 16'h2e )
reg_sda <= 1 ;
else if( cstate == AGAIN && cnt_us == 16'h33 )
reg_sda <= 0 ;
else if( cstate == SADDR && scl_nedge )
begin
case ( shift_wcnt )
0 : reg_sda <= slave_addr[6];
1 : reg_sda <= slave_addr[5];
2 : reg_sda <= slave_addr[4];
3 : reg_sda <= slave_addr[3];
4 : reg_sda <= slave_addr[2];
5 : reg_sda <= slave_addr[1];
6 : reg_sda <= slave_addr[0];
7 : reg_sda <= 0 ; // WRITER
default : reg_sda <= 0 ;
endcase
end
else if( cstate == RADDR )
begin
case ( shift_wcnt )
0 : reg_sda <= regis_addr[7];
1 : reg_sda <= regis_addr[6];
2 : reg_sda <= regis_addr[5];
3 : reg_sda <= regis_addr[4];
4 : reg_sda <= regis_addr[3];
5 : reg_sda <= regis_addr[2];
6 : reg_sda <= regis_addr[1];
7 : reg_sda <= regis_addr[0] ;
default : reg_sda <= 0 ;
endcase
end
else if( cstate == DADDR && scl_nedge )
case ( shift_wcnt )
0 : reg_sda <= slave_addr[6];
1 : reg_sda <= slave_addr[5];
2 : reg_sda <= slave_addr[4];
3 : reg_sda <= slave_addr[3];
4 : reg_sda <= slave_addr[2];
5 : reg_sda <= slave_addr[1];
6 : reg_sda <= slave_addr[0];
7 : reg_sda <= 1 ; // READ
default : reg_sda <= 0 ;
endcase
else if( cstate == RDATA && shift_rcnt == 9 && scl_nedge && read_mode[2] == 0)
reg_sda <= 1 ;
else if( cstate == RDATA && shift_rcnt == 0 && scl_nedge && read_mode[2] == 1)
reg_sda <= 1'hz ;
else if( cstate == RDATA && shift_rcnt == 9 && scl_nedge && read_mode[2] == 1 && cnt_tags != read_mode + 1 )
reg_sda <= 0 ;
else if( cstate == RACK && scl_nedge )
reg_sda <= 0 ;
else if( cstate == STOP && cnt_stop == 4 )
reg_sda <= 1 ;
else ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys )
write_sack <= 0 ;
else if(( cstate != IDLE )&& ( i2c_sda_m_t == 0 && i2c_sda_m_i == 0 ))
write_sack <= 1 ;
else
write_sack <= 0 ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys || cstate == IDLE )
cnt_tags <= 0 ;
else if( cstate == RDATA && shift_rcnt == 9 && scl_pedge && read_mode[2] == 1 )
cnt_tags <= cnt_tags + ~&cnt_tags ;
else ;
end
// ========================================================
// READ
// ========================================================
always @ ( posedge clk_sys )
begin
if( rst_sys )
shift_rcnt <= 0 ;
else if(shift_rcnt == 9 && scl_pedge )
shift_rcnt <= 0 ;
else if( cstate == RDATA && scl_pedge && ( cnt_tags == 0 || read_mode[2] == 0 ))
shift_rcnt <= shift_rcnt + 1 ;
else if( cstate == RDATA && (cnt_tags > 0 && scl_nedge && read_mode[2] == 1 ))
shift_rcnt <= shift_rcnt + 1 ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys )
shift_dat <= 0 ;
else if( cstate == RDATA && scl_pedge )
shift_dat <= {shift_dat[6:0],i2c_sda_m_i};
else ;
end
// always @ ( posedge clk_sys )
// begin
// if( rst_sys || ( cstate == RDATA && shift_edge == 2 && scl_pedge ))
// shift_edge <= 0 ;
// else if( cstate == RDATA && shift_rcnt == 9 && scl_pedge )
// shift_edge <= shift_edge + 1 ;
// else if( cstate == RDATA && shift_rcnt == 0 && scl_pedge && shift_edge == 1 )
// shift_edge <= shift_edge + ~&shift_edge ;
// else ;
// end
always @ ( posedge clk_sys )
begin
if( rst_sys || cstate == IDLE )
cnt_stop <= 0 ;
else if( cstate == STOP && reg_scl && cnt_div == UCNT - 1 )
cnt_stop <= cnt_stop + ~&cnt_stop ;
else ;
end
assign i2c_scl_m_o = reg_scl ;
assign i2c_sda_m_o = reg_sda ;
assign i2c_sda_m_t = i2c_sda_m_i ;
// ========================================================
//
// ========================================================
always @ ( posedge clk_sys )
begin
if( rst_sys )
read_valid <= 0 ;
else if( cstate == RDATA && shift_rcnt == 9 )
read_valid <= 1 ;
else
read_valid <= 0 ;
end
always @ ( posedge clk_sys )
begin
if( rst_sys )
read_tdata <= 0 ;
else if( cstate == RDATA && shift_rcnt == 9 )
read_tdata <= shift_dat ;
else ;
end
// ========================================================
//
// ========================================================
edge_detect u0_edge_detect
(
//input
.clk ( clk_sys ),
.sig_in ( write_dena ),
//output
.p_edge ( write_start ),
.n_edge ( )
);
edge_detect u2_edge_detect
(
//input
.clk ( clk_sys ),
.sig_in ( write_sack ),
//output
.p_edge ( pedge_sack ),
.n_edge ( )
);
edge_detect u1_edge_detect
(
//input
.clk ( clk_sys ),
.sig_in ( reg_scl ),
//output
.p_edge ( scl_pedge ),
.n_edge ( scl_nedge )
);
edge_detect u3_edge_detect
(
//input
.clk ( clk_sys ),
.sig_in ( cstate == RDATA ),
//output
.p_edge ( sda_pedge ),
.n_edge ( sda_nedge )
);
endmodule
具体得仿真等到IIC-Slave读取代码书写完成后会发布上传