三大通信协议(2)IIC

目录

一、IIC通信协议简介

二、IIC通信时序

1.时序概括

2.24LC04  IIC通信时序

三、实例

       1.程序代码

       2.仿真验证:



一、IIC通信协议简介

        IIC(Inter-Integrated Circuit) ,简单说就是IC(芯片)之间通信的总线,IIC的意思是“完成集成电路或者功能单元之间信息交换的规范或协议”,该通信协议常应用与EEPROM的读写、芯片寄存器的配置等。I2C总线由数据线SDA和时钟线SCL构成通信线路,其通信原理是通过对SDA和SCL线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递,SCL用于传输时钟控制信号,SDA用于传输数据,在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。由于只有SDA一根线进行数据传输,因此,同一时刻只能用于发送或接收数据,故IIC通信协议属于半双工的通信方式。IIC协议的通信速度分为三种模式:1标准模式的传输速率为100Kb/s;2快速模式为400Kb/s;3高速模式为3.4Mb/s。

二、IIC通信时序

1.时序概括

   空闲状态:SDA和SCL全为高电平

   开始状态:当SCL为高电平时,SDA由高到低的跳变,表示数据传输的开始

   传输数据有效状态:在数据传输时,SCL时钟线上的信号为高电平期间,SDA数据线上的数据

                                   必须保持稳定,只有在SCL为低电平期间,SDA数据线上的高电平或低电

                                   平状态才允许变化。即:数据在SCL的上升沿来之前就需准备好,并在下

                                   降沿到来之前必须稳定。

  应答状态:发端在SDA上每发送一个字节的数据,就在SCL第9个时钟期间释放该数据线SDA,

                  收端反馈一个应答信号。应答信号为低电平时,规定为有效应答(ACK简称应答位),

                  表示收端已经成功地接收了该字节;应答信号为高电平时,规定为非应答(NACK),

                  表示收端接收该字节没有成功。

 结束状态:当SCL为高电平时,SDA由低到高的跳变,表示数据的结束。

三大通信协议(2)IIC

2.24LC04  IIC通信时序

     1.24LC04简介

      EEPROM即电可擦除可编程只读存储器,是一种常用的非易失性存储器(掉电后,数据不丢失)。24LC04,总容量是4Kbite(2*256*8bite),512B,因此需要的地址位宽是9位。通过IIC总线进行通信。下图是24LC04器件的原理图,其中A0,A1,A2是器件的地址,这里在硬件连接中全部设置为低电平,WP是写保护信号,SCL和SDA分别是上述提到的时钟信号和数据信号。

三大通信协议(2)IIC

     24LC04器件手册

     三大通信协议(2)IIC

    三大通信协议(2)IIC

     主要注意SCL的时钟范围以及循环写之间的时间间隔。

     2. 单字节写操作如下:

三大通信协议(2)IIC

      在数据传输期间,主机首先向从机发送7个bite的器件地址(高四位1010表示EEPROM,第三位由该器件的硬件电路决定),接着发送一个bite的写控制命令(即低电平),由从机反馈一个应答信号,如果主机检测到该应答信号有效,则进行下一个字节的发送。接着进入WORD ADDRESS,向从机发送写入的地址,如果地址超过两个字节,则需要发送两次,第一次发送高8位的地址,第二次发送低8位的地址,同样在每次发送完一个字节数据后,从机会反馈一个应答信号,如果该应答信号有效,则进入DATA,向从机发送写入的数据,发送完一个字节的数据后,从机响应一个应答信号,接着发送结束位,表示该次写操作完成。

     3. 页写操作如下:

三大通信协议(2)IIC

    页写操作需注意,每次连续写入的数据不能超过单页的最大容量,24LC04单页最大的容量是16B,因此,一次最多写入16个字节。

     4.单字节读操作如下:

三大通信协议(2)IIC

       在数据传输期间,主机首先向从机发送7个bite的器件地址,接着发送一个bite的写控制命令,由从机反馈一个应答信号,接着进行写地址的发送,同样地址字节发送结束后从机向主机发送一个应答信号,接着主机向从机发送7bite的器件地址加1个bite的读控制命令,从机反馈一个应答信号,接着从机向主机发送读出的一个字节的数据,主机向从机反馈一个非应答信号,接着发送结束位,表示该次单字节读操作完成。

      5.连续字节读操作如下:

    三大通信协议(2)IIC

        连续字节读操作和单字节读操作基本类似,只不过在数据读取的过程中从机向主机每发送一个字节读取的数据,主机回应一个应答信号,然后从机进行下一个字节数据的发送,直到主机发送非应答信号,接着发送一个结束信号,连续读字节操作完成。

