
I2C Communication
- Solomon Ankomah
- Blog
- 11 Mar, 2025
Mastering I2C Communication in Embedded Systems
I2C (Inter-Integrated Circuit) is one of the most popular communication protocols in embedded systems. It allows multiple slave devices to communicate with one or more master devices using just two wires. In this blog post, we’ll break down how I2C works, explore real-world use cases, and demonstrate implementations on Arduino, Raspberry Pi, and STM32.
Table of Contents
- What is I2C?
- I2C Bus Structure and Features
- Typical Use Cases
- I2C Protocol Basics
- I2C Communication Diagram
- Arduino I2C Example
- Raspberry Pi I2C Example (Python)
- STM32 I2C Example (HAL Library)
- Tips for Reliable I2C Communication
- Conclusion
1. What is I2C?
I2C (pronounced “eye-squared-see”) is a synchronous, multi-master, multi-slave, packet-switched protocol that uses two lines:
- SDA (Serial Data Line)
- SCL (Serial Clock Line)
It was developed by Philips (now NXP) and is ideal for connecting low-speed peripherals to processors and microcontrollers.
2. I2C Bus Structure and Features
- 2-wire interface: SDA (data) and SCL (clock)
- Supports multiple masters and slaves
- Each device has a unique 7-bit or 10-bit address
- Bidirectional data line with open-drain drivers
- Speeds: Standard (100 kHz), Fast (400 kHz), Fast-mode Plus (1 MHz), High-Speed (3.4 MHz)
I2C Bus Diagram:
+-----------+ +-----------+ +-----------+
| Master | | Slave 1 | | Slave 2 |
| (e.g. MCU)|----------| (e.g. RTC)|---------| (e.g. OLED)|
| SDA/SCL | | SDA/SCL | | SDA/SCL |
+-----------+ +-----------+ +-----------+
Shared SDA/SCL Lines
3. Typical Use Cases
- Real-Time Clocks (RTC)
- EEPROM and FRAM storage
- Environmental sensors (temperature, humidity, pressure)
- OLED or LCD displays
- Digital potentiometers
4. I2C Protocol Basics
Communication Flow:
- Start Condition (S)
- Slave Address + R/W bit
- ACK/NACK
- Data Bytes with ACK
- Stop Condition (P)
Signals:
- Logic High = Idle state
- Master controls clock and initiates communication
5. I2C Communication Diagram
START | SLA+W | ACK | DATA1 | ACK | DATA2 | NACK | STOP
---------+-------+-------+--------+-------+--------+-------+------>
0x50 0x3F 0x7C
6. Arduino I2C Example
Connect an I2C LCD or sensor (e.g., BME280) to Arduino
#include <Wire.h>
void setup() {
Wire.begin(); // Join I2C bus as master
Serial.begin(9600);
}
void loop() {
Wire.beginTransmission(0x76); // BME280 I2C address
Wire.write(0xD0); // Register to read
Wire.endTransmission();
Wire.requestFrom(0x76, 1);
if (Wire.available()) {
byte chipID = Wire.read();
Serial.println(chipID, HEX);
}
delay(1000);
}
7. Raspberry Pi I2C Example (Python)
Enable I2C:
sudo raspi-config # Enable I2C
sudo apt-get install -y i2c-tools python3-smbus
Python Code to Read from BME280:
import smbus
import time
bus = smbus.SMBus(1)
address = 0x76
chip_id = bus.read_byte_data(address, 0xD0)
print(f"Chip ID: {hex(chip_id)}")
8. STM32 I2C Example (HAL Library)
CubeMX Configuration:
- Enable I2C1 in Master Mode
- Use PB8 (SCL), PB9 (SDA)
Main Code (STM32 HAL C):
uint8_t chip_id;
HAL_I2C_Mem_Read(&hi2c1, 0x76 << 1, 0xD0, I2C_MEMADD_SIZE_8BIT, &chip_id, 1, 100);
printf("Chip ID: 0x%X\r\n", chip_id);
9. Tips for Reliable I2C Communication
- Pull-Up Resistors: Essential for SDA and SCL (typically 4.7kΩ or 10kΩ)
- Check Device Address: Use
i2cdetect
(Raspberry Pi) or datasheets - Avoid Bus Conflicts: Only one master should communicate at a time
- Keep Lines Short: Long lines increase capacitance, reducing signal integrity
- Error Handling: Implement retries on NACKs or bus errors
10. Conclusion
I2C is a powerful, flexible protocol for short-distance communication between embedded components. Whether you’re working with Arduino, Raspberry Pi, or STM32, understanding how to configure and troubleshoot I2C will greatly enhance your ability to build modular and scalable embedded projects.
In upcoming tutorials, we’ll connect multiple devices on the same I2C bus and explore advanced topics like clock stretching and multi-master configurations.
Stay tuned, and happy tinkering!