"Linux Driver: I2C Protocol Analysis"

I. Introduction

The I2C bus supports short-distance communication between devices and is used for data transmission between the processor and some peripheral devices. It only needs two signal lines to complete data transmission. Before analyzing the I2C bus driver, you need to understand the i2c protocol.

Second, the hardware circuit

The hardware connection is generally shown in the figure below. There are two signal lines SDA and SCL on the main control MCU. Multiple devices can be connected at the same time. SDA and SCL must be connected to pull-up resistors, and the resistance value is generally 4.7K. It can be seen from the figure that I2C is in the master-slave mode, the one that sends out SCL is the master, and the others are slaves. Two signal lines, one clock line, and one data line determine that it is a half-duplex communication mode, and can only transmit data in one direction at the same time.
insert image description here

Three, data transmission

The I2C protocol divides the transmitted messages into two types of frames: an address frame and one or more data frames. The address frame indicates which slave device the master will communicate with. During data transmission, the data on the SDA line must remain stable during the high level of SCL, and the transition between high and low levels can only occur during the low level of SCL.
insert image description here

START: Start signal, when SCL is high level, SDA transitions from high level to low level.
ADDRESS+R/W+ACK: address frame, including the address of the slave device that will communicate, 1~7 bits are the address of the slave device, and the 8th bit is the read and write bit (high level-read, low level-write) , ACK is the response of the slave (at this time, SDA is controlled by the slave, and the slave outputs a low level to indicate the response).
DATA+ACK: The data to be written to the slave, or the data to be read from the slave.
STOP: stop signal, when SCL is high level, SDA transitions from low level to high level.

3.1 General flow of write operation

  • The master device initiates a start signal
  • Start to transmit the slave device address, the upper seven bits indicate the slave device address, and the last bit is 0, which means write.
  • After the slave address transmission is completed, set SDA to the input state and read the SDA level state. If it is 0, it means that it is pulled low by the slave device, which is an ACK signal, indicating that the slave device has been found. If it is not 0, it means that the slave device has not responded, and the master device initiates a stop signal to terminate the transmission.
  • After finding the slave device, start to transmit the address value that needs to write data to the internal location of the slave device, similar to the previous step.
  • After transferring the internal address of the slave device, the data that needs to be written to the internal address can be transferred. Determine the size of the data to be written, and transfer in bytes.
  • After the transmission is completed, the host sends a stop signal to terminate the write operation.
    insert image description here

3.2 General flow of read operation

For the read operation, it is necessary to first transmit the internal address of the slave device to be read to the slave device, and then execute the read operation.

  • Write to the internal address of the slave device to be read. See 3.1 for steps.
  • The master device initiates a start signal
  • Start to transmit the slave device address, the upper seven bits indicate the slave device address, and the last bit is 1, which means read.
  • After the slave address transmission is completed, set SDA to the input state and read the SDA level state. If it is 0, it means that it is pulled low by the slave device, which is an ACK signal, indicating that the slave device has been found. If it is not 0, it means that the slave device has not responded, and the master device initiates a stop signal to terminate the transmission.
  • After finding the slave device, it starts to read the data at the internal address of the slave device.
  • Determine the size of the data to be read, and transfer in bytes.
  • After the transmission is completed, the host sends a stop signal to terminate the write operation.
    insert image description here

Four, IO port analog I2C

4.1 IO port initialization

void sys_i2c_init_board(void)
{
    
    
    //初始化IO口,SCL配置为输出高
    GPIO_InitTypeDef PB6={
    
    GPIO_Pin_6,GPIO_Speed_2MHz,GPIO_Mode_Out_OD};
    GPIO_Init(GPIOB, &PB6);
    //初始化IO口,SDA配置为输出高
    GPIO_InitTypeDef PB7={
    
    GPIO_Pin_7,GPIO_Speed_2MHz,GPIO_Mode_Out_OD};
    GPIO_Init(GPIOB,&PB7);
}

4.2 Start signal

//START: High -> Low on SDA while SCL is High
void soft_i2c_send_start(void)
{
    
    
    SET_SDA_OUT;
    I2C_DELAY();
    
    I2C_SDA(1);
    I2C_SCL(1);
    I2C_DELAY();
    
    I2C_SDA(0);  // scl高电平期间,SDA拉低
    I2C_DELAY();
    
    I2C_SCL(0);
    I2C_DELAY();
}

4.3 Stop signal

//STOP: Low -> High on SDA while SCL is High
void soft_i2c_send_stop(void)
{
    
    
    I2C_SCL(0);
    SET_SDA_OUT;
    I2C_SDA(0);
    I2C_DELAY();
    
  
    I2C_SCL(1);
    I2C_DELAY();

    I2C_SDA(1);  // scl高电平期间,SDA拉高
    I2C_DELAY();
}

