基于FPGA的IIC通讯-MASTER接收篇

基于FPGA的IIC通讯主机读实现

(三)、主机读

        上一篇文章完成了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读取代码书写完成后会发布上传

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值