三、实例

        该例子是学习了野火FPGA系列的教学视频,通过外部按键进行输入控制,向EEPROM中写入若干数据,然后再通过外部按键进行读操作,读取EEPROM中写入的数据。本篇文章仅进行EEPROM控制部分Verilog程序的编写,关于其他模块的,后续再进行更新。

        下图是整个系统模块的示意图:

三大通信协议(2)IIC

        下图是EEPROM控制模块的示意图:

三大通信协议(2)IIC

        输入信号分别是:系统时钟信号                  sys_clk

                                  系统复位信号                  sys_res

                                  数据读写开始信号           iic_start (由读写操作模块提供)

                                  写操作使能信号               wr_en   (由读写操作模块提供)

                                  写入的数据                      wr_data (由读写操作模块提供)

                                  读写数据的地址              byte_addr(由读写操作模块提供)

                                  读操作使能信号              rd_en       (由读写操作模块提供)

                                  地址位控制信号              addr_num(高电平代表16位地址,低电平代表8位地址)

        输出信号分别是:数据传输总线                     sda

                                     时钟传输总线                     scl

                                     读出来的数据                     rd_data

                                     一次读或写操作结束信号   iic_end

                                     用于读写操作模块的时钟   iic_clk(系统时钟50分频后得到)

     下图是该EEPROM控制模块的状态转换图:

三大通信协议(2)IIC

IDLE:空闲状态

START:开始状态(占据一个scl时钟周期)

ST_SLADDR:传输器件地址(占据8个scl时钟周期,包括4位的EEPROM通信地址+3位的24LC04 的器件地址+1位的读写控制指令)

ACK1:应答信号1

ST_ADDR16:传输高八位地址(占据8个scl时钟周期)

ACK2:应答信号2

ST_ADDR8:传输低八位地址(占据8个scl时钟周期)

ACK3:应答信号3

WR_DATA:传输写入的数据(由于只是单字节写,占据8个scl时钟周期)

ACK4:应答信号4

START2:开始状态2 (占据一个scl时钟周期)

ST2_SLADDR:传输器件地址(占据8个scl时钟周期,包括4位的EEPROM通信地址+3位的24LC04 的器件地址+1位的读写控制指令)

ACK5:应答信号5

RD_DATA:读出数据(由于只是单字节读,占据8个scl时钟周期)

N_ACK:主机给从机发送无应答信号(占据一个scl时钟周期)

END:结合状态

     下图是该模块的时序图:共分为三部分,iic_clk生成时序、写操作部分时序、读操作部分时序

     1.iic_clk生成时序

三大通信协议(2)IIC

   2.写操作部分时序

三大通信协议(2)IIC

   3.读操作部分时序

三大通信协议(2)IIC

1.程序代码

module  iic_control
(
     input                 sys_clk      ,
	 input                 sys_res      ,
	 input                 iic_start    ,
	 input                 wr_en        ,
	 input           [7:0] wr_data      ,
	 input           [15:0]byte_addr    ,
	 input                 rd_en        ,
	 input                 addr_num     ,//高电平代表2B地址
	 inout                 sda          ,
	 output     reg        scl          ,
	 output     reg  [7:0] rd_data      ,
	 output     reg        iic_end      ,
	 output     reg        iic_clk      
);
parameter         DEVICE_ADDR =    7'b1010000;
parameter         SYSCLK      =    50_000_000;
parameter         SCLCLK      =    250_000;//sda,scl的时钟
parameter         IICCLK      =    1_000_000;//用于读写控制模块的时钟(一般为SCL时钟的4倍)
parameter         CNT_IICCLK  =    (SYSCLK/IICCLK)/2;
                             
parameter         IDLE        =    4'd0     ;
parameter         START       =    4'd1     ;
parameter         ST_SLADDR   =    4'd2     ;
parameter         ACK1        =    4'd3     ;
parameter         ST_ADDR16   =    4'd4     ;
parameter         ACK2        =    4'd5     ;
parameter         ST_ADDR8    =    4'd6     ;
parameter         ACK3        =    4'd7     ;
parameter         WR_DATA     =    4'd8     ;
parameter         ACK4        =    4'd9     ;
parameter         END         =    4'd10    ;
                             
parameter         START2      =    4'd11    ;
parameter         ST2_SLADDR  =    4'd12    ;
parameter         ACK5        =    4'd13    ;
parameter         RD_DATA     =    4'd14    ;
parameter         N_ACK       =    4'd15    ; 





reg           [4:0]   cnt_iic_clk  ;