4.4 Read a byte (used internally by this I2C routine)

static u8 soft_i2c_read_byte(int ack)
{
    
    
  u8  data;
  int  j;
  
  SET_SDA_IN;
  
  data = 0;
  for(j = 0; j < 8; j++) 
  {
    
    
    I2C_DELAY();
    I2C_SCL(1);
    I2C_DELAY();
    data <<= 1;
    data |= I2C_SDA_VALUE_IN;
    I2C_DELAY();
    I2C_SCL(0);
    I2C_DELAY();
  }
  soft_i2c_send_ack(ack);
  
  return(data);
}

4.5 Write a byte (used internally by this I2C routine)

static int soft_i2c_write_byte(u8 data)
{
    
    
  int j;
  int nack;
  
  SET_SDA_OUT;
  for(j = 0; j < 8; j++) 
  {
    
    
    I2C_SDA(data & 0x80);
    I2C_DELAY();
    I2C_SCL(1);
    I2C_DELAY();
    I2C_DELAY();
    I2C_SCL(0);
    I2C_DELAY();
    
    data <<= 1;
  }

  /*
   * Look for an <ACK>(negative logic) and return it.
   */
  
  SET_SDA_IN;
  I2C_DELAY();
  I2C_SCL(1);
  I2C_DELAY();
  nack = I2C_SDA_VALUE_IN;
  I2C_DELAY();
  I2C_SCL(0);
  I2C_DELAY();
  
  SET_SDA_OUT;
  //I2C_SDA(1);
  return(nack);	/* not a nack is an ack */
}

4.6 I2C read data (for external calls)

int soft_i2c_read(u8 chipaddr, u32 internalAddr, u32 internalAddrLen, u8 *buffer, int len)
{
    
    
  int shift;

  /*
   * Do the addressing portion of a write cycle to set the
   * chip's address pointer.  If the address length is zero,
   * don't do the normal write cycle to set the address pointer,
   * there is no address pointer in this chip.
   */
  soft_i2c_send_start();
  if(internalAddrLen > 0) 
  {
    
    
    if(soft_i2c_write_byte(chipaddr << 1)) 	/* write cycle */
    {
    
    
      soft_i2c_send_stop();
      return(1);
    }
    shift = (internalAddrLen-1) * 8;
    while(internalAddrLen-- > 0) 
    {
    
    
      if(soft_i2c_write_byte(internalAddr >> shift)) 
      {
    
    
        //printf("i2c_read, internal address not <ACK>ed\n");
        return(1);
      }
      shift -= 8;
    }

  /* Some I2C chips need a stop/start sequence here,
   * other chips don't work with a full stop and need
   * only a start.  Default behaviour is to send the
   * stop/start sequence.
   */
#ifdef CONFIG_SOFT_I2C_READ_REPEATED_START
    soft_i2c_send_start();
#else
    soft_i2c_send_stop();
    soft_i2c_send_start();
#endif
  }
  /*
   * Send the chip address again, this time for a read cycle.
   * Then read the data.  On the last byte, we do a NACK instead
   * of an ACK(len == 0) to terminate the read.
   */
  soft_i2c_write_byte((chipaddr << 1) | 1);	/* read cycle */
  while(len-- > 0) 
  {
    
    
      *buffer++ = soft_i2c_read_byte(len == 0);
  }
  soft_i2c_send_stop();
  return(0);
}

4.7 I2C write data (for external calls)

int soft_i2c_write(u8 chipaddr, u32 internalAddr, u32 internalAddrLen, u8 *buffer, int len)
{
    
    
    int shift, failures = 0;

    //printf("i2c_write: chip %02X addr %02X alen %d buffer %p len %d\n",chip, addr, alen, buffer, len);

    soft_i2c_send_start();
    if(soft_i2c_write_byte(chipaddr<<1)) 	/* write cycle */
    {
    
    	
        soft_i2c_send_stop();
        //printf("i2c_write, no chip responded %02X\n", chipaddr);
        return(1);
    }
    shift = (internalAddrLen-1) * 8;
    while(internalAddrLen-- > 0) 
    {
    
    
        if(soft_i2c_write_byte(internalAddr >> shift)) 
        {
    
    
            //printf("i2c_write, address not <ACK>ed\n");
            return(1);
        }
        shift -= 8;
    }

    while(len-- > 0) 
    {
    
    
        if(soft_i2c_write_byte(*buffer++)) 
        {
    
    
            failures++;
        }
    }
    soft_i2c_send_stop();
    return(failures);
}

4.8 Complete routine

Link: https://pan.baidu.com/s/1DdKy33_3XhbQKfYtprmP_w
Extraction code: 1nnl

Guess you like

Origin blog.csdn.net/qq_40709487/article/details/127348147