Understanding of IIC communication protocol

Publisher:SerendipitySoulLatest update time:2019-04-08 Source: eefocusKeywords:IIC Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

 Some time ago, I have been adjusting the SDRAM and VGA drivers. After a long time and referring to a lot of materials, I finally figured out my ideas. However, since I don’t have the relevant hardware circuits, I have put it aside for the time being. Let’s get back to the topic and look at the communication between IICs first.

   

First of all, IIC communication, UART, and SPI are collectively referred to as serial interface communication, but there are still differences between them, such as the negative level logic of UART, and UART communication does not require a clock, only a specific baud rate is required. SPI and IIC can both have one host and multiple slaves, but IIC is suitable for short-distance transmission, such as inter-chip communication, camera configuration and other scenarios.

   

To get IIC, first look at the hardware interface of IIC:


Write the picture description here


As shown in the figure, we know that one IIC host can connect multiple slaves, so the address lines A2, A1, and A0 can implement the chip select function. The function of the WP pin is that when WP is floating or grounded, it means that the EEPROM can be read and written at this time. When WP is connected to the power supply, it can only be read but not written.


    The SCL and SDL pins must be pulled up, otherwise the driving capability is insufficient and normal IIC communication cannot be performed.


    OK, the hardware interface has been clearly introduced, so now we can start to look at the protocol.

    First of all, IIC is divided into byte reading and writing and page reading and writing. First, let's look at the byte reading and writing protocol:


Write the picture description here 

 

As shown in the figure above, if we want to write a byte of data to the EEPROM, we have to follow the following steps: 

1. Start signal - when SCLK is high, the SDA signal is pulled low (from 1 to 0). 

2. Control byte - that is, the device address, which is the EEPROM you are operating. 

3.ACK signal - sent by the slave and received by the host, so at this stage, sda_link must be set to 0, that is, to read this response signal, so during the high point of SCLK. 

4. Byte address——that is, which address in a certain EEPROM. 

5.ACK signal - same as above. 

6. Data signal - that is, the 8-bit data you write to a certain address. 

7.ACK signal——same as above. 

8. End signal - During the high level period of SCLK, pull up the SDA signal to indicate the end of communication.


Let's look at the reading sequence: 


 Write the picture description here 


It can be seen from the above figure that the previous processing method of the read timing is the same as that of the write timing. The difference is that after the third ACK signal comes, if it is a read, there will be another start signal, followed by the read device address, then the response, then the read data, and then a NO ACK signal is sent during the low level of SCLK. Remember that this signal is sent by the host, followed by an end signal.


From the above read and write timing, we can know that the start of communication jumps during the high level of SCLK. This determines that our other signal jumps are all on the falling edge of SCLK. The data is stable during the high level of SCLK, which is suitable for reading (that is, low level changes data, high level collects data). 


The specific process is as follows: 

First, a delay is required for the board to be initialized after powering on. You can use a counter to determine the specific delay. 

code show as below: 

reg [6:0] hadware_initial_delay;


wire hadware_initial_delay_done;


always@(posedge clk or negedge rst_n) 

if(!rst_n) 

hadware_initial_delay<=7'd0; 

else 