reg           [3:0]   state        ;
reg           [1:0]   cnt_sel      ;
reg                   en_cnt_sel   ; 
reg           [2:0]   cnt_sda_bite ;
reg                   ack          ;
reg                   sda_out      ;
reg                   sda_en       ;
wire                  sda_in       ;
reg           [7:0]   rd_data_reg  ;
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)
	    cnt_iic_clk <= 'd0;
	else if(cnt_iic_clk == CNT_IICCLK-1)
		cnt_iic_clk <= 'd0;
	else 
	    cnt_iic_clk <= cnt_iic_clk + 1'b1;
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)
        iic_clk <= 1'b0;
    else if(cnt_iic_clk == 	CNT_IICCLK-1)
		iic_clk <= ~iic_clk;
		
/
always@(posedge iic_clk or negedge sys_res)
    if(!sys_res)
	    en_cnt_sel <= 1'b0;
	else if((state == IDLE && iic_start == 1'b0)||(state == END && cnt_sda_bite == 'd3 && cnt_sel == 'd3))
	    en_cnt_sel <= 1'b0;
	else 
	    en_cnt_sel <= 1'b1;
always@(posedge iic_clk or negedge sys_res)
    if(!sys_res)
	    cnt_sel <= 'd0;
	else if(en_cnt_sel && cnt_sel == 'd3)
	    cnt_sel <= 'd0;
	else if(en_cnt_sel)
	    cnt_sel <= cnt_sel + 1'b1;
	else 
	    cnt_sel <= 'd0;
always@(posedge iic_clk or negedge sys_res)
    if(!sys_res)
        cnt_sda_bite <= 'd0;
    else if((state == ST_SLADDR || state == ST_ADDR16 || state == ST_ADDR8 ||state ==  WR_DATA || state == ST2_SLADDR || state == RD_DATA) && 	cnt_sel == 'd3 && cnt_sda_bite == 'd7 )
        cnt_sda_bite <= 'd0;    
    else if((state == ST_SLADDR || state == ST_ADDR16 || state == ST_ADDR8 ||state ==  WR_DATA || state == ST2_SLADDR || state == RD_DATA) &&  cnt_sel == 'd3)
	cnt_sda_bite <= cnt_sda_bite + 1'b1;
    else if(state == END && cnt_sel == 'd3 && cnt_sda_bite == 'd3)
	 cnt_sda_bite <= 'd0;
    else if(state == END && cnt_sel == 'd3)
	 cnt_sda_bite <= cnt_sda_bite + 1'b1;
    else if(state == IDLE || state == START || state == START2 || state == ACK1 || state == ACK2 || state == ACK3 || state == ACK4 || state == ACK5 || state == N_ACK)
         cnt_sda_bite <= 'd0;
    else 
         cnt_sda_bite <= cnt_sda_bite;	


always@(posedge iic_clk or negedge sys_res)
    if(!sys_res)
	    state <= IDLE;
	else 
	    case(state)
            IDLE      :  if(iic_start)
			                 state <= START;
            START     :  if(cnt_sel == 'd3)   
			                 state <= ST_SLADDR;
            ST_SLADDR :  if(cnt_sel == 'd3 && cnt_sda_bite == 'd7)
			                 state <= ACK1;
            ACK1      :  if(cnt_sel == 'd3 && ack == 1'b0 && addr_num == 1'b1)
			                 state <= ST_ADDR16;
						 else if(cnt_sel == 'd3 && ack == 1'b0)
						     state <= ST_ADDR8 ;
            ST_ADDR16 :  if(cnt_sel == 'd3 && cnt_sda_bite == 'd7)
                             state <= ACK2;			
            ACK2      :  if(cnt_sel == 'd3 && ack == 1'b0)
			                 state <= ST_ADDR8;
            ST_ADDR8  :  if(cnt_sel == 'd3 && cnt_sda_bite == 'd7)
			                 state <= ACK3;
            ACK3      :  if(cnt_sel == 'd3 && ack == 1'b0 && wr_en)
			                 state <= WR_DATA;
					     else if(cnt_sel == 'd3 && ack == 1'b0 && rd_en)
						     state <= START2;
            WR_DATA   :  if(cnt_sel == 'd3 && cnt_sda_bite == 'd7)
			                 state <= ACK4;
            ACK4      :  if(cnt_sel == 'd3 && ack == 1'b0)
			                 state <= END;
            START2    :  if(cnt_sel == 'd3)
			                 state <= ST2_SLADDR;
	    ST2_SLADDR:  if(cnt_sel == 'd3 && cnt_sda_bite == 'd7)
			                 state <= ACK5;
	    ACK5      :  if(cnt_sel == 'd3 && ack == 1'b0)
			                 state <= RD_DATA;
	    RD_DATA   :  if(cnt_sel == 'd3 && cnt_sda_bite == 'd7)
                             state <= N_ACK;
            N_ACK     :  if(cnt_sel == 'd3 )
                             state <= END;			
            END       :  if(cnt_sel == 'd3 && cnt_sda_bite == 'd3)
			                 state <= IDLE;
	       default:  state <= IDLE;  
	endcase
	     
/输出sda、scl
always@(posedge iic_clk or negedge sys_res)
    if(!sys_res)
        scl <= 1'b1;
    else if(state == IDLE ||(state == START && cnt_sel == 'd0)|| (state == END && cnt_sel != 'd0)) 
        scl <= 1'b1;
    else if(cnt_sel == 'd2 || cnt_sel == 'd0)
        scl <= ~scl;
    		
always@(*)
    case(state)
	        IDLE         :     sda_out <= 1'b1   ;
		START        :  if(cnt_sel == 'd0)
		                    sda_out <= 1'b1  ;
                                else 
                                    sda_out <= 1'b0  ;				
		ST_SLADDR    :  if(cnt_sda_bite <= 'd6)
		                    sda_out <= DEVICE_ADDR[6-cnt_sda_bite];
			        else if(cnt_sda_bite == 'd7)
				    sda_out <= 1'b0;//低电平代表写
		ST_ADDR16    :  sda_out <= byte_addr[15-cnt_sda_bite];
		ST_ADDR8     :  sda_out <= byte_addr[7-cnt_sda_bite];
		WR_DATA      :  sda_out <= wr_data[7-cnt_sda_bite];
		END          :  if(cnt_sel <= 'd3 && cnt_sda_bite == 'd0)
		                   sda_out <= 1'b0;
			        else
                                   sda_out <= 1'b1; 				
                START2       :  if(cnt_sel <= 'd1)
		                   sda_out <= 1'b1;
			        else
                                   sda_out <= 1'b0;	
		ST2_SLADDR   :  if(cnt_sda_bite <= 'd6)
		                    sda_out <= DEVICE_ADDR[6-cnt_sda_bite];
			        else if(cnt_sda_bite == 'd7)
				    sda_out <= 1'b1;//高电平代表读
		N_ACK        :  sda_out <= 1'b1;//主机给从机的响应信号
		RD_DATA,ACK1,ACK2,ACK3,ACK4,ACK5:sda_out <= 1'b1;  
	    default:;	             
	endcase 
always@(*)
    case(state)
	    IDLE,START,ST_SLADDR,ST_ADDR16,ST_ADDR8,WR_DATA,END,START2,ST2_SLADDR,N_ACK:
		    sda_en <= 1'b1;
	    ACK1,ACK2,ACK3,ACK4,ACK5,RD_DATA:     
                    sda_en <= 1'b0;
		default:;
    endcase 

always@(*)
    case(state)
	    ACK1,ACK2,ACK3,ACK4,ACK5:
		          if(cnt_sel == 'd0)
				      ack <= sda_in;
		          else 
				      ack <= ack;
		default:  ack <= 1'b1;
    endcase 	
	
assign   sda_in = sda;

assign   sda = (sda_en==1'b1)?sda_out:1'bz;

always@(posedge iic_clk or negedge sys_res)
    if(!sys_res)
	    iic_end <= 1'b0;
	else if(state == END && cnt_sel == 'd3 && cnt_sda_bite == 'd3)
	    iic_end <= 1'b1;
	else 
	    iic_end <= 1'b0;
always@(*)
   case(state)
        IDLE:
            rd_data_reg <= 'd0;
        RD_DATA:
	   if(cnt_sel == 'd2)
              rd_data_reg[7-cnt_sda_bite] <= sda_in; 		
        default:rd_data_reg <= rd_data_reg;           
   endcase 
 
always@(posedge iic_clk or negedge sys_res)
    if(!sys_res)
         rd_data <= 'd0;
    else if(state == RD_DATA && cnt_sel == 'd3 && cnt_sda_bite == 'd7)		 
         rd_data <= rd_data_reg;
		 
endmodule

2.仿真验证:

`timescale 1ns/1ns
module tb_iic_control();
reg         clk                  ;
reg         res                  ;
reg         iic_start            ;
reg         wr_en                ;
reg         rd_en                ;
wire [7:0]  wr_data              ;
wire [15:0] byte_addr            ;
//wire        addr_num             ;
wire        sda                  ;
wire        scl                  ;
wire [7:0]  rd_data              ;
wire        iic_end              ;
wire        iic_clk              ;
reg         flag_wr              ;

initial  begin
    wr_en <= 1'b0;
	rd_en <= 1'b0;
	flag_wr <= 1'b0; 
    clk <= 1'b0;
	res <= 1'b0;
	#200 res <= 1'b1; 
end 
initial begin
    iic_start <= 1'b0;
	#500 iic_start <= 1'b1;
	#1000 iic_start <= 1'b0;
	#1000_000 iic_start <= 1'b1;
	#1000 iic_start <= 1'b0;
end 
always #10 clk <= ~clk;
always@(posedge iic_clk)
    if(iic_start == 1'b1&&flag_wr == 1'b0)begin
	    wr_en <= 1'b1;
		rd_en <= 1'b0;
		flag_wr <= flag_wr;
	end 
    else if(iic_start == 1'b1&&flag_wr == 1'b1)begin
	    wr_en <= 1'b0;
		rd_en <= 1'b1;
		flag_wr <= flag_wr;
	end 
	else if(iic_end == 1'b1)begin
        wr_en <= 1'b0;
        rd_en <= 1'b0;
        flag_wr <= ~flag_wr;		
	end 
	
assign  wr_data = 8'haa;
assign  byte_addr = 16'h01_02;
//assign  addr_num = 1'b1;



iic_control u_iic_control
(
     .sys_clk     (clk) ,
	 .sys_res     (res) ,
	 .iic_start   (iic_start) ,
	 .wr_en       (wr_en    ) ,
	 .wr_data     (wr_data   ) ,
	 .byte_addr   (byte_addr ) ,
	 .rd_en       (rd_en) ,
	 .addr_num    (1'b1 ) ,//高电平代表2B地址
	 .sda         (sda      ) ,
	 .scl         (scl      ) ,
	 .rd_data     (rd_data  ) ,
	 .iic_end     (iic_end  ) ,
	 .iic_clk     (iic_clk  ) 
);   

M24LC64  M24lc64_inst
(
    .A0     (1'b0       ),  //器件地址
    .A1     (1'b0       ),  //器件地址
    .A2     (1'b0       ),  //器件地址
    .WP     (1'b0       ),  //写保护信号,高电平有效
    .RESET  (~res    ),  //复位信号,高电平有效

    .SDA    (sda        ),  //串行数据
    .SCL    (scl        )   //串行时钟
);
               
endmodule 

  下边是一个24LC64的iic通信测试模板文件

`timescale 1ns/10ps

module M24LC64 (A0, A1, A2, WP, SDA, SCL, RESET);

   input                A0;                             // chip select bit
   input                A1;                             // chip select bit
   input                A2;                             // chip select bit

   input                WP;                             // write protect pin

   inout                SDA;                            // serial data I/O
   input                SCL;                            // serial data clock

   input                RESET;                          // system reset


// *******************************************************************************************************
// **   DECLARATIONS                                                                                    **
// *******************************************************************************************************

   reg                  SDA_DO;                         // serial data - output
   reg                  SDA_OE;                         // serial data - output enable

   wire                 SDA_DriveEnable;                // serial data output enable
   reg                  SDA_DriveEnableDlyd;            // serial data output enable - delayed

   wire [02:00]         ChipAddress;                    // hardwired chip address

   reg  [03:00]         BitCounter;                     // serial bit counter

   reg                  START_Rcvd;                     // START bit received flag
   reg                  STOP_Rcvd;                      // STOP bit received flag
   reg                  CTRL_Rcvd;                      // control byte received flag
   reg                  ADHI_Rcvd;                      // byte address hi received flag
   reg                  ADLO_Rcvd;                      // byte address lo received flag
   reg                  MACK_Rcvd;                      // master acknowledge received flag

   reg                  WrCycle;                        // memory write cycle
   reg                  RdCycle;                        // memory read cycle

   reg  [07:00]         ShiftRegister;                  // input data shift register

   reg  [07:00]         ControlByte;                    // control byte register
   wire                 RdWrBit;                        // read/write control bit

   reg  [12:00]         StartAddress;                   // memory access starting address
   reg  [04:00]         PageAddress;                    // memory page address

   reg  [07:00]         WrDataByte [0:31];              // memory write data buffer
   wire [07:00]         RdDataByte;                     // memory read data

   reg  [15:00]         WrCounter;                      // write buffer counter

   reg  [04:00]         WrPointer;                      // write buffer pointer
   reg  [12:00]         RdPointer;                      // read address pointer

   reg                  WriteActive;                    // memory write cycle active

   reg  [07:00]         MemoryBlock [0:8191];           // EEPROM data memory array

   integer              LoopIndex;                      // iterative loop index

   integer              tAA;                            // timing parameter
   integer              tWC;                            // timing parameter


// *******************************************************************************************************
// **   INITIALIZATION                                                                                  **
// *******************************************************************************************************

//----------------------------
//------写数据间隔改动----------
   initial tAA = 1000;                                   // SCL to SDA output delay
   initial tWC = 500;                                   // memory write cycle time

//   initial tAA = 900;                                   // SCL to SDA output delay
//   initial tWC = 5000000;                               // memory write cycle time
	
	
   initial begin
      SDA_DO = 0;
      SDA_OE = 0;
   end

   initial begin
      START_Rcvd = 0;
      STOP_Rcvd  = 0;
      CTRL_Rcvd  = 0;
      ADHI_Rcvd  = 0;
      ADLO_Rcvd  = 0;
      MACK_Rcvd  = 0;
   end

   initial begin
      BitCounter  = 0;
      ControlByte = 0;
   end

   initial begin
      WrCycle = 0;
      RdCycle = 0;

      WriteActive = 0;
   end

   assign ChipAddress = {A2,A1,A0};


// *******************************************************************************************************
// **   CORE LOGIC                                                                                      **
// *******************************************************************************************************
// -------------------------------------------------------------------------------------------------------
//      1.01:  START Bit Detection
// -------------------------------------------------------------------------------------------------------

   always @(negedge SDA) begin
      if (SCL == 1) begin
         START_Rcvd <= 1;
         STOP_Rcvd  <= 0;
         CTRL_Rcvd  <= 0;
         ADHI_Rcvd  <= 0;
         ADLO_Rcvd  <= 0;
         MACK_Rcvd  <= 0;

         WrCycle <= #1 0;
         RdCycle <= #1 0;

         BitCounter <= 0;
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.02:  STOP Bit Detection
// -------------------------------------------------------------------------------------------------------

   always @(posedge SDA) begin
      if (SCL == 1) begin
         START_Rcvd <= 0;
         STOP_Rcvd  <= 1;
         CTRL_Rcvd  <= 0;
         ADHI_Rcvd  <= 0;
         ADLO_Rcvd  <= 0;
         MACK_Rcvd  <= 0;

         WrCycle <= #1 0;
         RdCycle <= #1 0;

         BitCounter <= 10;
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.03:  Input Shift Register
// -------------------------------------------------------------------------------------------------------

   always @(posedge SCL) begin
      ShiftRegister[00] <= SDA;
      ShiftRegister[01] <= ShiftRegister[00];
      ShiftRegister[02] <= ShiftRegister[01];
      ShiftRegister[03] <= ShiftRegister[02];
      ShiftRegister[04] <= ShiftRegister[03];
      ShiftRegister[05] <= ShiftRegister[04];
      ShiftRegister[06] <= ShiftRegister[05];
      ShiftRegister[07] <= ShiftRegister[06];
   end

// -------------------------------------------------------------------------------------------------------
//      1.04:  Input Bit Counter
// -------------------------------------------------------------------------------------------------------

   always @(posedge SCL) begin
      if (BitCounter < 10) BitCounter <= BitCounter + 1;
   end

// -------------------------------------------------------------------------------------------------------
//      1.05:  Control Byte Register
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (START_Rcvd & (BitCounter == 8)) begin
         if (!WriteActive & (ShiftRegister[07:01] == {4'b1010,ChipAddress[02:00]})) begin
            if (ShiftRegister[00] == 0) WrCycle <= 1;
            if (ShiftRegister[00] == 1) RdCycle <= 1;

            ControlByte <= ShiftRegister[07:00];

            CTRL_Rcvd <= 1;
         end

         START_Rcvd <= 0;
      end
   end

   assign RdWrBit = ControlByte[00];

// -------------------------------------------------------------------------------------------------------
//      1.06:  Byte Address Register
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (CTRL_Rcvd & (BitCounter == 8)) begin
         if (RdWrBit == 0) begin
            StartAddress[12:08] <= ShiftRegister[04:00];
            RdPointer[12:08]    <= ShiftRegister[04:00];

            ADHI_Rcvd <= 1;
         end

         WrCounter <= 0;
         WrPointer <= 0;

         CTRL_Rcvd <= 0;
      end
   end

   always @(negedge SCL) begin
      if (ADHI_Rcvd & (BitCounter == 8)) begin
         if (RdWrBit == 0) begin
            StartAddress[07:00] <= ShiftRegister[07:00];
            RdPointer[07:00]    <= ShiftRegister[07:00];

            ADLO_Rcvd <= 1;
         end

         WrCounter <= 0;
         WrPointer <= 0;

         ADHI_Rcvd <= 0;
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.07:  Write Data Buffer
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (ADLO_Rcvd & (BitCounter == 8)) begin
         if (RdWrBit == 0) begin
            WrDataByte[WrPointer] <= ShiftRegister[07:00];

            WrCounter <= WrCounter + 1;
            WrPointer <= WrPointer + 1;
         end
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.08:  Acknowledge Generator
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (!WriteActive) begin
         if (BitCounter == 8) begin
            if (WrCycle | (START_Rcvd & (ShiftRegister[07:01] == {4'b1010,ChipAddress[02:00]}))) begin
               SDA_DO <= 0;
               SDA_OE <= 1;
            end 
         end
         if (BitCounter == 9) begin
            BitCounter <= 0;

            if (!RdCycle) begin
               SDA_DO <= 0;
               SDA_OE <= 0;
            end
         end
      end
   end 

// -------------------------------------------------------------------------------------------------------
//      1.09:  Acknowledge Detect
// -------------------------------------------------------------------------------------------------------

   always @(posedge SCL) begin
      if (RdCycle & (BitCounter == 8)) begin
         if ((SDA == 0) & (SDA_OE == 0)) MACK_Rcvd <= 1;
      end
   end

   always @(negedge SCL) MACK_Rcvd <= 0;

// -------------------------------------------------------------------------------------------------------
//      1.10:  Write Cycle Timer
// -------------------------------------------------------------------------------------------------------

   always @(posedge STOP_Rcvd) begin
      if (WrCycle & (WP == 0) & (WrCounter > 0)) begin
         WriteActive = 1;
         #(tWC);
         WriteActive = 0;
      end
   end

   always @(posedge STOP_Rcvd) begin
      #(1.0);
      STOP_Rcvd = 0;
   end

// -------------------------------------------------------------------------------------------------------
//      1.11:  Write Cycle Processor
// -------------------------------------------------------------------------------------------------------

   always @(negedge WriteActive) begin
      for (LoopIndex = 0; LoopIndex < WrCounter; LoopIndex = LoopIndex + 1) begin
         PageAddress = StartAddress[04:00] + LoopIndex;

         MemoryBlock[{StartAddress[12:05],PageAddress[04:00]}] = WrDataByte[LoopIndex[04:00]];
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.12:  Read Data Multiplexor
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (BitCounter == 8) begin
         if (WrCycle & ADLO_Rcvd) begin
            RdPointer <= StartAddress + WrPointer + 1;
         end
         if (RdCycle) begin
            RdPointer <= RdPointer + 1;
         end
      end
   end

   assign RdDataByte = MemoryBlock[RdPointer[12:00]];

// -------------------------------------------------------------------------------------------------------
//      1.13:  Read Data Processor
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (RdCycle) begin
         if (BitCounter == 8) begin
            SDA_DO <= 0;
            SDA_OE <= 0;
         end
         else if (BitCounter == 9) begin
            SDA_DO <= RdDataByte[07];

            if (MACK_Rcvd) SDA_OE <= 1;
         end
         else begin
            SDA_DO <= RdDataByte[7-BitCounter];
         end
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.14:  SDA Data I/O Buffer
// -------------------------------------------------------------------------------------------------------

   bufif1 (SDA, 1'b0, SDA_DriveEnableDlyd);

   assign SDA_DriveEnable = !SDA_DO & SDA_OE;
   always @(SDA_DriveEnable) SDA_DriveEnableDlyd <= #(tAA) SDA_DriveEnable;


// *******************************************************************************************************
// **   DEBUG LOGIC                                                                                     **
// *******************************************************************************************************
// -------------------------------------------------------------------------------------------------------
//      2.01:  Memory Data Bytes
// -------------------------------------------------------------------------------------------------------

   wire [07:00] MemoryByte_000 = MemoryBlock[00];
   wire [07:00] MemoryByte_001 = MemoryBlock[01];
   wire [07:00] MemoryByte_002 = MemoryBlock[02];
   wire [07:00] MemoryByte_003 = MemoryBlock[03];
   wire [07:00] MemoryByte_004 = MemoryBlock[04];
   wire [07:00] MemoryByte_005 = MemoryBlock[05];
   wire [07:00] MemoryByte_006 = MemoryBlock[06];
   wire [07:00] MemoryByte_007 = MemoryBlock[07];
   wire [07:00] MemoryByte_008 = MemoryBlock[08];
   wire [07:00] MemoryByte_009 = MemoryBlock[09];
   wire [07:00] MemoryByte_00A = MemoryBlock[10];
   wire [07:00] MemoryByte_00B = MemoryBlock[11];
   wire [07:00] MemoryByte_00C = MemoryBlock[12];
   wire [07:00] MemoryByte_00D = MemoryBlock[13];
   wire [07:00] MemoryByte_00E = MemoryBlock[14];
   wire [07:00] MemoryByte_00F = MemoryBlock[15];

// -------------------------------------------------------------------------------------------------------
//      2.02:  Write Data Buffer
// -------------------------------------------------------------------------------------------------------

   wire [07:00] WriteData_00 = WrDataByte[00];
   wire [07:00] WriteData_01 = WrDataByte[01];
   wire [07:00] WriteData_02 = WrDataByte[02];
   wire [07:00] WriteData_03 = WrDataByte[03];
   wire [07:00] WriteData_04 = WrDataByte[04];
   wire [07:00] WriteData_05 = WrDataByte[05];
   wire [07:00] WriteData_06 = WrDataByte[06];
   wire [07:00] WriteData_07 = WrDataByte[07];
   wire [07:00] WriteData_08 = WrDataByte[08];
   wire [07:00] WriteData_09 = WrDataByte[09];
   wire [07:00] WriteData_0A = WrDataByte[10];
   wire [07:00] WriteData_0B = WrDataByte[11];
   wire [07:00] WriteData_0C = WrDataByte[12];
   wire [07:00] WriteData_0D = WrDataByte[13];
   wire [07:00] WriteData_0E = WrDataByte[14];
   wire [07:00] WriteData_0F = WrDataByte[15];

   wire [07:00] WriteData_10 = WrDataByte[16];
   wire [07:00] WriteData_11 = WrDataByte[17];
   wire [07:00] WriteData_12 = WrDataByte[18];
   wire [07:00] WriteData_13 = WrDataByte[19];
   wire [07:00] WriteData_14 = WrDataByte[20];
   wire [07:00] WriteData_15 = WrDataByte[21];
   wire [07:00] WriteData_16 = WrDataByte[22];
   wire [07:00] WriteData_17 = WrDataByte[23];
   wire [07:00] WriteData_18 = WrDataByte[24];
   wire [07:00] WriteData_19 = WrDataByte[25];
   wire [07:00] WriteData_1A = WrDataByte[26];
   wire [07:00] WriteData_1B = WrDataByte[27];
   wire [07:00] WriteData_1C = WrDataByte[28];
   wire [07:00] WriteData_1D = WrDataByte[29];
   wire [07:00] WriteData_1E = WrDataByte[30];
   wire [07:00] WriteData_1F = WrDataByte[31];


// *******************************************************************************************************
// **   TIMING CHECKS                                                                                   **
// *******************************************************************************************************

   wire TimingCheckEnable = (RESET == 0) & (SDA_OE == 0);
   wire StopTimingCheckEnable = TimingCheckEnable && SCL;
	
//--------------------------------
//-------仿真时时序约束需改动--------
//--------------------------------
   specify
      specparam
         tHI = 600,                                     // SCL pulse width - high
//         tLO = 1300,                                    // SCL pulse width - low
         tLO = 600, 
			tSU_STA = 600,                                 // SCL to SDA setup time
         tHD_STA = 600,                                 // SCL to SDA hold time
         tSU_DAT = 100,                                 // SDA to SCL setup time
         tSU_STO = 600,                                 // SCL to SDA setup time
         tSU_WP = 600,                                  // WP to SDA setup time
         tHD_WP = 1300,                                 // WP to SDA hold time
//         tBUF = 1300;                                   // Bus free time
         tBUF = 600; 
			
      $width (posedge SCL, tHI);
      $width (negedge SCL, tLO);

      $width (posedge SDA &&& SCL, tBUF);

      $setup (posedge SCL, negedge SDA &&& TimingCheckEnable, tSU_STA);
      $setup (SDA, posedge SCL &&& TimingCheckEnable, tSU_DAT);
      $setup (posedge SCL, posedge SDA &&& TimingCheckEnable, tSU_STO);
      $setup (WP, posedge SDA &&& StopTimingCheckEnable, tSU_WP);

      $hold  (negedge SDA &&& TimingCheckEnable, negedge SCL, tHD_STA);
      $hold  (posedge SDA &&& StopTimingCheckEnable, WP, tHD_WP);
   endspecify

endmodule

  仿真结果:

三大通信协议(2)IIC

三大通信协议(2)IIC

      由于24LC04仿真模型的问题,仿真结果中读出的数据并不理想,所有的高电平默认为高阻态,后期将整个系统下载到开发板上,在signal tap中抓取相应的信号,验证程序的可行性。

四、总结

    初次创作,难免文章中存在错误,希望读者能够及时纠正并给予私信,望大家共同进步!


上一篇:SpringBoot:自定义注解实现后台接收Json参数


下一篇:ROS2学习之旅(18)——在类中使用参数(C++)