STM32 I2C Bus Communication

STM32 I2C Bus Communication

The I2C bus is the “universal” interface in the microcontroller world, as almost all common sensors, display modules, and memory chips support I2C communication. This article will guide you through the working principles of I2C and how to implement I2C communication on STM32.

What is I2C?

I2C (Inter-Integrated Circuit) is a simple, bidirectional, two-wire synchronous serial bus. It only requires two wires – SCL (Serial Clock Line) and SDA (Serial Data Line) to transmit data between connected devices.

Imagine I2C as a two-lane highway:

  • • SCL is the traffic light, controlling the pace of data transmission
    • • SDA is the lane, where data travels like vehicles

Note: All devices on the I2C bus are connected in parallel, like toll booths on a highway. Each device has its own address, and the master device (usually the microcontroller) selects the slave device to communicate with by broadcasting the address.

Hardware Connection of I2C

I2C Hardware Connection Diagram

As shown in the figure above, the hardware connection of I2C is very simple:

  1. 1. Connect the SCL pins of all devices together
  2. 2. Connect the SDA pins of all devices together
  3. 3. Both SCL and SDA require pull-up resistors (usually 4.7kΩ) connected to VCC

Why Pull-Up Resistors are Needed? Because I2C uses an open-drain output method, devices can only pull the bus low actively, and it is pulled high by the pull-up resistors when released. This is like speed bumps on a highway, preventing data transmission from being too fast and causing “accidents.”

I2C Communication Protocol

I2C communication is always initiated by the master device, following these steps:

  1. 1. Start Condition : The master pulls SDA low while keeping SCL high
  2. 2. Address Frame : Sends a 7-bit slave address and a 1-bit read/write flag
  3. 3. Acknowledge Bit : The slave acknowledges
  4. 4. Data Frame : Sends or receives 8 bits of data
  5. 5. Acknowledge Bit : The receiver acknowledges
  6. 6. Repeat Steps 4-5 until transmission is complete
  7. 7. Stop Condition : The master pulls SDA high while keeping SCL high

This process is like a complete journey on a highway:

  • • Start condition is getting on the highway
    • • Address frame is choosing the correct exit
    • • Data frame is the journey
    • • Stop condition is getting off the highway

STM32 I2C Configuration

The I2C peripheral of STM32 is very powerful, and we can use it with simple configuration. Here are the steps for configuration:

  1. 1. Enable I2C clock and GPIO clock
  2. 2. Configure I2C pins as open-drain output
  3. 3. Configure I2C parameters (such as speed, address, etc.)
  4. 4. Enable I2C
STM32 I2C Bus Communication

void I2C_Config(void){// Enable clockRCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    // Configure GPIO
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    // Configure I2C
    I2C_InitTypeDef I2C_InitStructure;
    I2C_InitStructure.I2C_ClockSpeed = 100000;  // 100kHz
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_Init(I2C1, &I2C_InitStructure);
    
    // Enable I2C
    I2C_Cmd(I2C1, ENABLE);
}

Note: Before using I2C, you must first configure GPIO and clock! This is like ensuring your vehicle is in good condition and the fuel tank is full before getting on the highway.

I2C Data Transmission

Here is a simple function to write data via I2C:

void I2C_Write(uint8_t SlaveAddress, uint8_t RegAddress, uint8_t Data)
{
    // Wait for I2C to be idle
    while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
    
    // Send start signal
    I2C_GenerateSTART(I2C1, ENABLE);
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
    
    // Send slave address
    I2C_Send7bitAddress(I2C1, SlaveAddress, I2C_Direction_Transmitter);
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
    
    // Send register address
    I2C_SendData(I2C1, RegAddress);
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
    
    // Send data
    I2C_SendData(I2C1, Data);
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
    
    // Send stop signal
    I2C_GenerateSTOP(I2C1, ENABLE);
}

This process is like delivering a package:

  1. 1. Wait for good road conditions (I2C idle)
  2. 2. Depart (send start signal)
  3. 3. Find the destination (send slave address)
  4. 4. Confirm the recipient (send register address)
  5. 5. Deliver the package (send data)
  6. 6. Return (send stop signal)

Practical Application Example

Let’s take reading data from the MPU6050 gyroscope as an example:

#define MPU6050_ADDR 0xD0
#define ACCEL_XOUT_H 0x3B

void ReadAccelData(int16_t* AccelX, int16_t* AccelY, int16_t* AccelZ)
{
    uint8_t buffer[6];
    
    // Read acceleration data
    I2C_Write(MPU6050_ADDR, ACCEL_XOUT_H, 0);  // Set read start address
    
    // Read 6 bytes of data
    for(int i = 0; i < 6; i++)
    {
        while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
        buffer[i] = I2C_ReceiveData(I2C1);
    }
    
    // Combine high and low bytes
    *AccelX = (buffer[0] << 8) | buffer[1];
    *AccelY = (buffer[2] << 8) | buffer[3];
    *AccelZ = (buffer[4] << 8) | buffer[5];
}

Note: When reading multiple bytes of data, pay attention to the byte order! The MPU6050 data has high byte first and low byte last.

Common Problems and Solutions

  1. 1. I2C Bus Stuck
  • • Reason: The slave device may not have released the SDA line
  1. 2. Communication Failure
  • • Solution: Reinitialize I2C or manually send 9 clock pulses to clear the bus
  • • Check hardware connections, especially pull-up resistors
    1. 3. Data Error
    • • Confirm if the slave device address is correct
    • • Check if the clock frequency matches
  • • Check for interference sources and consider adding filter capacitors
    • • Confirm if the data format (such as endianness) is correct

    Conclusion

    Although I2C seems simple, mastering it requires a lot of practice. It is recommended to start with simple devices (like EEPROM) and gradually transition to more complex sensors. When using new devices, carefully read the datasheet, paying special attention to timing requirements and special instructions.

    Mastering I2C is like learning to drive on the highway, enabling you to navigate smoothly in the world of microcontrollers. Start your I2C journey, and soon you will become an experienced driver in I2C communication!

    Leave a Comment