if(hadware_initial_delay<=7'd49) 

hadware_initial_delay<=hadware_initial_delay+1; 

else 

hadware_initial_delay<=hadware_initial_delay;


assign hadware_initial_delay_done=(hadware_initial_delay==7'd50)?1'b1:1'b0;


OK, we need to know that the IIC rate is generally a few hundred KH and our system clock is 50M, so we need to divide the frequency: 

code show as below: 

reg [8:0] sclk_cnt;


always@(posedge clk or negedge rst_n) 

if(!rst_n) 

sclk_cnt<=9'd0; 

else 

if(hadware_initial_delay_done) 

begin 

if(sclk_cnt<9'd499) 

sclk_cnt<=sclk_cnt+1; 

else 

sclk_cnt<=0; 

end


assign sclk=(sclk_cnt<=9'd249)?1'b1:1'b0;


OK, we know that data is collected during the high level of SCLK, and data is changed during the low level. Of course, this "period" must be the best in the middle of the clock edge. After all, it is easier to meet the setup time and hold time, and it is very stable. 

The specific code is as follows: 

wire sclk_posedge_middle=(sclk_cnt==9'd124)?1'b1:1'b0; 

wire sclk_negedge_middle=(sclk_cnt==9'd374)?1'b1:1'b0;


OK, there are so many processes defined for reading and writing, of course a state machine is needed to handle it, and the variables are defined as follows: 

parameter IDLE = 4'd0; 

parameter START1 = 4'd1; 

parameter ADD1 = 4'd2; 

parameter ACK1 = 4'd3 ; 

parameter ADD2 = 4'd4; 

parameter ACK2 = 4'd5 ; 

parameter DATA = 4'd6; 

parameter ACK3 = 4'd7; 

parameter STOP1 = 4'd8; 

parameter START2 = 4'd9; 

parameter ADD3 = 4'd10; 

parameter ACK4 = 4'd11; 

parameter DATA_READ = 4'd12; 

parameter NO_ACK = 4'd13; 

parameter STOP2 = 4'd14;


OK, here is another macro definition, assuming that the addresses and data to be written are these.


define DEVICE_READ 8'b1010_0001 

define DEVICE_WRITE 8'b1010_0000 

define WRITE_DATA 8'b0001_0001 

define BYTE_ADDR 8'b0000_0011


SDA bidirectional port, remember this, generally do it like this; 

reg sda_link; 

reg sda_out_r;


assign sda=sda_link?sda_out_r:1'bz;


When used as an output, right, sda_link is pulled high, and when used as an input, the input is high impedance. 

The processes are as follows: 

reg [3:0] current_state; 

//reg [3:0] next_state; 

reg [7:0] db_r; 

reg [3:0] num;


reg [7:0] data_out_reg; 

always@(posedge clk or negedge rst_n) 

if(!rst_n) 

begin 

sda_link<=0; 

db_r<=0; 

num<=0; 

current_state<=IDLE; 

sda_out_r<=0; 

data_out_reg<=8'b0; 

end 

else 

begin 

case(current_state) 

IDLE:begin 

sda_out_r<=1; 

sda_link<=1; 

if(!sw1_r||!sw2_r) 

current_state<=START1; 

else 

current_state<=IDLE; 

end


  START1:if(sclk_posedge_middle)

           begin

           sda_out_r<=0;

           db_r<=`DEVICE_WRITE;

           current_state<=ADD1;

           end

           else

           current_state<=START1;

  ADD1:

        if(sclk_negedge_middle)

        begin

              if(num==4'd8)  

                begin                 

                 sda_link<=0;

                 num<=0;

                 current_state<=ACK1;

                 sda_out_r<=1;

                 end

              else

                 begin

                 current_state<=ADD1;

                 sda_out_r<=db_r[7-num];

                 num<=num+1;

                 end

        end

        else

        current_state<=ADD1;

   ACK1:

         if(sclk_posedge_middle)


                                         // begin   

                                        // if(!sda)

                                           // begin

            begin // */

           current_state<=ADD2;

            db_r<=`BYTE_ADDR;

            end

            else

            current_state<=ACK1;



    ADD2:begin

          sda_link<=1;

           if(sclk_negedge_middle)begin

              if(num==4'd8)

                 begin

                 sda_link<=0;

                 current_state<=ACK2;

                 num<=4'd0;     

                 sda_out_r<=1;                   

                 end

              else

                begin

                num<=num+1;

                current_state<=ADD2;

                sda_out_r<=db_r[7-num];

                end

                                  end

           else

                current_state<=ADD2;

         end


   ACK2:

        if(sclk_posedge_middle)

        ////begin

            //if(!sda)

            begin

                 if(!sw1_r)

                     begin

                  db_r<=`WRITE_DATA;

                    current_state<=DATA;

                  end

                 else

                  if(!sw2_r)

                     begin

                       current_state<=START2;

                       sda_out_r<=1;

                     end          

               end                 

            else

               current_state<=ACK2;



    DATA: begin

          sda_link<=1;

          if(sclk_negedge_middle)

            begin

              if(num==4'd8)

               begin

                  num<=4'd0;

                  current_state<=ACK3;

                       sda_out_r<=1;

                       sda_link<=0;

               end

           else

                    begin

                   num<=num+1;

                    current_state<=DATA;

                    sda_out_r<=db_r[7-num];

                    end

              end

        else

          current_state<=DATA;

              end


     ACK3: if(sclk_posedge_middle)

         // begin

           // if(!sda)

                current_state<=STOP1;

          // end


     STOP1:

            begin

             sda_link<=1;

             sda_out_r<=0;

              if(sclk_posedge_middle)

                begin

                sda_out_r<=1;

                 if(sw1_r)

                // If you don't wait for it to be released before restoring the initial state, then once you restore the initial state SW1_r will be low, and it will start writing again, so as to avoid repeated writing of data.

                   current_state<=IDLE;

                    else

                     current_state<=STOP1;

                end 

                else

                current_state<=STOP1;

            end


     START2:begin

            sda_link<=1;

            if(sclk_posedge_middle)

              begin

              sda_out_r<=0;

              sda_link<=1;

              db_r<=`DEVICE_READ;

              current_state<=ADD3;

              end

            end

     ADD3: begin

           if(sclk_negedge_middle)

              begin

               if(num==4'd8)

                  begin

                   num<=0;

                   sda_link<=0;

                   sda_out_r<=1;

                   current_state<=ACK4;

                  end

               else 

                    begin

                    num<=num+1;

                    sda_out_r<=db_r[7-num];

                    current_state<=ADD3;

                    end


               end

           else

             current_state<=ADD3;


            end


     ACK4:

         if(sclk_posedge_middle)

    // begin

           // if(!sda)

              current_state<=DATA_READ;

             else

              current_state<=ACK4;

         // end



     DATA_READ:

                 begin

                  sda_link<=0;

                   if(sclk_posedge_middle)

                     begin

                     if(num==4'd8)

                        begin

                         sda_link<=1;

                         sda_out_r<=1;

                         current_state<=NO_ACK;

                         num<=4'd0;

                        end

                     else

                         begin

                  num<=num+1;

                         current_state<=DATA_READ;

                         data_out_reg[7-num]<=sda;

                         end

                     end

                 end




        NO_ACK:

             if(sclk_negedge_middle)

               begin

                sda_out_r<=1;

                current_state<=STOP2;

               end

             else

                current_state<=NO_ACK;


        STOP2: begin

           sda_out_r<=0;

            sda_link<=1;

             if(sclk_posedge_middle)

               begin

                sda_out_r<=1;

                current_state<=IDLE;


                end

             else

               current_state<=STOP2;

               end

     default:current_state<=IDLE;

     endcase


     end


assign data_out=data_out_reg;


endmodule


The simulation results are as follows: 


Write the picture description here

OK, done. The output can of course be connected to a digital tube, LED, etc. to show whether it is correct.

Keywords:IIC Reference address:Understanding of IIC communication protocol

Previous article:The difference between master-slave communication between standard IIC and IO port simulated IIC
Next article:Understanding of IIC during GPIO simulation of IIC

Recommended ReadingLatest update time:2024-11-16 14:32

Very detailed comments 51 single chip IIC source program
I wrote the IIC program myself. Since I am a beginner, there are still some references. However, all the programs are hand-typed, and each line has detailed comments. The microcontroller source program is as follows: #include reg52.h #include iic.h sbit IIC_SDA=P2^6; //Declare that the data line of the IIC bus i
[Microcontroller]
51 single chip microcomputer STC89C52 iic
SCL BIT P2.1    SDA BIT P2.0   ORG 0000H   LJMP MAIN   ORG 0050H   MAIN:   MOV SP,#30H   MOV P1,#0FFH ; Turn off LED   MOV R6,#2; R6 stores the internal address to be written in AT24C02, which is used to pass parameters   MOV R7,#55H; R7 stores the data to be written and is used to pass parameters   LCALL i
[Microcontroller]
Use of IIC library of stm8
I. Introduction stm8 is a low-power MCU chip, which has the advantages of stm32 library functions and rich resources. It also has the characteristics of low price and low power consumption. In some projects, it can play a good role. Here I will introduce the IIC hardware library function driver code and implementation
[Microcontroller]
STM32 programming software simulates IIC to read and write 24C02 memory
1. Main program #include "stm32f10x_conf.h" #include "led.h" #include "key.h" #include "usart.h" #include "lcd.h" #include "24cxx.h" #include "myiic.h" #include "delay.h" u8 tabel ="write data is:0 1 2 3 4 5 6 7 8 9 "; /* Program function: STM32 implements 24C02 read and write operations.           When
[Microcontroller]
MCU iic+pca9685 drive control 9-way servo source program
Using the iic communication of the stc12c5a60s2 microcontroller to drive the pca9685, because 9 servos are used, there is no need to short the solder joints on the driver board. The driver's scl and sda are connected to the corresponding microcontrollers. Vcc is connected to 3.3v, and then v+ and gnd are given 5v. Plu
[Microcontroller]
MCU iic+pca9685 drive control 9-way servo source program
MSP430F249 IIC write and Uart send
//******************************************************************************/ #include msp430.h //Note: There must be a delay between two transmissions, otherwise it cannot be sent again, serial port transmission format: unsigned char PTxData ; // Pointer to TX data unsigned char pHead; unsigned char pTail; un
[Microcontroller]
PIC IIC read and write
Using C language to implement 24LC256 reading and writing (non-software simulation method) #include PIC1687X.H unsigned  CHAR  i=0; unsigned  CHAR  receive=0x00; void i2c_start() {         SEN=1;      //Start         do{         }while(SSPIF==0);         SSPIF=0; } void i2c_stop() {         PEN=1;   
[Microcontroller]
Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号